Not a member of Pastebin yet?
                        Sign Up,
                        it unlocks many cool features!                    
                - var TankGame = (function() {
 - var TARGET_WIDTH = 801; // Desired width
 - var TARGET_HEIGHT = 601;
 - var WIDTH; // Actual width of game, scaled to fit screen
 - var HEIGHT;
 - var GUI_HEIGHT = 150;
 - var DEBUG = false;
 - // WASD
 - var P1_UP = 87;
 - var P1_DOWN = 83;
 - var P1_LEFT = 65;
 - var P1_RIGHT = 68;
 - var P1_FIRE = 49; // Character 1
 - // Arrow keys
 - var P2_UP = 38;
 - var P2_DOWN = 40;
 - var P2_LEFT = 37;
 - var P2_RIGHT = 39;
 - var P2_FIRE = 189; // Character -
 - // Other settings
 - var TANK_SIZE = 15;
 - var TANK_SPEED = 1;
 - var TANK_TURN_SPEED = 5;
 - var WALL_WIDTH = 2;
 - var CELL_SIZE = 50;
 - var RESET_COUNTER_MAX = 200; // Time to start next round (frames)
 - var EPSILON = 0.001; // Used for comparing floats
 - // Optimization: skip collision checks between distant objects
 - // NOTE: This only works if there are no large objects in the scene
 - var MAX_DIST_FOR_COLLISIONS = 2; // (this is multiplied by CELL_SIZE)
 - // Global variables (Do not attempt to configure)
 - var TANK_P1, TANK_P2;
 - var CELLS_X, CELLS_Y;
 - var CANVAS, CTX, KEYSTATE, GAME_OBJECTS;
 - var PRERENDERED_CANVAS, PRERENDERED_CTX, PRERENDERED_REDRAW_NEEDED;
 - var GUI_REDRAW_NEEDED;
 - var END_ROUND = false;
 - var RESET_COUNTER;
 - var P1 = 1;
 - var P2 = 2;
 - var P1_SCORE = 0;
 - var P2_SCORE = 0;
 - /*
 - ===============================================================================
 - -------------------------------------CLASSES-----------------------------------
 - ===============================================================================
 - */
 - function deg2rad(degrees) {
 - return degrees * (Math.PI/180);
 - }
 - class Vector2d {
 - constructor(x, y) {
 - if (typeof x == 'undefined' || typeof y == 'undefined') {
 - throw "Invalid arguments";
 - }
 - this.x = x;
 - this.y = y;
 - }
 - rotate(radians) {
 - // Rotates coordinates counterclockwise
 - //radians=-radians
 - if (radians != 0) {
 - var x = this.x;
 - var y = this.y;
 - this.x = x * Math.cos(radians) - y * Math.sin(radians);
 - this.y = x * Math.sin(radians) + y * Math.cos(radians);
 - }
 - return this;
 - }
 - add(vector) {
 - if (vector instanceof Vector2d) {
 - this.x += vector.x;
 - this.y += vector.y;
 - }
 - else throw "Invalid argument";
 - return this;
 - }
 - subtract(vector) {
 - if (vector instanceof Vector2d) {
 - this.x -= vector.x;
 - this.y -= vector.y;
 - }
 - else throw "Invalid argument";
 - return this;
 - }
 - multiply(value) {
 - if (isNaN(value) === false) {
 - this.x *= value;
 - this.y *= value;
 - }
 - else throw "Invalid argument";
 - return this;
 - }
 - get_dot_product(other) {
 - // Calculates the dot product between given vector
 - if (other instanceof Vector2d) return this.x*other.x + this.y*other.y;
 - else throw "Invalid argument";
 - }
 - get_unit_vector() {
 - var c = this.get_magnitude();
 - var unit_vec = new Vector2d(this.x/c, this.y/c);
 - return unit_vec;
 - }
 - get_right_normal() {
 - return new Vector2d(this.y, -this.x);
 - }
 - get_magnitude() {
 - return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
 - }
 - get_magnitude_squared() {
 - return Math.pow(this.x, 2) + Math.pow(this.y, 2);
 - }
 - get_inverted() {
 - return new Vector2d(-this.x, -this.y);
 - }
 - clone() {
 - return new Vector2d(this.x, this.y);
 - }
 - reflect(vector) {
 - /*
 - Reflects this vector around given unit vector.
 - vec1 - (2*vec2*(vec2.vec1))
 - */
 - if (vector instanceof Vector2d) {
 - var vec2 = vector.clone(); // Avoid modifying given vector
 - vec2.multiply(vec2.get_dot_product(this));
 - vec2.multiply(2);
 - this.subtract(vec2);
 - return this;
 - }
 - else throw "Invalid argument";
 - }
 - };
 - class GameObject {
 - constructor(x, y, width, height, movable = false) {
 - this.destructible = true; // Can this object be destroyed?
 - this.max_hp = 1000;
 - this.hp = this.max_hp;
 - this.pos = new Vector2d(x, y);
 - this.width = width;
 - this.height = height;
 - this.rotation = 0; // In degrees
 - this.velocity = new Vector2d(0, 0);
 - this.movable = movable; // Can be moved by collisions
 - this.color = {};
 - this.color.r = 0;
 - this.color.g = 0;
 - this.color.b = 0;
 - this.verts = [];
 - this.rotated_verts = [];
 - this.ignored_collision_objs = [this];
 - this.circle = false;
 - this.radius = 0;
 - this.unstoppable = false; // Lets object move through destructible objects
 - var w = this.width / 2;
 - var h = this.height / 2;
 - this.verts.push(new Vector2d(-w, -h));
 - this.verts.push(new Vector2d(-w, h));
 - this.verts.push(new Vector2d(w, h));
 - this.verts.push(new Vector2d(w, -h));
 - GAME_OBJECTS.push(this);
 - if (this.movable == false) {
 - PRERENDERED_REDRAW_NEEDED = true;
 - }
 - }
 - damage(amount) {
 - if (this.destructible && this.hp > 0) {
 - if (amount > 0) {
 - this.hp -= amount;
 - this.color_by_damage();
 - }
 - else console.log("WARNING: Attempted to damage by negative amount!!!");
 - }
 - }
 - set_unstoppable(value) {
 - this.unstoppable = value;
 - }
 - set_destructible(value) {
 - this.destructible = value;
 - }
 - color_by_damage() {
 - var red = Math.round(255 - (255 * (this.hp/this.max_hp)));
 - this.color.r = red;
 - }
 - rotate(degrees) {
 - if (degrees != 0) {
 - this.rotation += degrees;
 - while (this.rotation < 0) this.rotation += 360;
 - while (this.rotation > 360) this.rotation -= 360;
 - this.calculate_rotated_verts();
 - }
 - }
 - move(vector) {
 - if (vector instanceof Vector2d) {
 - this.pos.add(vector);
 - }
 - else throw "Invalid argument";
 - }
 - get_rect(local_coordinates) {
 - var x = this.pos.x;
 - var y = this.pos.y;
 - var w = this.width / 2;
 - var h = this.height / 2;
 - if (local_coordinates) return [-w, -h, w*2, h*2];
 - else return [x-w, y-h, w*2, h*2];
 - }
 - calculate_rotated_verts() {
 - var radians = deg2rad(this.rotation)
 - this.rotated_verts = [];
 - for (var vert of this.verts) {
 - this.rotated_verts.push(vert.clone().rotate(radians));
 - }
 - this.rotated_verts;
 - }
 - get_verts() {
 - if (this.rotated_verts.length === 0) this.calculate_rotated_verts();
 - return this.rotated_verts;
 - }
 - on_collision(obj) {
 - /*
 - Default GameObjects do nothing on collision. Child classes can use this
 - for example to damage or give powerups to tanks on collision.
 - */
 - //console.log(typeof(obj));
 - }
 - destroy() {
 - var i = GAME_OBJECTS.indexOf(this);
 - delete GAME_OBJECTS[i];
 - if (this.movable == false) {
 - PRERENDERED_REDRAW_NEEDED = true;
 - }
 - }
 - update() {
 - /*
 - Moves GameObject by its velocity and checks for collisions.
 - If collisions are found, attempts to solve them by moving itself.
 - */
 - if (this.hp <= 0) {
 - this.destroy();
 - return;
 - }
 - if (this.movable) {
 - // Move by velocity, if it has any
 - this.move(this.velocity);
 - // Get all colliding objects
 - var collisions = GetCollisions(this);
 - var attempts = 0; // Track attempts to prevent infinite loops
 - var done = false;
 - var max_attempts = 5;
 - while(done === false && attempts < max_attempts) {
 - var prev_pos = this.pos.clone();
 - var prev_velo = this.velocity.clone();
 - done = true;
 - if (collisions.length > 0) {
 - //console.log(collisions);
 - }
 - for (var i = 0; i < collisions.length; i++) {
 - // Loop over all collisions one at a time
 - var collision = collisions[i];
 - var obj1 = collision.obj1; // This object (redundant)
 - var obj2 = collision.obj2; // The colliding object
 - var dir = collision.direction.clone();
 - if (this.unstoppable && obj2.destructible == true) {
 - // Don't attempt to solve collisions for unstoppable objects
 - // unstoppable objects can go through almost anything.
 - obj1.on_collision(obj2);
 - obj2.on_collision(obj1);
 - break;
 - }
 - this.move(dir.clone().multiply(collision.magnitude));
 - this.velocity.reflect(dir);
 - // Get all new collisions after moving
 - var new_collisions = GetCollisions(this);
 - if (new_collisions.length === 0) {
 - // Success! No new collisions found
 - obj1.on_collision(obj2);
 - obj2.on_collision(obj1);
 - break; // Don't check any other collisions
 - }
 - else if (i < collisions.length-1) {
 - // Fail! Move back to original position and attempt to solve the next collision
 - this.pos = prev_pos;
 - this.velocity = prev_velo;
 - }
 - else {
 - // Fail! No collisions remaining. Try to resolve collisions from the new position
 - obj1.on_collision(obj2);
 - obj2.on_collision(obj1);
 - collisions = new_collisions;
 - done = false;
 - attempts++;
 - }
 - }
 - }
 - if(attempts > 1) {
 - console.log("Attempted to resolve collisions " + attempts + " times");
 - }
 - }
 - }
 - draw(context) {
 - context.save();
 - let color = "rgb(" + this.color.r + "," + this.color.g + "," + this.color.b + ")";
 - context.fillStyle = color;
 - context.translate(this.pos.x, this.pos.y);
 - context.beginPath();
 - if (this.circle === true) {
 - context.arc(0, 0, this.radius, 0, 2 * Math.PI);
 - }
 - else {
 - var verts = this.get_verts();
 - context.moveTo(verts[0].x, verts[0].y);
 - for (var vert of verts) {
 - context.lineTo(vert.x, vert.y);
 - }
 - context.lineTo(vert.x, vert.y);
 - }
 - context.fill();
 - context.restore();
 - }
 - };
 - var GunTypes = {
 - // Enumeration for different types of guns
 - 'normal' : 1,
 - 'machinegun' : 2,
 - 'heavy' : 3,
 - }
 - class Gun {
 - /*
 - Handles the firing of tank guns.
 - */
 - constructor(tank) {
 - this.bullet_size = 5;
 - this.bullet_speed = 1.5;
 - this.ammo = 1000; // Max shots for current gun
 - this.clip = 5; // Max simultaenous shots
 - this.fire_delay = 0.5;
 - this.last_shot = Date.now();
 - this.tank = tank; // Used for ignoring collisions when firing
 - this.type = GunTypes.normal;
 - this.damage_amount = 4;
 - this.randomize_direction = false;
 - }
 - fire(x, y, direction) {
 - var bullet;
 - if (this.randomize_direction) {
 - direction += Math.random() * 10 - 5; // Add a random offset of +-5 degrees
 - }
 - if (this.clip > 0 && this.ammo > 0) {
 - if (Date.now() - this.last_shot > this.fire_delay * 1000) {
 - GUI_REDRAW_NEEDED = true; // GUI has info about remaining ammo
 - this.clip--;
 - this.ammo--;
 - this.last_shot = Date.now();
 - bullet = new Bullet(x, y, direction, this.damage_amount, this, this.bullet_size, this.bullet_speed);
 - }
 - }
 - return bullet;
 - }
 - reload() {
 - // Adds one bullet to clip.
 - this.clip++;
 - }
 - get_name() {
 - return "40mm gun";
 - }
 - get_ammo_str() {
 - switch (this.type) {
 - case GunTypes.normal:
 - return "infinite";
 - break;
 - default:
 - return this.ammo;
 - }
 - }
 - };
 - class Machinegun extends Gun {
 - constructor(tank) {
 - super(tank);
 - this.bullet_size = 2;
 - this.bullet_speed = 2;
 - this.ammo = 75;
 - this.clip = 25;
 - this.fire_delay = 0.1;
 - this.damage_amount = 1;
 - this.randomize_direction = true;
 - this.type = GunTypes.machinegun;
 - }
 - get_name() {
 - return ".50 caliber machine gun";
 - }
 - };
 - class Heavygun extends Gun {
 - constructor(tank) {
 - super(tank);
 - this.bullet_size = 20;
 - this.bullet_speed = 1.5;
 - this.ammo = 3;
 - this.clip = 3;
 - this.fire_delay = 1;
 - this.damage_amount = 1000;
 - this.randomize_direction = false;
 - this.type = GunTypes.heavy;
 - }
 - fire(x, y, direction) {
 - // Override firing to make the bullet unstoppable
 - var bullet = super.fire(x, y, direction);
 - if (bullet)
 - bullet.set_unstoppable(true);
 - }
 - get_name() {
 - return "155mm heavy gun";
 - }
 - };
 - class Tank extends GameObject {
 - constructor(x, y, player) {
 - super(x, y, TANK_SIZE, TANK_SIZE, true); // Movable=true
 - this.player = player;
 - this.speed = 1;
 - this.turn_speed = 5;
 - this.fire_delay = 0;
 - this.max_fire_delay = 30;
 - this.max_ammo = 5;
 - this.ammo = this.max_ammo;
 - this.max_hp = 10;
 - this.hp = this.max_hp;
 - this.color_by_damage();
 - // Add a gun
 - this.set_gun(GunTypes.normal);
 - var w = this.width / 2;
 - var h = this.height / 2;
 - var last_vert = this.verts.pop();
 - this.verts.push(new Vector2d(w, h/2));
 - this.verts.push(new Vector2d(w*2, h/2));
 - this.verts.push(new Vector2d(w*2, -h/2));
 - this.verts.push(new Vector2d(w, -h/2));
 - this.verts.push(last_vert);
 - }
 - set_gun(type) {
 - GUI_REDRAW_NEEDED = true; // GUI has info about player weapons
 - switch (type) {
 - case GunTypes.normal :
 - this.gun = new Gun(this);
 - break;
 - case GunTypes.machinegun :
 - this.gun = new Machinegun(this);
 - break;
 - case GunTypes.heavy :
 - this.gun = new Heavygun(this);
 - break;
 - default :
 - console.log("Invalid gun type given " + type);
 - this.gun = new Gun(this);
 - break;
 - }
 - }
 - destroy() {
 - END_ROUND = true;
 - this.player === P1 ? P2_SCORE++ : P1_SCORE++;
 - for (var i = 0; i < 360; i += 60) {
 - // Spawn a ring of bullets on death
 - var radians = deg2rad(i);
 - var damage = 4;
 - var off_x = this.width * Math.cos(radians);
 - var off_y = this.width * Math.sin(radians);
 - new Bullet(this.pos.x + off_x, this.pos.y + off_y, i, damage);
 - }
 - super.destroy();
 - }
 - update() {
 - /*
 - Checks for user input and checks collisions.
 - */
 - if (this.fire_delay > 0) this.fire_delay--;
 - var p = this.player;
 - var radians = deg2rad(this.rotation);
 - if ((p == P1 && KEYSTATE[P1_UP]) || (p == P2 && KEYSTATE[P2_UP])) {
 - this.velocity.x = this.speed * Math.cos(radians);
 - this.velocity.y = this.speed * Math.sin(radians);
 - }
 - else if ((p == P1 && KEYSTATE[P1_DOWN]) || (p == P2 && KEYSTATE[P2_DOWN])) {
 - this.velocity.x = -this.speed * Math.cos(radians);
 - this.velocity.y = -this.speed * Math.sin(radians);
 - }
 - else {
 - this.velocity = new Vector2d(0, 0);
 - }
 - if ((p == P1 && KEYSTATE[P1_LEFT]) || (p == P2 && KEYSTATE[P2_LEFT])) {
 - this.rotate(-this.turn_speed);
 - }
 - else if ((p == P1 && KEYSTATE[P1_RIGHT]) || (p == P2 && KEYSTATE[P2_RIGHT])) {
 - this.rotate(this.turn_speed);
 - }
 - super.update(); // Move and check collisions before firing
 - if ((p == P1 && KEYSTATE[P1_FIRE]) || (p == P2 && KEYSTATE[P2_FIRE])) {
 - if (this.gun.ammo > 0) {
 - var off_x = this.width * 0.9 * Math.cos(radians);
 - var off_y = this.width * 0.9 * Math.sin(radians);
 - this.gun.fire(this.pos.x + off_x, this.pos.y + off_y, this.rotation);
 - }
 - else {
 - this.set_gun(GunTypes.normal); // Replace all special guns with a regular gun
 - }
 - }
 - }
 - };
 - var PowerupType = {
 - 'machinegun' : 1,
 - 'heavy' : 2,
 - 'speed' : 3,
 - 'get_random_type' :
 - function get_random_type() {
 - return Math.ceil(Math.random() * 3);
 - }
 - };
 - class Powerup extends GameObject {
 - constructor(x, y, type) {
 - super(x, y, 10, 10, true);
 - this.type = type;
 - this.max_hp = 20;
 - this.hp = this.max_hp;
 - this.last_damage_tick = Date.now(); // Cause damage to self every second
 - this.turn_speed = 5;
 - this.re_color();
 - }
 - re_color() {
 - switch(this.type) {
 - case PowerupType.machinegun:
 - this.color = {"r":0, "g": 200, "b": 200};
 - break;
 - case PowerupType.heavy:
 - this.color = {"r":0, "g": 50, "b": 120};
 - break;
 - case PowerupType.speed:
 - this.color = {"r":0, "g": 255, "b": 255};
 - break;
 - default:
 - console.log("Powerup has invalid type!");
 - this.color = {"r":0, "g": 10, "b": 10};
 - }
 - }
 - update() {
 - this.rotate(this.turn_speed);
 - if (Date.now() - this.last_damage_tick > 1000) {
 - this.last_damage_tick = Date.now();
 - this.damage(1); // Max time to live is 20 seconds;
 - }
 - super.update();
 - }
 - on_collision(obj) {
 - if (obj instanceof Tank) {
 - switch (this.type) {
 - case PowerupType.machinegun:
 - obj.set_gun(GunTypes.machinegun);
 - break;
 - case PowerupType.heavy:
 - obj.set_gun(GunTypes.heavy);
 - break;
 - case PowerupType.speed:
 - obj.speed++;
 - break;
 - default:
 - console.log("Powerup has invalid type!");
 - }
 - this.destroy();
 - }
 - }
 - };
 - class Bullet extends GameObject {
 - constructor(x, y, direction, damage, gun, size, speed) {
 - if (typeof speed == 'undefined') speed = 1.5;
 - if (typeof size == 'undefined') size = 5;
 - super(x, y, size, size, true);
 - this.max_time_to_live = 15000; // After this time the bullet disappears (milliseconds)
 - this.remaining_bounces = 10;
 - this.first_bounce = true;
 - this.spawn_time = Date.now();
 - this.ignore_owner_for = 3 * size / speed; // HACK: this value is just randomly guessed (milliseconds)
 - this.speed = speed;
 - this.gun = gun;
 - if (this.gun && this.gun.tank) this.ignored_collision_objs.push(this.gun.tank); // Don't collide with tank before first bounce
 - this.color.r = Math.round(Math.random() * 255);
 - this.color.g = Math.round(Math.random() * 255);
 - this.color.b = Math.round(Math.random() * 255);
 - var radians = deg2rad(direction);
 - this.velocity.x = this.speed * Math.cos(radians);
 - this.velocity.y = this.speed * Math.sin(radians);
 - this.damage_amount = damage;
 - this.radius = this.width/2;
 - this.circle = true;
 - }
 - update() {
 - super.update();
 - if (Date.now() - this.spawn_time > this.max_time_to_live) {
 - // This bullet has been around long enough, destroy it
 - this.destroy();
 - }
 - }
 - on_collision(obj) {
 - this.remaining_bounces--;
 - if (this.first_bounce && (Date.now() - this.spawn_time > this.ignore_owner_for) && this.gun && this.gun.tank) {
 - // After first bounce bullet can collide with the shooting tank
 - this.first_bounce = false;
 - var ind = this.ignored_collision_objs.indexOf(this.gun.tank);
 - if (ind > -1) delete this.ignored_collision_objs[ind];
 - }
 - if (this.remaining_bounces < 1) {
 - this.destroy();
 - }
 - obj.damage(this.damage_amount);
 - if (obj instanceof Tank) {
 - this.destroy();
 - }
 - }
 - destroy() {
 - if (this.gun) this.gun.reload();
 - super.destroy();
 - }
 - };
 
Advertisement
 
                    Add Comment                
                
                        Please, Sign In to add comment