Refactor
This commit is contained in:
parent
1ce961d6c2
commit
99cdb62928
76 changed files with 338 additions and 431 deletions
15
.do/apps.yml
15
.do/apps.yml
|
|
@ -1,15 +0,0 @@
|
||||||
name: decla-red-server
|
|
||||||
region: fra
|
|
||||||
services:
|
|
||||||
- dockerfile_path: Dockerfile
|
|
||||||
github:
|
|
||||||
branch: main
|
|
||||||
deploy_on_push: true
|
|
||||||
repo: schmelczerandras/decla.red
|
|
||||||
http_port: 3000
|
|
||||||
instance_count: 1
|
|
||||||
instance_size_slug: basic-xxs
|
|
||||||
run_command: ' declared-server --name=Frankfurt --seed=102'
|
|
||||||
name: decla-red-server
|
|
||||||
routes:
|
|
||||||
- path: /
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
.dockerignore
|
|
||||||
Dockerfile
|
|
||||||
**/node_modules
|
|
||||||
node_modules
|
|
||||||
**/package-lock.json
|
|
||||||
package-lock.json
|
|
||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,6 +1,9 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"Deserializable",
|
||||||
|
"Deserialization",
|
||||||
"decla",
|
"decla",
|
||||||
|
"overridable",
|
||||||
"serializable"
|
"serializable"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
FROM node:14.13.0-alpine3.10
|
|
||||||
|
|
||||||
RUN npm i -g declared-server
|
|
||||||
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
CMD ["--port=3000", "--name=Docker server", "--seed=500"]
|
|
||||||
ENTRYPOINT [ "NODE_ENV=production node", "declared-server" ]
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "declared-server",
|
"name": "declared-server",
|
||||||
"version": "0.0.11",
|
"version": "0.0.12",
|
||||||
"description": "Game server for decla.red",
|
"description": "Game server for decla.red",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)",
|
"author": "András Schmelczer <andras@schmelczer.dev> (https://schmelczer.dev/)",
|
||||||
|
|
|
||||||
74
backend/src/create-world.ts
Normal file
74
backend/src/create-world.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { Random, PlanetBase, hsl, settings } from 'shared';
|
||||||
|
import { LampPhysical } from './objects/lamp-physical';
|
||||||
|
import { PlanetPhysical } from './objects/planet-physical';
|
||||||
|
import { PhysicalContainer } from './physics/containers/physical-container';
|
||||||
|
import { evaluateSdf } from './physics/functions/evaluate-sdf';
|
||||||
|
import { Physical } from './physics/physicals/physical';
|
||||||
|
|
||||||
|
export const createWorld = (objectContainer: PhysicalContainer, worldRadius: number) => {
|
||||||
|
const objects: Array<Physical> = [];
|
||||||
|
const lights: Array<Physical> = [];
|
||||||
|
|
||||||
|
for (let r = 0; r < worldRadius; r += settings.radiusSteps) {
|
||||||
|
const circumference = 2 * Math.PI * r;
|
||||||
|
const stepCount = circumference * settings.objectsOnCircleLength;
|
||||||
|
for (let rad = 0; rad < 2 * Math.PI; rad += (2 * Math.PI) / stepCount) {
|
||||||
|
const position = vec2.rotate(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.fromValues(r, 0),
|
||||||
|
vec2.create(),
|
||||||
|
rad,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (objects.length !== 0 && Random.getRandom() > 0.5) {
|
||||||
|
if (
|
||||||
|
evaluateSdf(position, objects) > 200 &&
|
||||||
|
!lights.find((l) => l.distance(position) < 2500)
|
||||||
|
) {
|
||||||
|
lights.push(
|
||||||
|
new LampPhysical(
|
||||||
|
position,
|
||||||
|
hsl(
|
||||||
|
(rad / (2 * Math.PI)) * 360,
|
||||||
|
Random.getRandomInRange(50, 100),
|
||||||
|
Random.getRandomInRange(40, 50),
|
||||||
|
),
|
||||||
|
Random.getRandomInRange(0.35, 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
evaluateSdf(position, objects) > 1400 &&
|
||||||
|
!lights.find((l) => l.distance(position) < 1700)
|
||||||
|
) {
|
||||||
|
const planet =
|
||||||
|
objects.length === 0
|
||||||
|
? new PlanetPhysical(
|
||||||
|
PlanetBase.createPlanetVertices(
|
||||||
|
position,
|
||||||
|
Random.getRandomInRange(1600, 2400),
|
||||||
|
Random.getRandomInRange(1600, 2400),
|
||||||
|
Random.getRandomInRange(80, 300),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: new PlanetPhysical(
|
||||||
|
PlanetBase.createPlanetVertices(
|
||||||
|
position,
|
||||||
|
Random.getRandomInRange(300, 1600),
|
||||||
|
Random.getRandomInRange(300, 1600),
|
||||||
|
Random.getRandomInRange(20, 100),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
objects.push(planet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('Generated planet count', objects.length);
|
||||||
|
console.log('Generated light count', lights.length);
|
||||||
|
|
||||||
|
[...objects, ...lights].forEach((o) => objectContainer.addObject(o));
|
||||||
|
};
|
||||||
|
|
@ -7,14 +7,12 @@ import {
|
||||||
ServerInformation,
|
ServerInformation,
|
||||||
PlayerInformation,
|
PlayerInformation,
|
||||||
UpdateGameState,
|
UpdateGameState,
|
||||||
serialize,
|
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
GameEnd,
|
GameEndCommand,
|
||||||
GameStart,
|
GameStartCommand,
|
||||||
Command,
|
Command,
|
||||||
last,
|
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { createWorld } from './map/create-world';
|
import { createWorld } from './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';
|
import { PlayerContainer } from './players/player-container';
|
||||||
|
|
@ -52,7 +50,7 @@ export class GameServer {
|
||||||
this.redPoints = 0;
|
this.redPoints = 0;
|
||||||
this.isInEndGame = false;
|
this.isInEndGame = false;
|
||||||
this.timeScaling = 1;
|
this.timeScaling = 1;
|
||||||
previousPlayers?.queueCommandForEachClient(new GameStart());
|
previousPlayers?.queueCommandForEachClient(new GameStartCommand());
|
||||||
previousPlayers?.sendQueuedCommands();
|
previousPlayers?.sendQueuedCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,7 +117,7 @@ export class GameServer {
|
||||||
this.isInEndGame = true;
|
this.isInEndGame = true;
|
||||||
const endTitleLength = 6000;
|
const endTitleLength = 6000;
|
||||||
this.players.queueCommandForEachClient(
|
this.players.queueCommandForEachClient(
|
||||||
new GameEnd(winningTeam, endTitleLength / 1000, true),
|
new GameEndCommand(winningTeam, endTitleLength / 1000, true),
|
||||||
);
|
);
|
||||||
setTimeout(() => this.destroy(), endTitleLength * 1.1);
|
setTimeout(() => this.destroy(), endTitleLength * 1.1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
export const getTimeInMilliseconds = (): number => {
|
|
||||||
const [seconds, nanoSeconds] = process.hrtime();
|
|
||||||
|
|
||||||
return seconds * 1000 + nanoSeconds / 1000 / 1000;
|
|
||||||
};
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
const shortAngleDist = (a0: number, a1: number): number => {
|
export const interpolateAngles = (from: number, to: number, q: number) => {
|
||||||
const max = Math.PI * 2;
|
const max = Math.PI * 2;
|
||||||
const da = (a1 - a0) % max;
|
const possibleDistance = (to - from) % max;
|
||||||
return ((2 * da) % max) - da;
|
const shortedDistance = ((2 * possibleDistance) % max) - possibleDistance;
|
||||||
};
|
return from + shortedDistance * q;
|
||||||
|
|
||||||
export const interpolateAngles = (a0: number, a1: number, t: number) => {
|
|
||||||
return a0 + shortAngleDist(a0, a1) * t;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
|
||||||
import {
|
import {
|
||||||
ReactsToCollision,
|
ReactsToCollision,
|
||||||
reactsToCollision,
|
reactsToCollision,
|
||||||
} from '../physics/physicals/reacts-to-collision';
|
} from '../physics/functions/reacts-to-collision';
|
||||||
|
|
||||||
@serializesTo(Circle)
|
@serializesTo(Circle)
|
||||||
export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision {
|
export class CirclePhysical implements Circle, DynamicPhysical, ReactsToCollision {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { interpolateAngles } from '../helper/interpolate-angles';
|
||||||
import { forceAtPosition } from '../physics/functions/force-at-position';
|
import { forceAtPosition } from '../physics/functions/force-at-position';
|
||||||
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
|
import { getBoundingBoxOfCircle } from '../physics/functions/get-bounding-box-of-circle';
|
||||||
import { PlanetPhysical } from './planet-physical';
|
import { PlanetPhysical } from './planet-physical';
|
||||||
import { ReactsToCollision } from '../physics/physicals/reacts-to-collision';
|
import { ReactsToCollision } from '../physics/functions/reacts-to-collision';
|
||||||
|
|
||||||
@serializesTo(PlayerCharacterBase)
|
@serializesTo(PlayerCharacterBase)
|
||||||
export class PlayerCharacterPhysical
|
export class PlayerCharacterPhysical
|
||||||
|
|
@ -479,6 +479,7 @@ export class PlayerCharacterPhysical
|
||||||
|
|
||||||
public kill() {
|
public kill() {
|
||||||
this.isDestroyed = true;
|
this.isDestroyed = true;
|
||||||
|
this.remoteCall('kill');
|
||||||
}
|
}
|
||||||
|
|
||||||
private destroy() {
|
private destroy() {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { CirclePhysical } from './circle-physical';
|
||||||
import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
|
import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
|
||||||
import { PhysicalContainer } from '../physics/containers/physical-container';
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
import { PlanetPhysical } from './planet-physical';
|
import { PlanetPhysical } from './planet-physical';
|
||||||
import { ReactsToCollision } from '../physics/physicals/reacts-to-collision';
|
import { ReactsToCollision } from '../physics/functions/reacts-to-collision';
|
||||||
import { PlayerCharacterPhysical } from './player-character-physical';
|
import { PlayerCharacterPhysical } from './player-character-physical';
|
||||||
import { moveCircle } from '../physics/functions/move-circle';
|
import { moveCircle } from '../physics/functions/move-circle';
|
||||||
|
|
||||||
|
|
@ -114,24 +114,6 @@ export class ProjectilePhysical
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const gravity = vec2.create();
|
|
||||||
const intersecting = this.container.findIntersecting(this.boundingBox);
|
|
||||||
intersecting.forEach((i) => {
|
|
||||||
if (i instanceof PlanetPhysical) {
|
|
||||||
vec2.add(gravity, gravity, i.getForce(this.center));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
vec2.add(
|
|
||||||
this.velocity,
|
|
||||||
this.velocity,
|
|
||||||
vec2.scale(
|
|
||||||
vec2.create(),
|
|
||||||
gravity,
|
|
||||||
deltaTime * settings.gravityScalingForProjectiles,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
vec2.copy(this.object.velocity, this.velocity);
|
vec2.copy(this.object.velocity, this.velocity);
|
||||||
const { velocity } = this.object.step2(deltaTime);
|
const { velocity } = this.object.step2(deltaTime);
|
||||||
vec2.copy(this.velocity, velocity);
|
vec2.copy(this.velocity, velocity);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export class BoundingBoxBase {
|
// axes aligned
|
||||||
|
export abstract class BoundingBoxBase {
|
||||||
constructor(
|
constructor(
|
||||||
protected _xMin: number = 0,
|
protected _xMin: number = 0,
|
||||||
protected _xMax: number = 0,
|
protected _xMax: number = 0,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
|
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
|
||||||
import { StaticPhysical } from '../physicals/static-physical';
|
import { StaticPhysical } from '../physicals/static-physical';
|
||||||
// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js
|
|
||||||
|
|
||||||
|
// source: https://github.com/ubilabs/kd-tree-javascript/blob/master/kdTree.js
|
||||||
class Node {
|
class Node {
|
||||||
public left: Node | null = null;
|
public left: Node | null = null;
|
||||||
public right: Node | null = null;
|
public right: Node | null = null;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { Circle } from 'shared';
|
import { Circle } from 'shared';
|
||||||
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
|
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
|
||||||
|
import { ImmutableBoundingBox } from '../bounding-boxes/immutable-bounding-box';
|
||||||
|
|
||||||
export const getBoundingBoxOfCircle = (circle: Circle): BoundingBoxBase =>
|
export const getBoundingBoxOfCircle = (circle: Circle): BoundingBoxBase =>
|
||||||
new BoundingBoxBase(
|
new ImmutableBoundingBox(
|
||||||
circle.center.x - circle.radius,
|
circle.center.x - circle.radius,
|
||||||
circle.center.x + circle.radius,
|
circle.center.x + circle.radius,
|
||||||
circle.center.y - circle.radius,
|
circle.center.y - circle.radius,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Circle, GameObject, rotate90Deg } from 'shared';
|
import { Circle, GameObject, rotate90Deg } from 'shared';
|
||||||
import { CirclePhysical } from '../../objects/circle-physical';
|
import { CirclePhysical } from '../../objects/circle-physical';
|
||||||
import { reactsToCollision } from '../physicals/reacts-to-collision';
|
import { reactsToCollision } from './reacts-to-collision';
|
||||||
import { evaluateSdf } from './evaluate-sdf';
|
import { evaluateSdf } from './evaluate-sdf';
|
||||||
import { Physical } from '../physicals/physical';
|
import { Physical } from '../physicals/physical';
|
||||||
|
|
||||||
|
|
|
||||||
8
backend/src/physics/functions/reacts-to-collision.ts
Normal file
8
backend/src/physics/functions/reacts-to-collision.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { GameObject } from 'shared';
|
||||||
|
|
||||||
|
export interface ReactsToCollision {
|
||||||
|
onCollision(other: GameObject): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const reactsToCollision = (a: any): a is ReactsToCollision =>
|
||||||
|
a && 'onCollision' in a;
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
SetAspectRatioActionCommand,
|
SetAspectRatioActionCommand,
|
||||||
calculateViewArea,
|
calculateViewArea,
|
||||||
SecondaryActionCommand,
|
SecondaryActionCommand,
|
||||||
PlayerDiedCommand,
|
|
||||||
settings,
|
settings,
|
||||||
PlayerInformation,
|
PlayerInformation,
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
|
|
@ -23,21 +22,21 @@ import {
|
||||||
ServerAnnouncement,
|
ServerAnnouncement,
|
||||||
PropertyUpdatesForObjects,
|
PropertyUpdatesForObjects,
|
||||||
PropertyUpdatesForObject,
|
PropertyUpdatesForObject,
|
||||||
|
PrimaryActionCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { getTimeInMilliseconds } from '../helper/get-time-in-milliseconds';
|
|
||||||
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
|
import { BoundingBox } from '../physics/bounding-boxes/bounding-box';
|
||||||
import { PhysicalContainer } from '../physics/containers/physical-container';
|
import { PhysicalContainer } from '../physics/containers/physical-container';
|
||||||
import { PlayerCharacterPhysical } from '../objects/player-character-physical';
|
import { PlayerCharacterPhysical } from '../objects/player-character-physical';
|
||||||
import { PlayerContainer } from './player-container';
|
import { PlayerContainer } from './player-container';
|
||||||
import { PlayerBase } from './player-base';
|
import { PlayerBase } from './player-base';
|
||||||
|
import { DeltaTimeCalculator } from '../helper/delta-time-calculator';
|
||||||
|
|
||||||
export class Player extends PlayerBase {
|
export class Player extends PlayerBase {
|
||||||
private aspectRatio: number = 16 / 9;
|
private aspectRatio: number = 16 / 9;
|
||||||
private isActive = true;
|
private isActive = true;
|
||||||
|
|
||||||
private objectsPreviouslyInViewArea: Array<GameObject> = [];
|
private objectsPreviouslyInViewArea: Array<GameObject> = [];
|
||||||
|
private pingDeltaTime = new DeltaTimeCalculator();
|
||||||
private pingTime?: number;
|
|
||||||
private _latency?: number;
|
private _latency?: number;
|
||||||
|
|
||||||
protected commandExecutors: CommandExecutors = {
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
|
@ -45,7 +44,7 @@ export class Player extends PlayerBase {
|
||||||
(this.aspectRatio = v.aspectRatio),
|
(this.aspectRatio = v.aspectRatio),
|
||||||
[MoveActionCommand.type]: (c: MoveActionCommand) =>
|
[MoveActionCommand.type]: (c: MoveActionCommand) =>
|
||||||
this.character?.handleMovementAction(c),
|
this.character?.handleMovementAction(c),
|
||||||
[SecondaryActionCommand.type]: (c: SecondaryActionCommand) => {
|
[PrimaryActionCommand.type]: (c: PrimaryActionCommand) => {
|
||||||
this.character?.shootTowards(c.position);
|
this.character?.shootTowards(c.position);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -63,7 +62,7 @@ export class Player extends PlayerBase {
|
||||||
|
|
||||||
socket.on(
|
socket.on(
|
||||||
TransportEvents.Pong,
|
TransportEvents.Pong,
|
||||||
() => (this._latency = getTimeInMilliseconds() - this.pingTime!),
|
() => (this._latency = this.pingDeltaTime.getNextDeltaTimeInSeconds()),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.measureLatency();
|
this.measureLatency();
|
||||||
|
|
@ -71,8 +70,9 @@ export class Player extends PlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public measureLatency() {
|
public measureLatency() {
|
||||||
this.pingTime = getTimeInMilliseconds();
|
this.pingDeltaTime.getNextDeltaTimeInSeconds(true);
|
||||||
this.socket.emit(TransportEvents.Ping);
|
this.socket.emit(TransportEvents.Ping);
|
||||||
|
|
||||||
if (this.isActive) {
|
if (this.isActive) {
|
||||||
setTimeout(this.measureLatency.bind(this), 10000);
|
setTimeout(this.measureLatency.bind(this), 10000);
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +106,6 @@ export class Player extends PlayerBase {
|
||||||
this.sumDeaths++;
|
this.sumDeaths++;
|
||||||
this.sumKills = this.character.killCount;
|
this.sumKills = this.character.killCount;
|
||||||
|
|
||||||
this.queueCommandSend(new PlayerDiedCommand(settings.playerDiedTimeout));
|
|
||||||
this.character = null;
|
this.character = null;
|
||||||
this.timeUntilRespawn = settings.playerDiedTimeout;
|
this.timeUntilRespawn = settings.playerDiedTimeout;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
frontend/src/scripts/commands/command-socket.ts
Normal file
19
frontend/src/scripts/commands/command-socket.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Command, CommandReceiver, serialize, TransportEvents } from 'shared';
|
||||||
|
|
||||||
|
export class CommandSocket extends CommandReceiver {
|
||||||
|
constructor(private readonly socket: SocketIOClient.Socket) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private commandQueue: Array<Command> = [];
|
||||||
|
protected defaultCommandExecutor(command: Command) {
|
||||||
|
this.commandQueue.push(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendQueuedCommands() {
|
||||||
|
if (this.commandQueue.length > 0) {
|
||||||
|
this.socket.emit(TransportEvents.PlayerToServer, serialize(this.commandQueue));
|
||||||
|
this.commandQueue = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
import { CommandGenerator, SecondaryActionCommand, TernaryActionCommand } from 'shared';
|
|
||||||
import { Game } from '../../game';
|
|
||||||
|
|
||||||
export class MouseListener extends CommandGenerator {
|
|
||||||
constructor(target: HTMLElement, private readonly game: Game) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
target.addEventListener('mousedown', (event: MouseEvent) => {
|
|
||||||
const position = this.positionFromEvent(event);
|
|
||||||
|
|
||||||
if (event.button == 0) {
|
|
||||||
this.sendCommandToSubscribers(new SecondaryActionCommand(position));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
target.addEventListener('contextmenu', (event: MouseEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const position = this.positionFromEvent(event);
|
|
||||||
this.sendCommandToSubscribers(new TernaryActionCommand(position));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private positionFromEvent(event: MouseEvent): vec2 {
|
|
||||||
return this.game.displayToWorldCoordinates(
|
|
||||||
vec2.fromValues(event.clientX, event.clientY),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue