diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 5e4200d0..d9ded855 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -10,6 +10,11 @@
+
+
+
+
+
@@ -119,7 +124,7 @@
-
+
1677886378553
diff --git a/src/game/game.ts b/src/game/game.ts
index b6e96f9d..577aba38 100644
--- a/src/game/game.ts
+++ b/src/game/game.ts
@@ -1,4 +1,5 @@
import crypto from "crypto";
+import { removeFrom } from "../utils";
import { Bodies, Body, Collision, Composite, Engine, Vector } from "matter-js";
import {
@@ -22,10 +23,29 @@ import { MapPacket } from "../packets/mapPacket";
import { KillPacket } from "../packets/killPacket";
export class Game {
+
+ /***
+ * The game ID. 16 hex characters, same as MD5
+ */
id: string;
+
map: Map;
+ /***
+ * All players, including dead and disconnected players.
+ */
players: Player[] = [];
+
+ /***
+ * All connected players. May be dead.
+ */
+ connectedPlayers: Player[] = [];
+
+ /***
+ * All connected and living players.
+ */
+ activePlayers: Player[] = [];
+
dirtyPlayers: Player[] = [];
aliveCount: number = 0;
aliveCountDirty: boolean = false;
@@ -44,8 +64,8 @@ export class Game {
initialGasDuration: number;
oldGasPosition: Vector;
newGasPosition: Vector;
- oldGasRad: number;
- newGasRad: number;
+ oldGasRadius: number;
+ newGasRadius: number;
constructor() {
this.id = crypto.createHash('md5').update(crypto.randomBytes(512)).digest('hex');
@@ -54,153 +74,154 @@ export class Game {
this.initialGasDuration = 0;
this.oldGasPosition = Vector.create(360, 360);
this.newGasPosition = Vector.create(360, 360);
- this.oldGasRad = 2048;
- this.newGasRad = 2048;
+ this.oldGasRadius = 2048;
+ this.newGasRadius = 2048;
- this.timer = setInterval(() => this.tick(), GameOptions.tickDelta);
this.engine = Engine.create();
- this.engine.gravity.scale = 0;
+ this.engine.gravity.scale = 0; // Disable gravity
this.map = new Map(this, "main");
- }
- private tick(): void {
-
- // Update physics engine
- Engine.update(this.engine, GameOptions.tickDelta);
-
- // First loop: Calculate movement & animations.
- for(const p of this.players) {
- if(p.dead || p.quit) continue;
-
- // TODO: Only check objects when player moves 1 unit. No reason to check every 0.2 units.
-
- // Movement
- p.notMoving = false;
- const s = GameOptions.movementSpeed, ds = GameOptions.diagonalSpeed;
- if(p.movingUp && p.movingLeft) p.setVelocity(-ds, ds);
- else if(p.movingUp && p.movingRight) p.setVelocity(ds, ds);
- else if(p.movingDown && p.movingLeft) p.setVelocity(-ds, -ds);
- else if(p.movingDown && p.movingRight) p.setVelocity(ds, -ds);
- else if(p.movingUp) p.setVelocity(0, s);
- else if(p.movingDown) p.setVelocity(0, -s);
- else if(p.movingLeft) p.setVelocity(-s, 0);
- else if(p.movingRight) p.setVelocity(s, 0);
- else {
- p.setVelocity(0, 0);
- if(!p.notMoving) p.setVelocity(0, 0);
- p.notMoving = true;
- }
+ this.timer = setInterval(() => {
+
+ // Update physics engine
+ Engine.update(this.engine, GameOptions.tickDelta);
+
+ // First loop: Calculate movement & animations.
+ for(const p of this.activePlayers) {
+
+ // TODO: Only check objects when player moves 1 unit. No reason to check every 0.2 units.
+
+ // Movement
+ p.notMoving = false;
+ const s = GameOptions.movementSpeed, ds = GameOptions.diagonalSpeed;
+ if(p.movingUp && p.movingLeft) p.setVelocity(-ds, ds);
+ else if(p.movingUp && p.movingRight) p.setVelocity(ds, ds);
+ else if(p.movingDown && p.movingLeft) p.setVelocity(-ds, -ds);
+ else if(p.movingDown && p.movingRight) p.setVelocity(ds, -ds);
+ else if(p.movingUp) p.setVelocity(0, s);
+ else if(p.movingDown) p.setVelocity(0, -s);
+ else if(p.movingLeft) p.setVelocity(-s, 0);
+ else if(p.movingRight) p.setVelocity(s, 0);
+ else {
+ p.setVelocity(0, 0);
+ if(!p.notMoving) p.setVelocity(0, 0);
+ p.notMoving = true;
+ }
- // p.updateVisibleObjects();
+ // p.updateVisibleObjects();
- if(p.shootStart) {
- p.shootStart = false;
- if(Date.now() - p.meleeCooldown >= 250) {
- p.meleeCooldown = Date.now();
+ if(p.shootStart) {
+ p.shootStart = false;
+ if(Date.now() - p.meleeCooldown >= 250) {
+ p.meleeCooldown = Date.now();
- // Start punching animation
- if(!p.animActive) {
- p.animActive = true;
- p.animType = 1;
- p.animTime = 0;
- }
+ // Start punching animation
+ if(!p.animActive) {
+ p.animActive = true;
+ p.animType = 1;
+ p.animTime = 0;
+ }
- // If the player is punching anything, damage the closest object
- let maxDepth: number = -1, closestObject = null;
- const weap = Weapons["fists"], // TODO Get player's melee, substitute here
- angle = Utils.unitVecToRadians(p.direction),
- offset = Vector.add(weap.attack.offset, Vector.mult(Vector.create(1, 0), p.scale - 1)),
- position = Vector.add(p.position, Vector.rotate(offset, angle));
- const body: Body = Bodies.circle(position.x, position.y, 0.9);
- for(const object of this.map.objects) { // TODO This is very inefficient. Only check visible objects
- if(!object.body || object.dead || object.id == p.id) continue;
- if(((object instanceof Obstacle && object.destructible) || object instanceof Player)) {
- const collision: Collision = Collision.collides(body, object.body);
- if(collision && collision.depth > maxDepth) {
- maxDepth = collision.depth;
- closestObject = object;
+ // If the player is punching anything, damage the closest object
+ let maxDepth: number = -1, closestObject = null;
+ const weapon = Weapons[p.loadout.meleeType],
+ angle = Utils.unitVecToRadians(p.direction),
+ offset = Vector.add(weapon.attack.offset, Vector.mult(Vector.create(1, 0), p.scale - 1)),
+ position = Vector.add(p.position, Vector.rotate(offset, angle));
+ const body: Body = Bodies.circle(position.x, position.y, 0.9);
+ for(const object of this.map.objects) { // TODO This is very inefficient. Only check visible objects
+ if(!object.body || object.dead || object.id == p.id) continue;
+ if(((object instanceof Obstacle && object.destructible) || object instanceof Player)) {
+ const collision: Collision = Collision.collides(body, object.body);
+ if(collision && collision.depth > maxDepth) {
+ maxDepth = collision.depth;
+ closestObject = object;
+ }
}
}
- }
- if(closestObject) {
- closestObject.damage(24, p);
- if(closestObject.isDoor) closestObject.interact(p);
- }
+ if(closestObject) {
+ closestObject.damage(24, p);
+ if(closestObject.isDoor) closestObject.interact(p);
+ }
- /* This code is more efficient, but doesn't work:
- for(const id of p.visibleObjects) {
- const object = this.map.objects[id];
- if(!object.body || object.dead || object.id == p.id) continue;
- if(((object instanceof Obstacle && object.destructible) || object instanceof Player)) {
- const collision: Collision = Collision.collides(body, object.body);
- if(collision && collision.depth > maxDepth) {
- maxDepth = collision.depth;
- closestObject = object;
+ /* This code is more efficient, but doesn't work:
+ for(const id of p.visibleObjects) {
+ const object = this.map.objects[id];
+ if(!object.body || object.dead || object.id == p.id) continue;
+ if(((object instanceof Obstacle && object.destructible) || object instanceof Player)) {
+ const collision: Collision = Collision.collides(body, object.body);
+ if(collision && collision.depth > maxDepth) {
+ maxDepth = collision.depth;
+ closestObject = object;
+ }
}
}
+ */
}
- */
}
- }
- if(p.animActive) {
- this.fullDirtyObjects.push(p.id);
- p.fullObjects.push(p.id);
- p.animTime++;
- p.animSeq = 1;
- if(p.animTime > 8) {
- p.animActive = false;
- p.animType = p.animSeq = p.animTime = 0;
+ if(p.animActive) {
+ this.fullDirtyObjects.push(p.id);
+ p.fullObjects.push(p.id);
+ p.animTime++;
+ p.animSeq = 1;
+ if(p.animTime > 8) {
+ p.animActive = false;
+ p.animType = p.animSeq = p.animTime = 0;
+ }
+ } else {
+ this.partialDirtyObjects.push(p.id);
+ p.partialObjects.push(p.id); // TODO Check for movement first
}
- } else {
- this.partialDirtyObjects.push(p.id);
- p.partialObjects.push(p.id); // TODO Check for movement first
}
- }
- // Second loop: calculate visible objects & send updates
- for(const p of this.players) {
- p.skipObjectCalculations = !this.fullDirtyObjects.length && !this.partialDirtyObjects.length && p.notMoving;
+ // Second loop: calculate visible objects & send packets
+ for(const p of this.connectedPlayers) {
+ p.skipObjectCalculations = !this.fullDirtyObjects.length && !this.partialDirtyObjects.length && p.notMoving;
- if(this.emotes.length > 0) {
- p.emotesDirty = true;
- p.emotes = this.emotes;
- }
+ if(this.emotes.length > 0) {
+ p.emotesDirty = true;
+ p.emotes = this.emotes;
+ }
- if(this.explosions.length > 0) {
- p.explosionsDirty = true;
- p.explosions = this.explosions;
- }
+ if(this.explosions.length > 0) {
+ p.explosionsDirty = true;
+ p.explosions = this.explosions;
+ }
- if(this.fullDirtyObjects.length > 0) {
- for(const id of this.fullDirtyObjects) {
- if(p.visibleObjects.includes(id)) p.fullObjects.push(id);
+ if(this.fullDirtyObjects.length > 0) {
+ for(const id of this.fullDirtyObjects) {
+ if(p.visibleObjects.includes(id)) p.fullObjects.push(id);
+ }
}
- }
- if(this.partialDirtyObjects.length > 0) {
- for(const id of this.partialDirtyObjects) {
- if(p.visibleObjects.includes(id)) p.partialObjects.push(id);
+ if(this.partialDirtyObjects.length > 0) {
+ for(const id of this.partialDirtyObjects) {
+ if(p.visibleObjects.includes(id)) p.partialObjects.push(id);
+ }
}
- }
- p.sendPacket(new UpdatePacket(p));
- if(this.aliveCountDirty) p.sendPacket(new AliveCountsPacket(p));
- if(this.kills.length) {
- for(const kill of this.kills) p.sendPacket(kill);
+ p.sendPacket(new UpdatePacket(p));
+ if(this.aliveCountDirty) p.sendPacket(new AliveCountsPacket(p));
+ if(this.kills.length) {
+ for(const kill of this.kills) p.sendPacket(kill);
+ }
}
- }
- this.emotes = [];
- this.explosions = [];
- this.kills = [];
- this.fullDirtyObjects = [];
- this.partialDirtyObjects = [];
- this.dirtyPlayers = [];
- this.deletedPlayerIds = [];
- this.aliveCountDirty = false;
+
+ // Reset everything
+ this.emotes = [];
+ this.explosions = [];
+ this.kills = [];
+ this.fullDirtyObjects = [];
+ this.partialDirtyObjects = [];
+ this.dirtyPlayers = [];
+ this.deletedPlayerIds = [];
+ this.aliveCountDirty = false;
+ }, GameOptions.tickDelta);
}
+
onMessage(stream, p) {
try {
const msgType = stream.readUint8();
@@ -226,7 +247,7 @@ export class Game {
p.direction = direction;
p.skipObjectCalculations = false;
}
- stream.readFloat(0, 64, 8); // Distance to mouse
+ stream.readFloat(0, 64, 8); // Distance to mouse
// Other inputs
@@ -277,6 +298,8 @@ export class Game {
p.id = this.map.objects.length;
this.map.objects.push(p);
this.players.push(p);
+ this.connectedPlayers.push(p);
+ this.activePlayers.push(p);
this.dirtyPlayers.push(p);
this.fullDirtyObjects.push(p.id);
this.aliveCount++;
@@ -299,8 +322,12 @@ export class Game {
p.quit = true;
this.deletedPlayerIds.push(p.id);
this.partialDirtyObjects.push(p.id);
- this.aliveCount--;
- this.aliveCountDirty = true;
+ removeFrom(this.activePlayers, p);
+ removeFrom(this.connectedPlayers, p);
+ if(!p.dead) {
+ this.aliveCount--;
+ this.aliveCountDirty = true;
+ }
}
addBody(body) {
diff --git a/src/game/objects/obstacle.ts b/src/game/objects/obstacle.ts
index 8fe6457f..dac59803 100644
--- a/src/game/objects/obstacle.ts
+++ b/src/game/objects/obstacle.ts
@@ -145,7 +145,6 @@ export class Obstacle {
} else {
}*/
- this.body.isSensor = this.doorOpen;
}
}
diff --git a/src/game/objects/player.ts b/src/game/objects/player.ts
index 1956b06b..3839b6b8 100644
--- a/src/game/objects/player.ts
+++ b/src/game/objects/player.ts
@@ -8,7 +8,7 @@ import {
Emote,
Explosion,
MsgType,
- ObjectKind,
+ ObjectKind, removeFrom,
SurvivBitStream as BitStream,
TypeToId
} from "../../utils";
@@ -29,7 +29,7 @@ export class Player {
//teamId: number = 0; // For 50v50?
groupId: number;
- direction: Vector;
+ direction: Vector = Vector.create(1, 0);
scale: number = 1;
zoom: number = 28; // 1x scope
layer: number = 0;
@@ -53,7 +53,7 @@ export class Player {
animSeq: number = 0;
animTime: number = 0;
- meleeCooldown: number;
+ meleeCooldown: number = 0;
private _health: number = 100;
boost: number = 0;
@@ -90,6 +90,7 @@ export class Player {
loadout: {
outfit: number,
melee: number,
+ meleeType: string,
heal: number,
boost: number,
emotes: number[],
@@ -107,6 +108,7 @@ export class Player {
this.loadout = {
outfit: TypeToId[loadout.outfit],
melee: TypeToId[loadout.melee],
+ meleeType: loadout.melee,
heal: TypeToId[loadout.heal],
boost: TypeToId[loadout.boost],
emotes: [],
@@ -117,6 +119,7 @@ export class Player {
this.loadout = {
outfit: 690,
melee: 557,
+ meleeType: "fists",
heal: 109,
boost: 138,
deathEffect: 0,
@@ -124,14 +127,11 @@ export class Player {
};
}
+ // Misc
this.groupId = this.game.players.length - 1;
-
- this.direction = Vector.create(1, 0);
-
this.username = username;
- this.meleeCooldown = Date.now();
-
+ // Init body
this.body = Bodies.circle(position.x, position.y, 1, { restitution: 0, friction: 0, frictionAir: 0, inertia: Infinity });
this.body.collisionFilter.category = CollisionCategory.Player;
this.body.collisionFilter.mask = CollisionCategory.Obstacle;
@@ -199,6 +199,7 @@ export class Player {
this.game.fullDirtyObjects.push(this.id);
this.game.fullDirtyObjects.push(this.deadBody.id);
this.game.deletedPlayerIds.push(this.id);
+ removeFrom(this.game.activePlayers, this);
}
}
diff --git a/src/packets/joinedPacket.ts b/src/packets/joinedPacket.ts
index 44256ef3..dfb40b20 100644
--- a/src/packets/joinedPacket.ts
+++ b/src/packets/joinedPacket.ts
@@ -17,12 +17,8 @@ export class JoinedPacket extends Packet {
stream.writeUint16(this.p.id); // Player ID
stream.writeBoolean(false); // Game started
stream.writeUint8(6); // Emote count
- stream.writeGameType(195); // First emote slot
- stream.writeGameType(193); // Second emote slot
- stream.writeGameType(196); // Third emote slot
- stream.writeGameType(194); // Fourth emote slot
- stream.writeGameType(0); // Fifth emote slot (win)
- stream.writeGameType(0); // Sixth emote slot (death)
+ for(let i = 0; i < 6; i++)
+ stream.writeGameType(this.p.loadout.emotes[i]);
}
}
diff --git a/src/packets/updatePacket.ts b/src/packets/updatePacket.ts
index fa182e15..c0120ba8 100644
--- a/src/packets/updatePacket.ts
+++ b/src/packets/updatePacket.ts
@@ -263,8 +263,8 @@ export class UpdatePacket extends Packet {
stream.writeBits(this.p.game.initialGasDuration, 8); // Duration
stream.writeVec(this.p.game.oldGasPosition, 0, 0, 1024, 1024, 16); // Old position
stream.writeVec(this.p.game.newGasPosition, 0, 0, 1024, 1024, 16); // New position
- stream.writeFloat(this.p.game.oldGasRad, 0, 2048, 16); // Old radius
- stream.writeFloat(this.p.game.newGasRad, 0, 2048, 16); // New radius
+ stream.writeFloat(this.p.game.oldGasRadius, 0, 2048, 16); // Old radius
+ stream.writeFloat(this.p.game.newGasRadius, 0, 2048, 16); // New radius
this.p.gasDirty = false;
}
diff --git a/src/utils.ts b/src/utils.ts
index aa780202..b7190540 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -85,6 +85,11 @@ export enum CollisionCategory {
Player = 1, Obstacle = 2, Loot = 4, Other = 8
}
+export function removeFrom(array: Array, object: any) {
+ const index: number = array.indexOf(object);
+ if(index != -1) array.splice(index, 1);
+}
+
export class Utils {
static log(message: string): void {