| @@ -4,6 +4,9 @@ function randint(min, max) { | |||
| function random(min, max) { | |||
| return Math.random() * (max - min) + min; | |||
| } | |||
| function inRange(n, min, max) { | |||
| return n >= min && n <= max; | |||
| } | |||
| class Vec2 { | |||
| constructor(x, y) { | |||
| @@ -20,17 +23,32 @@ class Vec2 { | |||
| } | |||
| set(x, y) { | |||
| if (x instanceof Vec2) | |||
| return this.set(x.x, x.y); | |||
| this.x = x; | |||
| this.y = y; | |||
| return this; | |||
| } | |||
| add(x, y) { | |||
| if (x instanceof Vec2) | |||
| return this.add(x.x, x.y); | |||
| this.x += x; | |||
| this.y += y; | |||
| return this; | |||
| } | |||
| sub(x, y) { | |||
| if (x instanceof Vec2) | |||
| return this.sub(x.x, x.y); | |||
| this.x -= x; | |||
| this.y -= y; | |||
| return this; | |||
| } | |||
| scale(num) { | |||
| this.x *= num; | |||
| this.y *= num; | |||
| @@ -66,6 +84,17 @@ class Rectangle { | |||
| this.width = width; | |||
| this.height = height; | |||
| } | |||
| intersects(b) { | |||
| let a = this; | |||
| //console.log("if ("+a.x+", "+a.y+") intersects ("+b.x+", "+b.y+")"); | |||
| return ( | |||
| (inRange(a.x, b.x, b.x + b.width) || inRange(b.x, a.x, a.x + a.width)) && | |||
| (inRange(a.y, b.y, b.y + b.height) || inRange(b.y, a.y, a.y + a.height)) | |||
| ); | |||
| } | |||
| } | |||
| class Entity { | |||
| @@ -83,13 +112,24 @@ class Entity { | |||
| } | |||
| get boundingRect() { | |||
| return new Rectangle(this.pos.x, this.pos.y, this.width, this.height); | |||
| if (this.oundingRectCache) | |||
| return this.boundingRectCache; | |||
| this.boundingRectCache = new Rectangle(this.pos.x, this.pos.y, this.width, this.height); | |||
| return this.boundingRectCache; | |||
| } | |||
| intersectsPoint(e) { | |||
| let rect = this.boundingRect; | |||
| let r = new Rectangle(e.pos.x, e.pos.y, 1, 1); | |||
| return rect.intersects(r); | |||
| } | |||
| move(dt) { | |||
| this.vforce.scale(this.forceScalar * dt); | |||
| this.vel.add(this.vforce.x, this.vforce.y); | |||
| this.pos.add(this.vel.x * dt, this.vel.y * dt); | |||
| this.vel.add(this.vforce); | |||
| this.pos.add(this.vel.clone().scale(dt)); | |||
| this.vforce.set(0, 0); | |||
| } | |||
| @@ -111,12 +151,12 @@ class Entity { | |||
| send() {} | |||
| despawn() { | |||
| delete this.game.entities[this.id]; | |||
| delete this.game.players[this.id]; | |||
| this.game.players.forEach((p) => p.sock.send("despawn", { | |||
| id: this.id | |||
| })); | |||
| delete this.game.entities[this.id]; | |||
| delete this.game.players[this.id]; | |||
| } | |||
| } | |||
| @@ -126,10 +166,13 @@ class Bullet extends Entity { | |||
| this.ownerId = ownerId; | |||
| this.vel = vel; | |||
| setTimeout(() => this.despawn(), 4000); | |||
| setTimeout(() => this.despawn(), 1000); | |||
| } | |||
| send() { | |||
| send(first) { | |||
| if (!first) | |||
| return; | |||
| this.game.players.forEach((p) => p.sock.send("set", { | |||
| type: "bullet", | |||
| id: this.id, | |||
| @@ -150,6 +193,7 @@ class Player extends Entity { | |||
| this.rotForce = 0; | |||
| this.rotVel = 0; | |||
| this.canShoot = true; | |||
| this.health = 100; | |||
| sock.on("request", (req) => { | |||
| if (req.url == "get_id") { | |||
| @@ -159,7 +203,7 @@ class Player extends Entity { | |||
| } else if (req.url == "keydown") { | |||
| this.keys[req.data.key] = true; | |||
| } else if (req.url == "keyup") { | |||
| this.keys[req.data.key] = false; | |||
| delete this.keys[req.data.key]; | |||
| } | |||
| }); | |||
| @@ -179,34 +223,96 @@ class Player extends Entity { | |||
| this.rotForce += 0.005; | |||
| if (this.keys.shoot && this.canShoot) { | |||
| let vel = new Vec2(0, -1).rotate(this.rot); | |||
| let b = new Bullet(this.pos, vel, this.id, this.game.id, this.game); | |||
| let vel = new Vec2(0, -1).rotate(this.rot).add(this.vel); | |||
| let posmod = new Vec2(0, -this.height/2).rotate(this.rot); | |||
| let pos = this.pos.clone().add(posmod); | |||
| let b = new Bullet(pos, vel, this.id, this.game.id, this.game); | |||
| this.game.spawn(b); | |||
| this.canShoot = false; | |||
| setTimeout(() => this.canShoot = true, 100); | |||
| setTimeout(() => this.canShoot = true, 50); | |||
| } | |||
| f.rotate(this.rot); | |||
| this.force(f.x, f.y); | |||
| //Detect collissions | |||
| this.game.entities.forEach((e) => { | |||
| if (e instanceof Bullet) { | |||
| if (e.ownerId !== this.id && this.intersectsPoint(e)) { | |||
| this.health -= 3; | |||
| e.despawn(); | |||
| if (this.health <= 0) | |||
| this.despawn(); | |||
| } | |||
| } | |||
| }); | |||
| this.boundingRectCache = null; | |||
| } | |||
| get boundingRect() { | |||
| if (this.boundingRectCache) | |||
| return this.boundingRectCache; | |||
| //0 1 | |||
| // * | |||
| //3 2 | |||
| var rotated = [ | |||
| new Vec2(-this.width/2, -this.height/2), | |||
| new Vec2(this.width/2, -this.height/2), | |||
| new Vec2(this.width/2, this.height/2), | |||
| new Vec2(-this.width/2, this.height/2) | |||
| ].map((p) => p.rotate(this.rot)); | |||
| let tl = new Vec2(0, 0); | |||
| let br = new Vec2(0, 0); | |||
| rotated.forEach((p) => { | |||
| if (p.x < tl.x) | |||
| tl.x = p.x; | |||
| if (p.y < tl.y) | |||
| tl.y = p.y; | |||
| if (p.x > br.x) | |||
| br.x = p.x; | |||
| if (p.y > br.y) | |||
| br.y = p.y; | |||
| }); | |||
| this.boundingRectCache = new Rectangle( | |||
| this.pos.x + tl.x, | |||
| this.pos.y + tl.y, | |||
| br.x - tl.x, | |||
| br.y - tl.y | |||
| ); | |||
| return this.boundingRectCache; | |||
| } | |||
| move(dt) { | |||
| super.move(dt); | |||
| this.rotForce *= this.forceScalar * dt; | |||
| this.rotVel += this.rotForce; | |||
| this.rot += this.rotVel * dt; | |||
| this.rot = (this.rot + this.rotVel * dt) % (Math.PI * 2); | |||
| this.rotForce = 0; | |||
| } | |||
| send() { | |||
| this.game.players.forEach((p) => p.sock.send("set", { | |||
| type: "player", | |||
| send(first) { | |||
| let obj = { | |||
| id: this.id, | |||
| pos: this.pos, | |||
| vel: this.vel, | |||
| rot: this.rot, | |||
| rotVel: this.rotVel | |||
| })); | |||
| rotVel: this.rotVel, | |||
| keys: this.keys, | |||
| health: this.health | |||
| } | |||
| if (first) | |||
| obj.type = "player"; | |||
| this.game.players.forEach((p) => p.sock.send("set", obj)); | |||
| } | |||
| } | |||
| @@ -232,6 +338,7 @@ export default class Game { | |||
| spawn(ent) { | |||
| this.entities[this.id] = ent; | |||
| this.id += 1; | |||
| ent.send(true); | |||
| } | |||
| start() { | |||