Refactor frontend to use commands
This commit is contained in:
parent
be26ab422c
commit
503c99cb1f
25 changed files with 8031 additions and 23084 deletions
|
|
@ -42,11 +42,11 @@
|
||||||
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
|
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
|
||||||
"workspaceFolder": "/workspace",
|
"workspaceFolder": "/workspace",
|
||||||
"mounts": [
|
"mounts": [
|
||||||
"source=decla-red-root-node_modules-volume3,target=/workspace/node_modules,type=volume",
|
"source=decla-red-root-node_modules-volume4,target=/workspace/node_modules,type=volume",
|
||||||
"source=decla-red-frontend-node_modules-volume3,target=/workspace/frontend/node_modules,type=volume",
|
"source=decla-red-frontend-node_modules-volume4,target=/workspace/frontend/node_modules,type=volume",
|
||||||
"source=decla-red-backend-node_modules-volume3,target=/workspace/backend/node_modules,type=volume",
|
"source=decla-red-backend-node_modules-volume4,target=/workspace/backend/node_modules,type=volume",
|
||||||
"source=decla-red-shared-node_modules-volume3,target=/workspace/shared/node_modules,type=volume"
|
"source=decla-red-shared-node_modules-volume4,target=/workspace/shared/node_modules,type=volume"
|
||||||
],
|
],
|
||||||
"forwardPorts": [3000, 8080],
|
"forwardPorts": [3000, 8080],
|
||||||
"postCreateCommand": "chown node:node ./**/node_modules && yarn"
|
"postCreateCommand": "chown node:node ./**/node_modules && npm run build"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npx webpack --mode production",
|
"build": "npx webpack --mode production",
|
||||||
"start": "npx concurrently --kill-others-on-fail \"npx webpack --mode development -w\" \"npx nodemon --legacy-watch dist/main.js\"",
|
"start": "concurrently --kill-others-on-fail \"webpack --mode development -w\" \"nodemon --legacy-watch dist/main.js\"",
|
||||||
"try-build": "npm run build && cd dist && node main.js && cd -"
|
"try-build": "npm run build && cd dist && node main.js && cd -"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ export class GameServer {
|
||||||
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
socket.on(TransportEvents.PlayerToServer, (json: string) => {
|
||||||
try {
|
try {
|
||||||
const commands: Array<Command> = deserialize(json);
|
const commands: Array<Command> = deserialize(json);
|
||||||
commands.forEach((c) => player.sendCommand(c));
|
commands.forEach((c) => player.handleCommand(c));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error while processing command', e);
|
console.error('Error while processing command', e);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
CharacterBase,
|
CharacterBase,
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
PropertyUpdatesForObject,
|
PropertyUpdatesForObject,
|
||||||
UpdateProperty,
|
UpdatePropertyCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
|
import { DynamicPhysical } from '../physics/physicals/dynamic-physical';
|
||||||
import { CirclePhysical } from './circle-physical';
|
import { CirclePhysical } from './circle-physical';
|
||||||
|
|
@ -256,9 +256,9 @@ export class CharacterPhysical
|
||||||
|
|
||||||
public getPropertyUpdates(): PropertyUpdatesForObject {
|
public getPropertyUpdates(): PropertyUpdatesForObject {
|
||||||
return new PropertyUpdatesForObject(this.id, [
|
return new PropertyUpdatesForObject(this.id, [
|
||||||
new UpdateProperty('head', this.head, this.headVelocity),
|
new UpdatePropertyCommand('head', this.head, this.headVelocity),
|
||||||
new UpdateProperty('leftFoot', this.leftFoot, this.leftFootVelocity),
|
new UpdatePropertyCommand('leftFoot', this.leftFoot, this.leftFootVelocity),
|
||||||
new UpdateProperty('rightFoot', this.rightFoot, this.rightFootVelocity),
|
new UpdatePropertyCommand('rightFoot', this.rightFoot, this.rightFootVelocity),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
PlanetBase,
|
PlanetBase,
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
PropertyUpdatesForObject,
|
PropertyUpdatesForObject,
|
||||||
UpdateProperty,
|
UpdatePropertyCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
|
|
||||||
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
||||||
|
|
@ -110,7 +110,7 @@ export class PlanetPhysical
|
||||||
|
|
||||||
public getPropertyUpdates(): PropertyUpdatesForObject {
|
public getPropertyUpdates(): PropertyUpdatesForObject {
|
||||||
return new PropertyUpdatesForObject(this.id, [
|
return new PropertyUpdatesForObject(this.id, [
|
||||||
new UpdateProperty('ownership', this.ownership, 0),
|
new UpdatePropertyCommand('ownership', this.ownership, 0),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
GameObject,
|
GameObject,
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
PropertyUpdatesForObject,
|
PropertyUpdatesForObject,
|
||||||
UpdateProperty,
|
UpdatePropertyCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
|
||||||
import { CirclePhysical } from './circle-physical';
|
import { CirclePhysical } from './circle-physical';
|
||||||
|
|
@ -102,7 +102,7 @@ export class ProjectilePhysical
|
||||||
|
|
||||||
public getPropertyUpdates(): PropertyUpdatesForObject {
|
public getPropertyUpdates(): PropertyUpdatesForObject {
|
||||||
return new PropertyUpdatesForObject(this.id, [
|
return new PropertyUpdatesForObject(this.id, [
|
||||||
new UpdateProperty('center', this.center, this.velocity),
|
new UpdatePropertyCommand('center', this.center, this.velocity),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
30629
frontend/package-lock.json
generated
30629
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -6,9 +6,6 @@ import {
|
||||||
CharacterBase,
|
CharacterBase,
|
||||||
ProjectileBase,
|
ProjectileBase,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { LampView } from './scripts/objects/lamp-view';
|
|
||||||
import { ProjectileView } from './scripts/objects/projectile-view';
|
|
||||||
import { PlanetView } from './scripts/objects/planet-view';
|
|
||||||
import './main.scss';
|
import './main.scss';
|
||||||
import '../static/og-image.png';
|
import '../static/og-image.png';
|
||||||
import '../static/favicons/apple-touch-icon.png';
|
import '../static/favicons/apple-touch-icon.png';
|
||||||
|
|
@ -19,7 +16,6 @@ import { LandingPageBackground } from './scripts/landing-page-background';
|
||||||
import { JoinFormHandler } from './scripts/join-form-handler';
|
import { JoinFormHandler } from './scripts/join-form-handler';
|
||||||
import { handleFullScreen } from './scripts/helper/handle-full-screen';
|
import { handleFullScreen } from './scripts/helper/handle-full-screen';
|
||||||
import { Game } from './scripts/game';
|
import { Game } from './scripts/game';
|
||||||
import { CharacterView } from './scripts/objects/character-view';
|
|
||||||
import { handleInsights } from './scripts/handle-insights';
|
import { handleInsights } from './scripts/handle-insights';
|
||||||
import { getInsightsFromRenderer } from './scripts/get-insights-from-renderer';
|
import { getInsightsFromRenderer } from './scripts/get-insights-from-renderer';
|
||||||
import { Renderer } from 'sdf-2d';
|
import { Renderer } from 'sdf-2d';
|
||||||
|
|
@ -29,6 +25,10 @@ import { hide } from './scripts/helper/hide';
|
||||||
import { show } from './scripts/helper/show';
|
import { show } from './scripts/helper/show';
|
||||||
import { SoundHandler, Sounds } from './scripts/sound-handler';
|
import { SoundHandler, Sounds } from './scripts/sound-handler';
|
||||||
import { VibrationHandler } from './scripts/vibration-handler';
|
import { VibrationHandler } from './scripts/vibration-handler';
|
||||||
|
import { CharacterView } from './scripts/objects/types/character-view';
|
||||||
|
import { LampView } from './scripts/objects/types/lamp-view';
|
||||||
|
import { PlanetView } from './scripts/objects/types/planet-view';
|
||||||
|
import { ProjectileView } from './scripts/objects/types/projectile-view';
|
||||||
|
|
||||||
glMatrix.setMatrixArrayType(Array);
|
glMatrix.setMatrixArrayType(Array);
|
||||||
|
|
||||||
|
|
|
||||||
7
frontend/src/scripts/commands/types/before-destroy.ts
Normal file
7
frontend/src/scripts/commands/types/before-destroy.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Command } from 'shared';
|
||||||
|
|
||||||
|
export class BeforeDestroyCommand extends Command {
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
frontend/src/scripts/commands/types/render.ts
Normal file
13
frontend/src/scripts/commands/types/render.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { Renderer } from 'sdf-2d';
|
||||||
|
import { Command } from 'shared';
|
||||||
|
|
||||||
|
export class RenderCommand extends Command {
|
||||||
|
public constructor(
|
||||||
|
public readonly renderer: Renderer,
|
||||||
|
public readonly overlay: HTMLElement,
|
||||||
|
public readonly shouldChangeLayout: boolean,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
7
frontend/src/scripts/commands/types/step.ts
Normal file
7
frontend/src/scripts/commands/types/step.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Command } from 'shared';
|
||||||
|
|
||||||
|
export class StepCommand extends Command {
|
||||||
|
public constructor(public readonly deltaTimeInSeconds: number) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,8 @@ import { GameObjectContainer } from './objects/game-object-container';
|
||||||
import parser from 'socket.io-msgpack-parser';
|
import parser from 'socket.io-msgpack-parser';
|
||||||
import { BlobShape } from './shapes/blob-shape';
|
import { BlobShape } from './shapes/blob-shape';
|
||||||
import { PlanetShape } from './shapes/planet-shape';
|
import { PlanetShape } from './shapes/planet-shape';
|
||||||
|
import { RenderCommand } from './commands/types/render';
|
||||||
|
import { StepCommand } from './commands/types/step';
|
||||||
|
|
||||||
export class Game extends CommandReceiver {
|
export class Game extends CommandReceiver {
|
||||||
public gameObjects = new GameObjectContainer(this);
|
public gameObjects = new GameObjectContainer(this);
|
||||||
|
|
@ -107,7 +109,7 @@ export class Game extends CommandReceiver {
|
||||||
|
|
||||||
this.socket.on(TransportEvents.ServerToPlayer, (serializedCommands: string) => {
|
this.socket.on(TransportEvents.ServerToPlayer, (serializedCommands: string) => {
|
||||||
const commands: Array<Command> = deserialize(serializedCommands);
|
const commands: Array<Command> = deserialize(serializedCommands);
|
||||||
commands.forEach((c) => this.sendCommand(c));
|
commands.forEach((c) => this.handleCommand(c));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socketReceiver = new CommandSocket(this.socket);
|
this.socketReceiver = new CommandSocket(this.socket);
|
||||||
|
|
@ -124,7 +126,7 @@ export class Game extends CommandReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected defaultCommandExecutor(c: Command) {
|
protected defaultCommandExecutor(c: Command) {
|
||||||
this.gameObjects.sendCommand(c);
|
this.gameObjects.handleCommand(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lastGameState?: UpdateGameState;
|
private lastGameState?: UpdateGameState;
|
||||||
|
|
@ -248,7 +250,7 @@ export class Game extends CommandReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public aspectRatioChanged(aspectRatio: number) {
|
public aspectRatioChanged(aspectRatio: number) {
|
||||||
this.socketReceiver.sendCommand(new SetAspectRatioActionCommand(aspectRatio));
|
this.socketReceiver.handleCommand(new SetAspectRatioActionCommand(aspectRatio));
|
||||||
}
|
}
|
||||||
|
|
||||||
private isActive = true;
|
private isActive = true;
|
||||||
|
|
@ -284,9 +286,12 @@ export class Game extends CommandReceiver {
|
||||||
|
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
|
|
||||||
|
this.gameObjects.handleCommand(new StepCommand(deltaTime));
|
||||||
|
this.gameObjects.handleCommand(
|
||||||
|
new RenderCommand(this.renderer, this.overlay, shouldChangeLayout),
|
||||||
|
);
|
||||||
|
|
||||||
this.socketReceiver.sendQueuedCommands();
|
this.socketReceiver.sendQueuedCommands();
|
||||||
this.gameObjects.stepObjects(deltaTime);
|
|
||||||
this.gameObjects.drawObjects(this.renderer, this.overlay, shouldChangeLayout);
|
|
||||||
|
|
||||||
return this.isActive;
|
return this.isActive;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,24 @@
|
||||||
import { Renderer } from 'sdf-2d';
|
import { Renderer } from 'sdf-2d';
|
||||||
import {
|
import {
|
||||||
|
Command,
|
||||||
CommandExecutors,
|
CommandExecutors,
|
||||||
CommandReceiver,
|
CommandReceiver,
|
||||||
CreateObjectsCommand,
|
CreateObjectsCommand,
|
||||||
CreatePlayerCommand,
|
CreatePlayerCommand,
|
||||||
DeleteObjectsCommand,
|
DeleteObjectsCommand,
|
||||||
|
GameObject,
|
||||||
Id,
|
Id,
|
||||||
PropertyUpdatesForObjects,
|
PropertyUpdatesForObjects,
|
||||||
RemoteCallsForObjects,
|
RemoteCallsForObjects,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
|
import { BeforeDestroyCommand } from '../commands/types/before-destroy';
|
||||||
|
import { StepCommand } from '../commands/types/step';
|
||||||
import { Game } from '../game';
|
import { Game } from '../game';
|
||||||
import { Camera } from './camera';
|
import { Camera } from './types/camera';
|
||||||
import { CharacterView } from './character-view';
|
import { CharacterView } from './types/character-view';
|
||||||
import { ViewObject } from './view-object';
|
|
||||||
|
|
||||||
export class GameObjectContainer extends CommandReceiver {
|
export class GameObjectContainer extends CommandReceiver {
|
||||||
protected objects: Map<Id, ViewObject> = new Map();
|
protected objects: Map<Id, GameObject> = new Map();
|
||||||
public player!: CharacterView;
|
public player!: CharacterView;
|
||||||
public camera!: Camera;
|
public camera!: Camera;
|
||||||
|
|
||||||
|
|
@ -35,7 +38,15 @@ export class GameObjectContainer extends CommandReceiver {
|
||||||
},
|
},
|
||||||
|
|
||||||
[CreateObjectsCommand.type]: (c: CreateObjectsCommand) =>
|
[CreateObjectsCommand.type]: (c: CreateObjectsCommand) =>
|
||||||
c.objects.forEach((o) => this.addObject(o as ViewObject)),
|
c.objects.forEach((o) => this.addObject(o as GameObject)),
|
||||||
|
|
||||||
|
[StepCommand.type]: (c: StepCommand) => {
|
||||||
|
this.objects.forEach((o) => o.handleCommand(c));
|
||||||
|
|
||||||
|
if (this.player) {
|
||||||
|
this.camera.center = this.player.position;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
[RemoteCallsForObjects.type]: (c: RemoteCallsForObjects) =>
|
[RemoteCallsForObjects.type]: (c: RemoteCallsForObjects) =>
|
||||||
c.callsForObjects.forEach((c) =>
|
c.callsForObjects.forEach((c) =>
|
||||||
|
|
@ -43,7 +54,9 @@ export class GameObjectContainer extends CommandReceiver {
|
||||||
),
|
),
|
||||||
|
|
||||||
[PropertyUpdatesForObjects.type]: (c: PropertyUpdatesForObjects) =>
|
[PropertyUpdatesForObjects.type]: (c: PropertyUpdatesForObjects) =>
|
||||||
c.updates.forEach((c) => this.objects.get(c.id)?.updateProperties(c.updates)),
|
c.updates.forEach((u) =>
|
||||||
|
u.updates.forEach((au) => this.objects.get(u.id)?.handleCommand(au)),
|
||||||
|
),
|
||||||
|
|
||||||
[DeleteObjectsCommand.type]: (c: DeleteObjectsCommand) =>
|
[DeleteObjectsCommand.type]: (c: DeleteObjectsCommand) =>
|
||||||
c.ids.forEach((id: Id) => this.deleteObject(id)),
|
c.ids.forEach((id: Id) => this.deleteObject(id)),
|
||||||
|
|
@ -53,29 +66,17 @@ export class GameObjectContainer extends CommandReceiver {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public stepObjects(deltaTimeInSeconds: number) {
|
protected defaultCommandExecutor(c: Command) {
|
||||||
this.objects.forEach((o) => o.step(deltaTimeInSeconds));
|
this.objects.forEach((o) => o.handleCommand(c));
|
||||||
|
|
||||||
if (this.player) {
|
|
||||||
this.camera.center = this.player.position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public drawObjects(
|
private addObject(object: GameObject) {
|
||||||
renderer: Renderer,
|
|
||||||
overlay: HTMLElement,
|
|
||||||
shouldChangeLayout: boolean,
|
|
||||||
) {
|
|
||||||
this.objects.forEach((o) => o.draw(renderer, overlay, shouldChangeLayout));
|
|
||||||
}
|
|
||||||
|
|
||||||
private addObject(object: ViewObject) {
|
|
||||||
this.objects.set(object.id, object);
|
this.objects.set(object.id, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
private deleteObject(id: Id) {
|
private deleteObject(id: Id) {
|
||||||
const object = this.objects.get(id);
|
const object = this.objects.get(id);
|
||||||
object?.beforeDestroy();
|
object?.handleCommand(new BeforeDestroyCommand());
|
||||||
this.objects.delete(id);
|
this.objects.delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
|
||||||
import { CircleLight, Renderer } from 'sdf-2d';
|
|
||||||
import { CommandExecutors, Id, LampBase, UpdateProperty } from 'shared';
|
|
||||||
import { ViewObject } from './view-object';
|
|
||||||
|
|
||||||
export class LampView extends LampBase implements ViewObject {
|
|
||||||
private light: CircleLight;
|
|
||||||
|
|
||||||
constructor(id: Id, center: vec2, color: vec3, lightness: number) {
|
|
||||||
super(id, center, color, lightness);
|
|
||||||
this.light = new CircleLight(center, color, lightness);
|
|
||||||
}
|
|
||||||
|
|
||||||
public updateProperties(update: UpdateProperty[]): void {}
|
|
||||||
|
|
||||||
public step(deltaTimeInSeconds: number): void {}
|
|
||||||
|
|
||||||
public beforeDestroy(): void {}
|
|
||||||
|
|
||||||
public draw(
|
|
||||||
renderer: Renderer,
|
|
||||||
overlay: HTMLElement,
|
|
||||||
shouldChangeLayout: boolean,
|
|
||||||
): void {
|
|
||||||
renderer.addDrawable(this.light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
import { CircleLight, Renderer } from 'sdf-2d';
|
|
||||||
import { CharacterTeam, Id, ProjectileBase, settings, UpdateProperty } from 'shared';
|
|
||||||
import { Vec2Extrapolator } from '../helper/vec2-extrapolator';
|
|
||||||
import { ViewObject } from './view-object';
|
|
||||||
|
|
||||||
export class ProjectileView extends ProjectileBase implements ViewObject {
|
|
||||||
private light: CircleLight;
|
|
||||||
|
|
||||||
private centerExtrapolator: Vec2Extrapolator;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
id: Id,
|
|
||||||
center: vec2,
|
|
||||||
radius: number,
|
|
||||||
team: CharacterTeam,
|
|
||||||
strength: number,
|
|
||||||
) {
|
|
||||||
super(id, center, radius, team, strength);
|
|
||||||
this.light = new CircleLight(
|
|
||||||
center,
|
|
||||||
settings.paletteDim[settings.colorIndices[team]],
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
this.centerExtrapolator = new Vec2Extrapolator(center);
|
|
||||||
}
|
|
||||||
|
|
||||||
public updateProperties(update: UpdateProperty[]): void {
|
|
||||||
update.forEach((u) => {
|
|
||||||
this.centerExtrapolator.addFrame(u.propertyValue, u.rateOfChange);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public step(deltaTimeInSeconds: number): void {
|
|
||||||
super.step(deltaTimeInSeconds);
|
|
||||||
|
|
||||||
this.center = this.centerExtrapolator.getValue(deltaTimeInSeconds);
|
|
||||||
this.light.center = this.center;
|
|
||||||
this.light.intensity = (0.15 * this.strength) / settings.projectileMaxStrength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public beforeDestroy(): void {}
|
|
||||||
|
|
||||||
public draw(
|
|
||||||
renderer: Renderer,
|
|
||||||
overlay: HTMLElement,
|
|
||||||
shouldChangeLayout: boolean,
|
|
||||||
): void {
|
|
||||||
renderer.addDrawable(this.light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Renderer } from 'sdf-2d';
|
import {
|
||||||
import { calculateViewArea, GameObject, mixRgb, settings, UpdateProperty } from 'shared';
|
calculateViewArea,
|
||||||
|
CommandExecutors,
|
||||||
|
GameObject,
|
||||||
|
mixRgb,
|
||||||
|
settings,
|
||||||
|
} from 'shared';
|
||||||
|
import { RenderCommand } from '../../commands/types/render';
|
||||||
|
import { Game } from '../../game';
|
||||||
|
|
||||||
import { Game } from '../game';
|
export class Camera extends GameObject {
|
||||||
import { ViewObject } from './view-object';
|
|
||||||
|
|
||||||
export class Camera extends GameObject implements ViewObject {
|
|
||||||
public center: vec2 = vec2.create();
|
public center: vec2 = vec2.create();
|
||||||
private aspectRatio?: number;
|
private aspectRatio?: number;
|
||||||
|
|
||||||
|
|
@ -13,11 +17,11 @@ export class Camera extends GameObject implements ViewObject {
|
||||||
super(null);
|
super(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateProperties(update: UpdateProperty[]): void {}
|
protected commandExecutors: CommandExecutors = {
|
||||||
public step(deltaTimeInSeconds: number): void {}
|
[RenderCommand.type]: this.draw.bind(this),
|
||||||
public beforeDestroy(): void {}
|
};
|
||||||
|
|
||||||
public draw(renderer: Renderer) {
|
private draw({ renderer }: RenderCommand) {
|
||||||
const canvasAspectRatio = renderer.canvasSize.x / renderer.canvasSize.y;
|
const canvasAspectRatio = renderer.canvasSize.x / renderer.canvasSize.y;
|
||||||
if (canvasAspectRatio !== this.aspectRatio) {
|
if (canvasAspectRatio !== this.aspectRatio) {
|
||||||
this.aspectRatio = canvasAspectRatio;
|
this.aspectRatio = canvasAspectRatio;
|
||||||
|
|
@ -6,15 +6,18 @@ import {
|
||||||
CharacterBase,
|
CharacterBase,
|
||||||
CharacterTeam,
|
CharacterTeam,
|
||||||
settings,
|
settings,
|
||||||
UpdateProperty,
|
CommandExecutors,
|
||||||
|
UpdatePropertyCommand,
|
||||||
} from 'shared';
|
} from 'shared';
|
||||||
import { CircleExtrapolator } from '../helper/circle-extrapolator';
|
import { BeforeDestroyCommand } from '../../commands/types/before-destroy';
|
||||||
import { BlobShape } from '../shapes/blob-shape';
|
import { RenderCommand } from '../../commands/types/render';
|
||||||
import { SoundHandler, Sounds } from '../sound-handler';
|
import { StepCommand } from '../../commands/types/step';
|
||||||
import { VibrationHandler } from '../vibration-handler';
|
import { CircleExtrapolator } from '../../helper/circle-extrapolator';
|
||||||
import { ViewObject } from './view-object';
|
import { BlobShape } from '../../shapes/blob-shape';
|
||||||
|
import { SoundHandler, Sounds } from '../../sound-handler';
|
||||||
|
import { VibrationHandler } from '../../vibration-handler';
|
||||||
|
|
||||||
export class CharacterView extends CharacterBase implements ViewObject {
|
export class CharacterView extends CharacterBase {
|
||||||
private shape: BlobShape;
|
private shape: BlobShape;
|
||||||
private nameElement: HTMLElement = document.createElement('div');
|
private nameElement: HTMLElement = document.createElement('div');
|
||||||
private statsElement: HTMLElement = document.createElement('div');
|
private statsElement: HTMLElement = document.createElement('div');
|
||||||
|
|
@ -26,6 +29,13 @@ export class CharacterView extends CharacterBase implements ViewObject {
|
||||||
private rightFootExtrapolator: CircleExtrapolator;
|
private rightFootExtrapolator: CircleExtrapolator;
|
||||||
private headExtrapolator: CircleExtrapolator;
|
private headExtrapolator: CircleExtrapolator;
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[RenderCommand.type]: this.draw.bind(this),
|
||||||
|
[StepCommand.type]: this.step.bind(this),
|
||||||
|
[BeforeDestroyCommand.type]: this.beforeDestroy.bind(this),
|
||||||
|
[UpdatePropertyCommand.type]: this.updateProperty.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: Id,
|
id: Id,
|
||||||
name: string,
|
name: string,
|
||||||
|
|
@ -54,18 +64,20 @@ export class CharacterView extends CharacterBase implements ViewObject {
|
||||||
return this.head!.center;
|
return this.head!.center;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateProperties(update: Array<UpdateProperty>) {
|
private updateProperty({
|
||||||
update.forEach((u) => {
|
propertyKey,
|
||||||
if (u.propertyKey === 'head') {
|
propertyValue,
|
||||||
this.headExtrapolator.addFrame(u.propertyValue, u.rateOfChange);
|
rateOfChange,
|
||||||
|
}: UpdatePropertyCommand) {
|
||||||
|
if (propertyKey === 'head') {
|
||||||
|
this.headExtrapolator.addFrame(propertyValue, rateOfChange);
|
||||||
}
|
}
|
||||||
if (u.propertyKey === 'leftFoot') {
|
if (propertyKey === 'leftFoot') {
|
||||||
this.leftFootExtrapolator.addFrame(u.propertyValue, u.rateOfChange);
|
this.leftFootExtrapolator.addFrame(propertyValue, rateOfChange);
|
||||||
}
|
}
|
||||||
if (u.propertyKey === 'rightFoot') {
|
if (propertyKey === 'rightFoot') {
|
||||||
this.rightFootExtrapolator.addFrame(u.propertyValue, u.rateOfChange);
|
this.rightFootExtrapolator.addFrame(propertyValue, rateOfChange);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setHealth(health: number) {
|
public setHealth(health: number) {
|
||||||
|
|
@ -87,7 +99,7 @@ export class CharacterView extends CharacterBase implements ViewObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public step(deltaTimeInSeconds: number): void {
|
private step({ deltaTimeInSeconds }: StepCommand): void {
|
||||||
this.head! = this.headExtrapolator.getValue(deltaTimeInSeconds);
|
this.head! = this.headExtrapolator.getValue(deltaTimeInSeconds);
|
||||||
this.leftFoot! = this.leftFootExtrapolator.getValue(deltaTimeInSeconds);
|
this.leftFoot! = this.leftFootExtrapolator.getValue(deltaTimeInSeconds);
|
||||||
this.rightFoot! = this.rightFootExtrapolator.getValue(deltaTimeInSeconds);
|
this.rightFoot! = this.rightFootExtrapolator.getValue(deltaTimeInSeconds);
|
||||||
|
|
@ -97,15 +109,11 @@ export class CharacterView extends CharacterBase implements ViewObject {
|
||||||
SoundHandler.play(Sounds.shoot, (0.6 * strength) / settings.playerMaxStrength);
|
SoundHandler.play(Sounds.shoot, (0.6 * strength) / settings.playerMaxStrength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public beforeDestroy(): void {
|
private beforeDestroy(): void {
|
||||||
this.nameElement.parentElement?.removeChild(this.nameElement);
|
this.nameElement.parentElement?.removeChild(this.nameElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
public draw(
|
private draw({ renderer, overlay, shouldChangeLayout }: RenderCommand): void {
|
||||||
renderer: Renderer,
|
|
||||||
overlay: HTMLElement,
|
|
||||||
shouldChangeLayout: boolean,
|
|
||||||
): void {
|
|
||||||
if (shouldChangeLayout) {
|
if (shouldChangeLayout) {
|
||||||
if (!this.nameElement.parentElement) {
|
if (!this.nameElement.parentElement) {
|
||||||
overlay.appendChild(this.nameElement);
|
overlay.appendChild(this.nameElement);
|
||||||
21
frontend/src/scripts/objects/types/lamp-view.ts
Normal file
21
frontend/src/scripts/objects/types/lamp-view.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
import { CircleLight, Renderer } from 'sdf-2d';
|
||||||
|
import { CommandExecutors, Id, LampBase, UpdatePropertyCommand } from 'shared';
|
||||||
|
import { RenderCommand } from '../../commands/types/render';
|
||||||
|
|
||||||
|
export class LampView extends LampBase {
|
||||||
|
private light: CircleLight;
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[RenderCommand.type]: this.draw.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(id: Id, center: vec2, color: vec3, lightness: number) {
|
||||||
|
super(id, center, color, lightness);
|
||||||
|
this.light = new CircleLight(center, color, lightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw({ renderer }: RenderCommand): void {
|
||||||
|
renderer.addDrawable(this.light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Renderer } from 'sdf-2d';
|
import { Id, Random, PlanetBase, UpdatePropertyCommand, CommandExecutors } from 'shared';
|
||||||
import { Id, Random, PlanetBase, UpdateProperty } from 'shared';
|
import { BeforeDestroyCommand } from '../../commands/types/before-destroy';
|
||||||
import { PlanetShape } from '../shapes/planet-shape';
|
import { RenderCommand } from '../../commands/types/render';
|
||||||
import { ViewObject } from './view-object';
|
import { StepCommand } from '../../commands/types/step';
|
||||||
|
import { PlanetShape } from '../../shapes/planet-shape';
|
||||||
|
|
||||||
type FallingPoint = {
|
type FallingPoint = {
|
||||||
velocity: vec2;
|
velocity: vec2;
|
||||||
|
|
@ -12,10 +13,17 @@ type FallingPoint = {
|
||||||
timeToLive: number;
|
timeToLive: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PlanetView extends PlanetBase implements ViewObject {
|
export class PlanetView extends PlanetBase {
|
||||||
private shape: PlanetShape;
|
private shape: PlanetShape;
|
||||||
private ownershipProgress: HTMLElement;
|
private ownershipProgress: HTMLElement;
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[RenderCommand.type]: this.draw.bind(this),
|
||||||
|
[StepCommand.type]: this.step.bind(this),
|
||||||
|
[BeforeDestroyCommand.type]: this.beforeDestroy.bind(this),
|
||||||
|
[UpdatePropertyCommand.type]: this.updateProperty.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
constructor(id: Id, vertices: Array<vec2>, ownership: number) {
|
constructor(id: Id, vertices: Array<vec2>, ownership: number) {
|
||||||
super(id, vertices);
|
super(id, vertices);
|
||||||
this.shape = new PlanetShape(vertices, ownership);
|
this.shape = new PlanetShape(vertices, ownership);
|
||||||
|
|
@ -25,7 +33,7 @@ export class PlanetView extends PlanetBase implements ViewObject {
|
||||||
this.ownershipProgress.className = 'ownership';
|
this.ownershipProgress.className = 'ownership';
|
||||||
}
|
}
|
||||||
|
|
||||||
public step(deltaTimeInSeconds: number): void {
|
private step(deltaTimeInSeconds: number): void {
|
||||||
this.shape.randomOffset += deltaTimeInSeconds / 4;
|
this.shape.randomOffset += deltaTimeInSeconds / 4;
|
||||||
this.shape.colorMixQ = this.ownership;
|
this.shape.colorMixQ = this.ownership;
|
||||||
|
|
||||||
|
|
@ -53,24 +61,18 @@ export class PlanetView extends PlanetBase implements ViewObject {
|
||||||
this.lastGeneratedPoint = value;
|
this.lastGeneratedPoint = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public beforeDestroy(): void {
|
private beforeDestroy(): void {
|
||||||
this.ownershipProgress.parentElement?.removeChild(this.ownershipProgress);
|
this.ownershipProgress.parentElement?.removeChild(this.ownershipProgress);
|
||||||
this.generatedPointElements.forEach((p) =>
|
this.generatedPointElements.forEach((p) =>
|
||||||
p.element.parentElement?.removeChild(p.element),
|
p.element.parentElement?.removeChild(p.element),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateProperties(update: UpdateProperty[]): void {
|
private updateProperty({ propertyValue }: UpdatePropertyCommand): void {
|
||||||
update.forEach((u) => {
|
this.ownership = propertyValue;
|
||||||
this.ownership = u.propertyValue;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public draw(
|
private draw({ renderer, overlay, shouldChangeLayout }: RenderCommand): void {
|
||||||
renderer: Renderer,
|
|
||||||
overlay: HTMLElement,
|
|
||||||
shouldChangeLayout: boolean,
|
|
||||||
): void {
|
|
||||||
if (shouldChangeLayout) {
|
if (shouldChangeLayout) {
|
||||||
if (!this.ownershipProgress.parentElement) {
|
if (!this.ownershipProgress.parentElement) {
|
||||||
overlay.appendChild(this.ownershipProgress);
|
overlay.appendChild(this.ownershipProgress);
|
||||||
57
frontend/src/scripts/objects/types/projectile-view.ts
Normal file
57
frontend/src/scripts/objects/types/projectile-view.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { CircleLight, Renderer } from 'sdf-2d';
|
||||||
|
import {
|
||||||
|
CharacterTeam,
|
||||||
|
CommandExecutors,
|
||||||
|
Id,
|
||||||
|
ProjectileBase,
|
||||||
|
settings,
|
||||||
|
UpdatePropertyCommand,
|
||||||
|
} from 'shared';
|
||||||
|
import { RenderCommand } from '../../commands/types/render';
|
||||||
|
import { StepCommand } from '../../commands/types/step';
|
||||||
|
import { Vec2Extrapolator } from '../../helper/vec2-extrapolator';
|
||||||
|
|
||||||
|
export class ProjectileView extends ProjectileBase {
|
||||||
|
private light: CircleLight;
|
||||||
|
|
||||||
|
private centerExtrapolator: Vec2Extrapolator;
|
||||||
|
|
||||||
|
protected commandExecutors: CommandExecutors = {
|
||||||
|
[RenderCommand.type]: this.draw.bind(this),
|
||||||
|
[StepCommand.type]: this.handleStep.bind(this),
|
||||||
|
[UpdatePropertyCommand.type]: this.updateProperty.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
id: Id,
|
||||||
|
center: vec2,
|
||||||
|
radius: number,
|
||||||
|
team: CharacterTeam,
|
||||||
|
strength: number,
|
||||||
|
) {
|
||||||
|
super(id, center, radius, team, strength);
|
||||||
|
this.light = new CircleLight(
|
||||||
|
center,
|
||||||
|
settings.paletteDim[settings.colorIndices[team]],
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
this.centerExtrapolator = new Vec2Extrapolator(center);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateProperty({ propertyValue, rateOfChange }: UpdatePropertyCommand): void {
|
||||||
|
this.centerExtrapolator.addFrame(propertyValue, rateOfChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleStep({ deltaTimeInSeconds }: StepCommand): void {
|
||||||
|
this.step(deltaTimeInSeconds);
|
||||||
|
|
||||||
|
this.center = this.centerExtrapolator.getValue(deltaTimeInSeconds);
|
||||||
|
this.light.center = this.center;
|
||||||
|
this.light.intensity = (0.15 * this.strength) / settings.projectileMaxStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw({ renderer }: RenderCommand): void {
|
||||||
|
renderer.addDrawable(this.light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { Renderer } from 'sdf-2d';
|
|
||||||
import { GameObject, UpdateProperty } from 'shared';
|
|
||||||
|
|
||||||
export interface ViewObject extends GameObject {
|
|
||||||
step(deltaTimeInMilliseconds: number): void;
|
|
||||||
draw(renderer: Renderer, overlay: HTMLElement, shouldChangeLayout: boolean): void;
|
|
||||||
updateProperties(update: Array<UpdateProperty>): void;
|
|
||||||
beforeDestroy(): void;
|
|
||||||
}
|
|
||||||
|
|
@ -13,6 +13,6 @@ export abstract class CommandGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sendCommandToSubscribers(command: Command): void {
|
protected sendCommandToSubscribers(command: Command): void {
|
||||||
this.subscribers.forEach((s) => s.sendCommand(command));
|
this.subscribers.forEach((s) => s.handleCommand(command));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,9 @@ import { Command } from './command';
|
||||||
export abstract class CommandReceiver {
|
export abstract class CommandReceiver {
|
||||||
protected commandExecutors: CommandExecutors = {};
|
protected commandExecutors: CommandExecutors = {};
|
||||||
|
|
||||||
public reactsToCommand(commandType: string): boolean {
|
|
||||||
return Object.prototype.hasOwnProperty.call(this.commandExecutors, commandType);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected defaultCommandExecutor(_: Command) {}
|
protected defaultCommandExecutor(_: Command) {}
|
||||||
|
|
||||||
public sendCommand(command: Command) {
|
public handleCommand(command: Command) {
|
||||||
const commandType = command.type;
|
const commandType = command.type;
|
||||||
|
|
||||||
if (Object.prototype.hasOwnProperty.call(this.commandExecutors, commandType)) {
|
if (Object.prototype.hasOwnProperty.call(this.commandExecutors, commandType)) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Command } from '../commands/command';
|
||||||
|
import { CommandReceiver } from '../commands/command-receiver';
|
||||||
import { Id } from '../communication/id';
|
import { Id } from '../communication/id';
|
||||||
import { serializable } from '../serialization/serializable';
|
import { serializable } from '../serialization/serializable';
|
||||||
|
|
||||||
|
|
@ -11,12 +13,14 @@ export class RemoteCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
@serializable
|
@serializable
|
||||||
export class UpdateProperty {
|
export class UpdatePropertyCommand extends Command {
|
||||||
constructor(
|
constructor(
|
||||||
public readonly propertyKey: string,
|
public readonly propertyKey: string,
|
||||||
public readonly propertyValue: any,
|
public readonly propertyValue: any,
|
||||||
public readonly rateOfChange: any,
|
public readonly rateOfChange: any,
|
||||||
) {}
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
return [this.propertyKey, this.propertyValue, this.rateOfChange];
|
return [this.propertyKey, this.propertyValue, this.rateOfChange];
|
||||||
|
|
@ -25,17 +29,22 @@ export class UpdateProperty {
|
||||||
|
|
||||||
@serializable
|
@serializable
|
||||||
export class PropertyUpdatesForObject {
|
export class PropertyUpdatesForObject {
|
||||||
constructor(public readonly id: Id, public readonly updates: Array<UpdateProperty>) {}
|
constructor(
|
||||||
|
public readonly id: Id,
|
||||||
|
public readonly updates: Array<UpdatePropertyCommand>,
|
||||||
|
) {}
|
||||||
|
|
||||||
public toArray(): Array<any> {
|
public toArray(): Array<any> {
|
||||||
return [this.id, this.updates];
|
return [this.id, this.updates];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class GameObject {
|
export abstract class GameObject extends CommandReceiver {
|
||||||
private remoteCalls: Array<RemoteCall> = [];
|
private remoteCalls: Array<RemoteCall> = [];
|
||||||
|
|
||||||
constructor(public readonly id: Id) {}
|
constructor(public readonly id: Id) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public processRemoteCalls(remoteCalls: Array<RemoteCall>) {
|
public processRemoteCalls(remoteCalls: Array<RemoteCall>) {
|
||||||
remoteCalls.forEach((r) =>
|
remoteCalls.forEach((r) =>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { serializable } from '../../serialization/serializable';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { Id } from '../../communication/id';
|
import { Id } from '../../communication/id';
|
||||||
import { CharacterTeam } from './character-base';
|
import { CharacterTeam } from './character-base';
|
||||||
|
import { CommandExecutors } from '../../commands/command-executors';
|
||||||
|
|
||||||
@serializable
|
@serializable
|
||||||
export class ProjectileBase extends GameObject {
|
export class ProjectileBase extends GameObject {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue