Advertisement
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;
- // Arrow keys
- var P2_UP = 38;
- var P2_DOWN = 40;
- var P2_LEFT = 37;
- var P2_RIGHT = 39;
- var P2_FIRE = 189;
- // 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;
- var EPSILON = 0.001;
- var MAX_DIST_FOR_COLLISIONS = 2; // (this is multiplied by CELL_SIZE)
- 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) {
- 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) {
- 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 casses 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";
- }
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement