|
|
@@ -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() { |