Add player container
This commit is contained in:
parent
1cd8f5fbe6
commit
b55e927a34
16 changed files with 159 additions and 133 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
import { PhysicalContainer } from './physics/containers/physical-container';
|
import { PhysicalContainer } from './physics/containers/physical-container';
|
||||||
import { Player } from './players/player';
|
|
||||||
import ioserver from 'socket.io';
|
import ioserver from 'socket.io';
|
||||||
import {
|
import {
|
||||||
TransportEvents,
|
TransportEvents,
|
||||||
|
|
@ -11,18 +10,22 @@ import {
|
||||||
import { createWorld } from './map/create-world';
|
import { createWorld } from './map/create-world';
|
||||||
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
import { DeltaTimeCalculator } from './helper/delta-time-calculator';
|
||||||
import { Options } from './options';
|
import { Options } from './options';
|
||||||
|
import { PlayerContainer } from './players/player-container';
|
||||||
|
|
||||||
const playerCountSubscribedRoom = 'playerCountUpdates';
|
const playerCountSubscribedRoom = 'playerCountUpdates';
|
||||||
|
|
||||||
export class GameServer {
|
export class GameServer {
|
||||||
private objects = new PhysicalContainer();
|
private objects = new PhysicalContainer();
|
||||||
private players: Array<Player> = [];
|
private players: PlayerContainer;
|
||||||
private deltaTimes: Array<number> = [];
|
private deltaTimes: Array<number> = [];
|
||||||
private deltaTimeCalculator = new DeltaTimeCalculator();
|
private deltaTimeCalculator = new DeltaTimeCalculator();
|
||||||
|
|
||||||
private serverName: string;
|
private serverName: string;
|
||||||
private playerLimit: number;
|
private playerLimit: number;
|
||||||
|
|
||||||
constructor(private readonly io: ioserver.Server, options: Options) {
|
constructor(private readonly io: ioserver.Server, options: Options) {
|
||||||
|
this.players = new PlayerContainer(this.objects);
|
||||||
|
|
||||||
this.serverName = options.name;
|
this.serverName = options.name;
|
||||||
this.playerLimit = options.playerLimit;
|
this.playerLimit = options.playerLimit;
|
||||||
|
|
||||||
|
|
@ -31,8 +34,7 @@ export class GameServer {
|
||||||
|
|
||||||
io.on('connection', (socket: SocketIO.Socket) => {
|
io.on('connection', (socket: SocketIO.Socket) => {
|
||||||
socket.on(TransportEvents.PlayerJoining, (playerInfo: PlayerInformation) => {
|
socket.on(TransportEvents.PlayerJoining, (playerInfo: PlayerInformation) => {
|
||||||
const player = new Player(playerInfo, this.players, this.objects, socket);
|
const player = this.players.createPlayer(playerInfo, socket);
|
||||||
this.players.push(player);
|
|
||||||
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
||||||
const command = deserialize(json);
|
const command = deserialize(json);
|
||||||
player.sendCommand(command);
|
player.sendCommand(command);
|
||||||
|
|
@ -42,7 +44,7 @@ export class GameServer {
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
player.destroy();
|
player.destroy();
|
||||||
this.players = this.players.filter((p) => p !== player);
|
this.players.deletePlayer(player);
|
||||||
this.sendPlayerCountUpdate();
|
this.sendPlayerCountUpdate();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -56,7 +58,7 @@ export class GameServer {
|
||||||
public sendPlayerCountUpdate() {
|
public sendPlayerCountUpdate() {
|
||||||
this.io
|
this.io
|
||||||
.to(playerCountSubscribedRoom)
|
.to(playerCountSubscribedRoom)
|
||||||
.emit(TransportEvents.PlayerCountUpdate, this.players.length);
|
.emit(TransportEvents.PlayerCountUpdate, this.players.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public start() {
|
public start() {
|
||||||
|
|
@ -64,8 +66,7 @@ export class GameServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlePhysics() {
|
private handlePhysics() {
|
||||||
const delta = this.deltaTimeCalculator.getNextDeltaTimeInMilliseconds();
|
const delta = this.deltaTimeCalculator.getNextDeltaTimeInSeconds();
|
||||||
this.deltaTimes.push(delta);
|
|
||||||
const framesBetweenDeltaTimeCalculation = 1000;
|
const framesBetweenDeltaTimeCalculation = 1000;
|
||||||
|
|
||||||
if (this.deltaTimes.length > framesBetweenDeltaTimeCalculation) {
|
if (this.deltaTimes.length > framesBetweenDeltaTimeCalculation) {
|
||||||
|
|
@ -79,15 +80,15 @@ export class GameServer {
|
||||||
`Memory used: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`,
|
`Memory used: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)} MB`,
|
||||||
);
|
);
|
||||||
this.deltaTimes = [];
|
this.deltaTimes = [];
|
||||||
console.log(this.players.map((p) => p.latency));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.objects.stepObjects(delta / 1000);
|
this.objects.stepObjects(delta);
|
||||||
this.players.forEach((p) => p.step(delta / 1000));
|
this.players.step(delta);
|
||||||
|
|
||||||
const physicsDelta = this.deltaTimeCalculator.getDeltaTimeInMilliseconds();
|
const physicsDelta = this.deltaTimeCalculator.getDeltaTimeInSeconds() * 1000;
|
||||||
this.deltaTimes.push(physicsDelta);
|
this.deltaTimes.push(physicsDelta);
|
||||||
const sleepTime = settings.targetPhysicsDeltaTimeInMilliseconds - physicsDelta;
|
const sleepTime = settings.targetPhysicsDeltaTimeInMilliseconds - physicsDelta;
|
||||||
|
|
||||||
if (sleepTime >= settings.minPhysicsSleepTime) {
|
if (sleepTime >= settings.minPhysicsSleepTime) {
|
||||||
setTimeout(this.handlePhysics.bind(this), sleepTime);
|
setTimeout(this.handlePhysics.bind(this), sleepTime);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -98,7 +99,7 @@ export class GameServer {
|
||||||
public get serverInfo(): ServerInformation {
|
public get serverInfo(): ServerInformation {
|
||||||
return {
|
return {
|
||||||
serverName: this.serverName,
|
serverName: this.serverName,
|
||||||
playerCount: this.players.length,
|
playerCount: this.players.count,
|
||||||
playerLimit: this.playerLimit,
|
playerLimit: this.playerLimit,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
export class DeltaTimeCalculator {
|
export class DeltaTimeCalculator {
|
||||||
private previousTime: [number, number] = process.hrtime();
|
private previousTime: [number, number] = process.hrtime();
|
||||||
|
|
||||||
public getNextDeltaTimeInMilliseconds(): number {
|
public getNextDeltaTimeInSeconds(): number {
|
||||||
const deltaTime = process.hrtime(this.previousTime);
|
const deltaTime = process.hrtime(this.previousTime);
|
||||||
this.previousTime = process.hrtime();
|
this.previousTime = process.hrtime();
|
||||||
|
|
||||||
const [seconds, nanoSeconds] = deltaTime;
|
const [seconds, nanoSeconds] = deltaTime;
|
||||||
|
|
||||||
return seconds * 1000 + nanoSeconds / 1000 / 1000;
|
return seconds * 1000 + nanoSeconds / 1000 / 1000 / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDeltaTimeInMilliseconds(): number {
|
public getDeltaTimeInSeconds(): number {
|
||||||
const deltaTime = process.hrtime(this.previousTime);
|
const deltaTime = process.hrtime(this.previousTime);
|
||||||
|
|
||||||
const [seconds, nanoSeconds] = deltaTime;
|
const [seconds, nanoSeconds] = deltaTime;
|
||||||
|
|
||||||
return seconds * 1000 + nanoSeconds / 1000 / 1000;
|
return seconds * 1000 + nanoSeconds / 1000 / 1000 / 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,12 +97,11 @@ export class PlayerCharacterPhysical
|
||||||
name: string,
|
name: string,
|
||||||
killCount: number,
|
killCount: number,
|
||||||
deathCount: number,
|
deathCount: number,
|
||||||
public readonly colorIndex: number,
|
|
||||||
team: CharacterTeam,
|
team: CharacterTeam,
|
||||||
private readonly container: PhysicalContainer,
|
private readonly container: PhysicalContainer,
|
||||||
startPosition: vec2,
|
startPosition: vec2,
|
||||||
) {
|
) {
|
||||||
super(id(), name, killCount, deathCount, colorIndex, team, settings.playerMaxHealth);
|
super(id(), name, killCount, deathCount, team, settings.playerMaxHealth);
|
||||||
|
|
||||||
this.head = new CirclePhysical(
|
this.head = new CirclePhysical(
|
||||||
vec2.add(vec2.create(), startPosition, PlayerCharacterPhysical.headOffset),
|
vec2.add(vec2.create(), startPosition, PlayerCharacterPhysical.headOffset),
|
||||||
|
|
@ -176,7 +175,6 @@ export class PlayerCharacterPhysical
|
||||||
const projectile = new ProjectilePhysical(
|
const projectile = new ProjectilePhysical(
|
||||||
vec2.clone(this.center),
|
vec2.clone(this.center),
|
||||||
20,
|
20,
|
||||||
this.colorIndex,
|
|
||||||
strength,
|
strength,
|
||||||
this.team,
|
this.team,
|
||||||
velocity,
|
velocity,
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,13 @@ export class ProjectilePhysical
|
||||||
constructor(
|
constructor(
|
||||||
center: vec2,
|
center: vec2,
|
||||||
radius: number,
|
radius: number,
|
||||||
colorIndex: number,
|
|
||||||
public strength: number,
|
public strength: number,
|
||||||
public team: CharacterTeam,
|
team: CharacterTeam,
|
||||||
private velocity: vec2,
|
private velocity: vec2,
|
||||||
public readonly originator: PlayerCharacterPhysical,
|
public readonly originator: PlayerCharacterPhysical,
|
||||||
readonly container: PhysicalContainer,
|
readonly container: PhysicalContainer,
|
||||||
) {
|
) {
|
||||||
super(id(), center, radius, colorIndex, strength);
|
super(id(), center, radius, team, strength);
|
||||||
this.object = new CirclePhysical(center, radius, this, container, 0.9);
|
this.object = new CirclePhysical(center, radius, this, container, 0.9);
|
||||||
|
|
||||||
this.moveOutsideOfObject();
|
this.moveOutsideOfObject();
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,5 @@ export interface PhysicalBase {
|
||||||
readonly gameObject: GameObject;
|
readonly gameObject: GameObject;
|
||||||
|
|
||||||
distance(target: vec2): number;
|
distance(target: vec2): number;
|
||||||
|
step(deltaTimeInMilliseconds: number): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
backend/src/players/player-container.ts
Normal file
49
backend/src/players/player-container.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { CharacterTeam, PlayerInformation, Random } from 'shared';
|
||||||
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
|
import { Player } from './player';
|
||||||
|
|
||||||
|
export class PlayerContainer {
|
||||||
|
private _players: Array<Player> = [];
|
||||||
|
|
||||||
|
constructor(private readonly objects: PhysicalContainer) {}
|
||||||
|
|
||||||
|
public createPlayer(playerInfo: PlayerInformation, socket: SocketIO.Socket): Player {
|
||||||
|
const player = new Player(
|
||||||
|
playerInfo,
|
||||||
|
this,
|
||||||
|
this.objects,
|
||||||
|
socket,
|
||||||
|
this.getTeamOfNextPlayer(),
|
||||||
|
);
|
||||||
|
this._players.push(player);
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get players(): Array<Player> {
|
||||||
|
return this._players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get count(): number {
|
||||||
|
return this.players.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public step(deltaTimeInSeconds: number) {
|
||||||
|
this.players.forEach((p) => p.step(deltaTimeInSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTeamOfNextPlayer(): CharacterTeam {
|
||||||
|
const declaCount = this._players.filter((p) => p.team === CharacterTeam.decla).length;
|
||||||
|
const redCount = this._players.filter((p) => p.team === CharacterTeam.red).length;
|
||||||
|
|
||||||
|
if ((declaCount === redCount && Random.getRandom() >= 0.5) || declaCount < redCount) {
|
||||||
|
return CharacterTeam.decla;
|
||||||
|
} else {
|
||||||
|
return CharacterTeam.red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public deletePlayer(player: Player) {
|
||||||
|
this._players = this._players.filter((p) => p !== player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { CharacterTeam, Random } from 'shared';
|
|
||||||
|
|
||||||
let declaCount = 0;
|
|
||||||
let redCount = 0;
|
|
||||||
|
|
||||||
export const requestTeam = (): { team: CharacterTeam; colorIndex: number } => {
|
|
||||||
if ((declaCount === redCount && Random.getRandom() > 0.5) || declaCount < redCount) {
|
|
||||||
declaCount++;
|
|
||||||
return { team: CharacterTeam.decla, colorIndex: 0 };
|
|
||||||
} else {
|
|
||||||
redCount++;
|
|
||||||
return { team: CharacterTeam.red, colorIndex: 1 };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const freeTeam = (team: CharacterTeam) => {
|
|
||||||
if (team === CharacterTeam.decla) {
|
|
||||||
declaCount--;
|
|
||||||
} else {
|
|
||||||
redCount--;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -29,8 +29,8 @@ import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
|
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
|
||||||
import { isCircleIntersecting } from '../physics/functions/is-circle-intersecting';
|
import { isCircleIntersecting } from '../physics/functions/is-circle-intersecting';
|
||||||
import { PlayerCharacterPhysical } from '../objects/player-character-physical';
|
import { PlayerCharacterPhysical } from '../objects/player-character-physical';
|
||||||
import { freeTeam, requestTeam } from './player-team-service';
|
|
||||||
import { PlanetPhysical } from '../objects/planet-physical';
|
import { PlanetPhysical } from '../objects/planet-physical';
|
||||||
|
import { PlayerContainer } from './player-container';
|
||||||
|
|
||||||
export class Player extends CommandReceiver {
|
export class Player extends CommandReceiver {
|
||||||
private character?: PlayerCharacterPhysical | null;
|
private character?: PlayerCharacterPhysical | null;
|
||||||
|
|
@ -66,52 +66,15 @@ export class Player extends CommandReceiver {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
private findEmptyPositionForPlayer(): vec2 {
|
|
||||||
let possibleCenter = this.players.find(
|
|
||||||
(p) => p.character?.isAlive && p.team === this.team,
|
|
||||||
)?.center;
|
|
||||||
|
|
||||||
if (!possibleCenter) {
|
|
||||||
possibleCenter = vec2.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
let rotation = 0;
|
|
||||||
let radius = 0;
|
|
||||||
for (;;) {
|
|
||||||
const playerPosition = vec2.fromValues(
|
|
||||||
radius * Math.cos(rotation) + possibleCenter.x,
|
|
||||||
radius * Math.sin(rotation) + possibleCenter.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
const playerBoundingCircle = new Circle(
|
|
||||||
playerPosition,
|
|
||||||
PlayerCharacterPhysical.boundRadius,
|
|
||||||
);
|
|
||||||
|
|
||||||
const playerBoundingBox = getBoundingBoxOfCircle(playerBoundingCircle);
|
|
||||||
const possibleIntersectors = this.objects.findIntersecting(playerBoundingBox);
|
|
||||||
if (!isCircleIntersecting(playerBoundingCircle, possibleIntersectors)) {
|
|
||||||
return playerPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
rotation += Math.PI / 8;
|
|
||||||
radius += 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly team: CharacterTeam;
|
|
||||||
private colorIndex: number;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly playerInfo: PlayerInformation,
|
private readonly playerInfo: PlayerInformation,
|
||||||
private readonly players: Array<Player>,
|
private readonly playerContainer: PlayerContainer,
|
||||||
private readonly objects: PhysicalContainer,
|
private readonly objectContainer: PhysicalContainer,
|
||||||
private readonly socket: SocketIO.Socket,
|
private readonly socket: SocketIO.Socket,
|
||||||
|
public readonly team: CharacterTeam,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
const { team, colorIndex } = requestTeam();
|
|
||||||
this.team = team;
|
this.team = team;
|
||||||
this.colorIndex = colorIndex;
|
|
||||||
|
|
||||||
this.createCharacter();
|
this.createCharacter();
|
||||||
|
|
||||||
|
|
@ -129,13 +92,12 @@ export class Player extends CommandReceiver {
|
||||||
this.playerInfo.name.slice(0, 20),
|
this.playerInfo.name.slice(0, 20),
|
||||||
this.sumKills,
|
this.sumKills,
|
||||||
this.sumDeaths,
|
this.sumDeaths,
|
||||||
this.colorIndex,
|
|
||||||
this.team,
|
this.team,
|
||||||
this.objects,
|
this.objectContainer,
|
||||||
this.findEmptyPositionForPlayer(),
|
this.findEmptyPositionForPlayer(),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.objects.addObject(this.character);
|
this.objectContainer.addObject(this.character);
|
||||||
this.objectsPreviouslyInViewArea.push(this.character);
|
this.objectsPreviouslyInViewArea.push(this.character);
|
||||||
|
|
||||||
this.socket.emit(
|
this.socket.emit(
|
||||||
|
|
@ -146,7 +108,7 @@ export class Player extends CommandReceiver {
|
||||||
|
|
||||||
private center: vec2 = vec2.create();
|
private center: vec2 = vec2.create();
|
||||||
private timeUntilRespawn = 0;
|
private timeUntilRespawn = 0;
|
||||||
public step(deltaTime: number) {
|
public step(deltaTimeInSeconds: number) {
|
||||||
if (this.character) {
|
if (this.character) {
|
||||||
this.center = this.character?.center;
|
this.center = this.character?.center;
|
||||||
|
|
||||||
|
|
@ -161,7 +123,7 @@ export class Player extends CommandReceiver {
|
||||||
this.character = null;
|
this.character = null;
|
||||||
this.timeUntilRespawn = settings.playerDiedTimeout;
|
this.timeUntilRespawn = settings.playerDiedTimeout;
|
||||||
}
|
}
|
||||||
} else if ((this.timeUntilRespawn -= deltaTime) < 0) {
|
} else if ((this.timeUntilRespawn -= deltaTimeInSeconds) < 0) {
|
||||||
this.createCharacter();
|
this.createCharacter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +133,7 @@ export class Player extends CommandReceiver {
|
||||||
bb.size = viewArea.size;
|
bb.size = viewArea.size;
|
||||||
|
|
||||||
const objectsInViewArea = Array.from(
|
const objectsInViewArea = Array.from(
|
||||||
new Set(this.objects.findIntersecting(bb).map((o) => o.gameObject)),
|
new Set(this.objectContainer.findIntersecting(bb).map((o) => o.gameObject)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const newlyIntersecting = objectsInViewArea.filter(
|
const newlyIntersecting = objectsInViewArea.filter(
|
||||||
|
|
@ -210,6 +172,41 @@ export class Player extends CommandReceiver {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private findEmptyPositionForPlayer(): vec2 {
|
||||||
|
let possibleCenter = this.playerContainer.players.find(
|
||||||
|
(p) => p.character?.isAlive && p.team === this.team,
|
||||||
|
)?.center;
|
||||||
|
|
||||||
|
if (!possibleCenter) {
|
||||||
|
possibleCenter = vec2.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
let rotation = 0;
|
||||||
|
let radius = 0;
|
||||||
|
for (;;) {
|
||||||
|
const playerPosition = vec2.fromValues(
|
||||||
|
radius * Math.cos(rotation) + possibleCenter.x,
|
||||||
|
radius * Math.sin(rotation) + possibleCenter.y,
|
||||||
|
);
|
||||||
|
|
||||||
|
const playerBoundingCircle = new Circle(
|
||||||
|
playerPosition,
|
||||||
|
PlayerCharacterPhysical.boundRadius,
|
||||||
|
);
|
||||||
|
|
||||||
|
const playerBoundingBox = getBoundingBoxOfCircle(playerBoundingCircle);
|
||||||
|
const possibleIntersectors = this.objectContainer.findIntersecting(
|
||||||
|
playerBoundingBox,
|
||||||
|
);
|
||||||
|
if (!isCircleIntersecting(playerBoundingCircle, possibleIntersectors)) {
|
||||||
|
return playerPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
rotation += Math.PI / 8;
|
||||||
|
radius += 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getOtherPlayers(): Array<OtherPlayerDirection> {
|
private getOtherPlayers(): Array<OtherPlayerDirection> {
|
||||||
if (!this.character) {
|
if (!this.character) {
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -220,12 +217,12 @@ export class Player extends CommandReceiver {
|
||||||
bb.topLeft = viewArea.topLeft;
|
bb.topLeft = viewArea.topLeft;
|
||||||
bb.size = viewArea.size;
|
bb.size = viewArea.size;
|
||||||
|
|
||||||
const playersInViewArea = this.objects
|
const playersInViewArea = this.objectContainer
|
||||||
.findIntersecting(bb)
|
.findIntersecting(bb)
|
||||||
.map((o) => o.gameObject)
|
.map((o) => o.gameObject)
|
||||||
.filter((g) => g instanceof PlayerCharacterPhysical);
|
.filter((g) => g instanceof PlayerCharacterPhysical);
|
||||||
|
|
||||||
const otherPlayers = this.players.filter(
|
const otherPlayers = this.playerContainer.players.filter(
|
||||||
(p) => playersInViewArea.indexOf(p.character!) < 0,
|
(p) => playersInViewArea.indexOf(p.character!) < 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -247,10 +244,7 @@ export class Player extends CommandReceiver {
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
freeTeam(this.team);
|
|
||||||
|
|
||||||
if (this.character) {
|
this.character?.destroy();
|
||||||
this.character.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Renderer } from 'sdf-2d';
|
import { Renderer } from 'sdf-2d';
|
||||||
import { CharacterBase, CharacterTeam, Circle, Id, UpdateMessage } from 'shared';
|
import {
|
||||||
|
CharacterBase,
|
||||||
|
CharacterTeam,
|
||||||
|
Circle,
|
||||||
|
Id,
|
||||||
|
settings,
|
||||||
|
UpdateMessage,
|
||||||
|
} from 'shared';
|
||||||
|
|
||||||
import { BlobShape } from '../shapes/blob-shape';
|
import { BlobShape } from '../shapes/blob-shape';
|
||||||
import { ViewObject } from './view-object';
|
import { ViewObject } from './view-object';
|
||||||
|
|
@ -10,15 +17,14 @@ export class CharacterView extends CharacterBase implements ViewObject {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: Id,
|
id: Id,
|
||||||
colorIndex: number,
|
|
||||||
team: CharacterTeam,
|
team: CharacterTeam,
|
||||||
health: number,
|
health: number,
|
||||||
head?: Circle,
|
head?: Circle,
|
||||||
leftFoot?: Circle,
|
leftFoot?: Circle,
|
||||||
rightFoot?: Circle,
|
rightFoot?: Circle,
|
||||||
) {
|
) {
|
||||||
super(id, colorIndex, team, health, head, leftFoot, rightFoot);
|
super(id, team, health, head, leftFoot, rightFoot);
|
||||||
this.shape = new BlobShape(colorIndex);
|
this.shape = new BlobShape(settings.colorIndices[team]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get position(): vec2 {
|
public get position(): vec2 {
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,14 @@ export class PlayerCharacterView extends PlayerCharacterBase implements ViewObje
|
||||||
name: string,
|
name: string,
|
||||||
killCount: number,
|
killCount: number,
|
||||||
deathCount: number,
|
deathCount: number,
|
||||||
colorIndex: number,
|
|
||||||
team: CharacterTeam,
|
team: CharacterTeam,
|
||||||
health: number,
|
health: number,
|
||||||
head?: Circle,
|
head?: Circle,
|
||||||
leftFoot?: Circle,
|
leftFoot?: Circle,
|
||||||
rightFoot?: Circle,
|
rightFoot?: Circle,
|
||||||
) {
|
) {
|
||||||
super(
|
super(id, name, killCount, deathCount, team, health, head, leftFoot, rightFoot);
|
||||||
id,
|
this.shape = new BlobShape(settings.colorIndices[team]);
|
||||||
name,
|
|
||||||
killCount,
|
|
||||||
deathCount,
|
|
||||||
colorIndex,
|
|
||||||
team,
|
|
||||||
health,
|
|
||||||
head,
|
|
||||||
leftFoot,
|
|
||||||
rightFoot,
|
|
||||||
);
|
|
||||||
this.shape = new BlobShape(colorIndex);
|
|
||||||
|
|
||||||
this.previousHealth = this.health;
|
this.previousHealth = this.health;
|
||||||
this.nameElement.className = 'player-tag ' + this.team;
|
this.nameElement.className = 'player-tag ' + this.team;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { CircleLight, ColorfulCircle, Renderer } from 'sdf-2d';
|
import { CircleLight, ColorfulCircle, Renderer } from 'sdf-2d';
|
||||||
import { Id, ProjectileBase, settings, UpdateMessage } from 'shared';
|
import { CharacterTeam, Id, ProjectileBase, settings, UpdateMessage } from 'shared';
|
||||||
import { ViewObject } from './view-object';
|
import { ViewObject } from './view-object';
|
||||||
|
|
||||||
export class ProjectileView extends ProjectileBase implements ViewObject {
|
export class ProjectileView extends ProjectileBase implements ViewObject {
|
||||||
|
|
@ -11,12 +11,16 @@ export class ProjectileView extends ProjectileBase implements ViewObject {
|
||||||
id: Id,
|
id: Id,
|
||||||
center: vec2,
|
center: vec2,
|
||||||
radius: number,
|
radius: number,
|
||||||
colorIndex: number,
|
team: CharacterTeam,
|
||||||
strength: number,
|
strength: number,
|
||||||
) {
|
) {
|
||||||
super(id, center, radius, colorIndex, strength);
|
super(id, center, radius, team, strength);
|
||||||
this.circle = new ColorfulCircle(center, radius / 2, colorIndex);
|
this.circle = new ColorfulCircle(center, radius / 2, settings.colorIndices[team]);
|
||||||
this.light = new CircleLight(center, settings.palette[colorIndex], 0);
|
this.light = new CircleLight(
|
||||||
|
center,
|
||||||
|
settings.paletteDim[settings.colorIndices[team]],
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public step(deltaTimeInMilliseconds: number): void {
|
public step(deltaTimeInMilliseconds: number): void {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { CharacterTeam } from './character-team';
|
||||||
export class CharacterBase extends GameObject {
|
export class CharacterBase extends GameObject {
|
||||||
constructor(
|
constructor(
|
||||||
id: Id,
|
id: Id,
|
||||||
public colorIndex: number,
|
|
||||||
public team: CharacterTeam,
|
public team: CharacterTeam,
|
||||||
public health: number,
|
public health: number,
|
||||||
public head?: Circle,
|
public head?: Circle,
|
||||||
|
|
@ -19,7 +18,7 @@ export class CharacterBase extends GameObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
const { id, colorIndex, team, health, head, leftFoot, rightFoot } = this;
|
const { id, team, health, head, leftFoot, rightFoot } = this;
|
||||||
return [id, colorIndex, team, health, head, leftFoot, rightFoot];
|
return [id, team, health, head, leftFoot, rightFoot];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export enum CharacterTeam {
|
export enum CharacterTeam {
|
||||||
decla = 'decla',
|
decla = 'decla',
|
||||||
|
neutral = 'neutral',
|
||||||
red = 'red',
|
red = 'red',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,13 @@ export class PlayerCharacterBase extends CharacterBase {
|
||||||
public name: string,
|
public name: string,
|
||||||
public killCount: number,
|
public killCount: number,
|
||||||
public deathCount: number,
|
public deathCount: number,
|
||||||
colorIndex: number,
|
|
||||||
team: CharacterTeam,
|
team: CharacterTeam,
|
||||||
health: number,
|
health: number,
|
||||||
head?: Circle,
|
head?: Circle,
|
||||||
leftFoot?: Circle,
|
leftFoot?: Circle,
|
||||||
rightFoot?: Circle,
|
rightFoot?: Circle,
|
||||||
) {
|
) {
|
||||||
super(id, colorIndex, team, health, head, leftFoot, rightFoot);
|
super(id, team, health, head, leftFoot, rightFoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
|
|
@ -27,7 +26,6 @@ export class PlayerCharacterBase extends CharacterBase {
|
||||||
this.name,
|
this.name,
|
||||||
this.killCount,
|
this.killCount,
|
||||||
this.deathCount,
|
this.deathCount,
|
||||||
this.colorIndex,
|
|
||||||
this.team,
|
this.team,
|
||||||
this.health,
|
this.health,
|
||||||
this.head,
|
this.head,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { vec2 } from 'gl-matrix';
|
||||||
import { Id } from '../../transport/identity';
|
import { Id } from '../../transport/identity';
|
||||||
import { serializable } from '../../transport/serialization/serializable';
|
import { serializable } from '../../transport/serialization/serializable';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
|
import { CharacterTeam } from './character-team';
|
||||||
|
|
||||||
@serializable
|
@serializable
|
||||||
export class ProjectileBase extends GameObject {
|
export class ProjectileBase extends GameObject {
|
||||||
|
|
@ -9,13 +10,13 @@ export class ProjectileBase extends GameObject {
|
||||||
id: Id,
|
id: Id,
|
||||||
public center: vec2,
|
public center: vec2,
|
||||||
public radius: number,
|
public radius: number,
|
||||||
public colorIndex: number,
|
public team: CharacterTeam,
|
||||||
public strength: number,
|
public strength: number,
|
||||||
) {
|
) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
return [this.id, this.center, this.radius, this.colorIndex, this.strength];
|
return [this.id, this.center, this.radius, this.team, this.strength];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
import { rgb255 } from './helper/rgb255';
|
import { rgb255 } from './helper/rgb255';
|
||||||
|
import { CharacterTeam } from './objects/types/character-team';
|
||||||
|
|
||||||
const q = 2.5;
|
const q = 2.5;
|
||||||
|
const qDim = 1.5;
|
||||||
const declaColor = rgb255(64 * q, 105 * q, 165 * q);
|
const declaColor = rgb255(64 * q, 105 * q, 165 * q);
|
||||||
|
const neutralColor = rgb255(82 * q, 165 * q, 64 * q);
|
||||||
const redColor = rgb255(209 * q, 86 * q, 82 * q);
|
const redColor = rgb255(209 * q, 86 * q, 82 * q);
|
||||||
|
const declaColorDim = rgb255(64 * qDim, 105 * qDim, 165 * qDim);
|
||||||
|
const redColorDim = rgb255(209 * qDim, 8 * qDim, 82 * qDim);
|
||||||
const declaPlanetColor = declaColor;
|
const declaPlanetColor = declaColor;
|
||||||
const redPlanetColor = redColor;
|
const redPlanetColor = redColor;
|
||||||
|
|
||||||
|
|
@ -32,14 +37,18 @@ export const settings = {
|
||||||
projectileFadeSpeed: 20,
|
projectileFadeSpeed: 20,
|
||||||
projectileCreationInterval: 0.1,
|
projectileCreationInterval: 0.1,
|
||||||
playerColorIndexOffset: 3,
|
playerColorIndexOffset: 3,
|
||||||
backgroundGradient: [rgb255(90, 38, 43), rgb255(43, 39, 73)],
|
backgroundGradient: [rgb255(90, 38, 43), rgb255(0, 0, 0), rgb255(43, 39, 73)],
|
||||||
declaColor,
|
declaColor,
|
||||||
declaPlanetColor,
|
declaPlanetColor,
|
||||||
redColor,
|
redColor,
|
||||||
redPlanetColor,
|
redPlanetColor,
|
||||||
declaIndex: 0,
|
colorIndices: {
|
||||||
redIndex: 1,
|
[CharacterTeam.decla]: 0,
|
||||||
palette: [declaColor, redColor],
|
[CharacterTeam.neutral]: 1,
|
||||||
|
[CharacterTeam.red]: 2,
|
||||||
|
},
|
||||||
|
palette: [declaColor, neutralColor, redColor],
|
||||||
|
paletteDim: [declaColorDim, neutralColor, redColorDim],
|
||||||
targetPhysicsDeltaTimeInMilliseconds: 20,
|
targetPhysicsDeltaTimeInMilliseconds: 20,
|
||||||
minPhysicsSleepTime: 4,
|
minPhysicsSleepTime: 4,
|
||||||
velocityAttenuation: 0.25,
|
velocityAttenuation: 0.25,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue