Fix issues

This commit is contained in:
schmelczerandras 2020-11-05 19:27:27 +01:00
parent 57d7009342
commit 42e4de28b5
15 changed files with 175 additions and 145 deletions

View file

@ -2,6 +2,7 @@
"cSpell.words": [ "cSpell.words": [
"Deserializable", "Deserializable",
"Deserialization", "Deserialization",
"Respawn",
"decla", "decla",
"overridable", "overridable",
"serializable" "serializable"

View file

@ -1,6 +1,6 @@
{ {
"name": "declared-server", "name": "declared-server",
"version": "0.0.12", "version": "0.0.13",
"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/)",

View file

@ -4,8 +4,8 @@ export const defaultOptions: Options = {
port: 3000, port: 3000,
name: 'Test server', name: 'Test server',
playerLimit: 16, playerLimit: 16,
npcCount: 16, npcCount: 12,
seed: Math.random(), seed: Math.random(),
scoreLimit: 500, scoreLimit: 1000,
worldSize: 8000, worldSize: 8000,
}; };

View file

@ -115,11 +115,12 @@ export class GameServer {
private endGame(winningTeam: CharacterTeam) { private endGame(winningTeam: CharacterTeam) {
this.isInEndGame = true; this.isInEndGame = true;
const endTitleLength = 6000; const endTitleLength = 6;
this.players.endGame(winningTeam);
this.players.queueCommandForEachClient( this.players.queueCommandForEachClient(
new GameEndCommand(winningTeam, endTitleLength / 1000, true), new GameEndCommand(winningTeam, endTitleLength, true),
); );
setTimeout(() => this.destroy(), endTitleLength * 1.1); setTimeout(() => this.destroy(), endTitleLength * 1000 * 1.1);
} }
private destroy() { private destroy() {
@ -147,15 +148,17 @@ export class GameServer {
); );
} }
let scaledDelta = delta;
if (this.isInEndGame) { if (this.isInEndGame) {
this.timeScaling *= Math.pow(settings.endGameDeltaScaling, delta); this.timeScaling *= Math.pow(settings.endGameDeltaScaling, delta);
delta /= this.timeScaling; scaledDelta /= this.timeScaling;
} else { } else {
this.updatePoints(); this.updatePoints();
} }
this.objects.stepObjects(delta); this.objects.stepObjects(scaledDelta);
this.players.step(delta); this.players.step(scaledDelta);
this.players.stepCommunication(delta);
this.objects.resetRemoteCalls(); this.objects.resetRemoteCalls();
this.deltaTimes.push(this.deltaTimeCalculator.getNextDeltaTimeInSeconds()); this.deltaTimes.push(this.deltaTimeCalculator.getNextDeltaTimeInSeconds());
@ -165,7 +168,7 @@ export class GameServer {
} }
private handleStats() { private handleStats() {
const framesBetweenDeltaTimeCalculation = 1000; const framesBetweenDeltaTimeCalculation = 10000;
if (this.deltaTimes.length > framesBetweenDeltaTimeCalculation) { if (this.deltaTimes.length > framesBetweenDeltaTimeCalculation) {
this.deltaTimes.sort((a, b) => a - b); this.deltaTimes.sort((a, b) => a - b);

View file

@ -8,6 +8,8 @@ import {
settings, settings,
PlanetBase, PlanetBase,
CharacterTeam, CharacterTeam,
PropertyUpdatesForObject,
UpdateProperty,
} from 'shared'; } from 'shared';
import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box'; import { ImmutableBoundingBox } from '../physics/bounding-boxes/immutable-bounding-box';
@ -102,12 +104,16 @@ export class PlanetPhysical
public step(deltaTime: number): void { public step(deltaTime: number): void {
this.timeSinceLastPointGeneration += deltaTime; this.timeSinceLastPointGeneration += deltaTime;
// In reverse order, so that teams can achieve a 100% control. // In reverse order, so that teams can achieve a 100% control.
this.remoteCall('setOwnership', this.ownership);
this.takeControl(CharacterTeam.neutral, deltaTime); this.takeControl(CharacterTeam.neutral, deltaTime);
} }
public getPropertyUpdates(): PropertyUpdatesForObject {
return new PropertyUpdatesForObject(this.id, [
new UpdateProperty('ownership', this.ownership, 0),
]);
}
public takeControl(team: CharacterTeam, deltaTime: number) { public takeControl(team: CharacterTeam, deltaTime: number) {
if (team === CharacterTeam.decla) { if (team === CharacterTeam.decla) {
this.ownership -= (0.5 / settings.takeControlTimeInSeconds) * deltaTime; this.ownership -= (0.5 / settings.takeControlTimeInSeconds) * deltaTime;

View file

@ -22,7 +22,7 @@ export class PlayerContainer {
this.npcMaxCount - this._npcs.length, this.npcMaxCount - this._npcs.length,
); );
for (let i = 0; i < newNpcCount; i++) { for (let i = 0; i < newNpcCount; i++) {
const name = `BOT ${Random.choose(settings.npcNames)}`; const name = `🤖 ${Random.choose(settings.npcNames)}`;
this._npcs.push( this._npcs.push(
new NPC({ name }, this, this.objects, this.getTeamOfNextPlayer(true)), new NPC({ name }, this, this.objects, this.getTeamOfNextPlayer(true)),
); );
@ -60,6 +60,14 @@ export class PlayerContainer {
this.players.forEach((p) => p.step(deltaTimeInSeconds)); this.players.forEach((p) => p.step(deltaTimeInSeconds));
} }
public stepCommunication(deltaTimeInSeconds: number) {
this._players.forEach((p) => p.stepCommunications(deltaTimeInSeconds));
}
public endGame(winner: CharacterTeam) {
this._players.forEach((p) => p.onGameEnded(winner));
}
public queueCommandForEachClient(command: Command) { public queueCommandForEachClient(command: Command) {
this._players.forEach((p) => p.queueCommandSend(command)); this._players.forEach((p) => p.queueCommandSend(command));
} }

View file

@ -32,7 +32,8 @@ import { PlayerBase } from './player-base';
export class Player extends PlayerBase { export class Player extends PlayerBase {
// default, until the clients sends its real value // default, until the clients sends its real value
private aspectRatio: number = 16 / 9; private aspectRatio: number = 16 / 9;
private timeUntilRespawn = 0;
private timeSinceLastMessage = 0;
private objectsPreviouslyInViewArea: Array<GameObject> = []; private objectsPreviouslyInViewArea: Array<GameObject> = [];
protected commandExecutors: CommandExecutors = { protected commandExecutors: CommandExecutors = {
@ -68,12 +69,12 @@ export class Player extends PlayerBase {
this.queueCommandSend(new CreatePlayerCommand(this.character!)); this.queueCommandSend(new CreatePlayerCommand(this.character!));
} }
private timeSinceLastMessage = 0; private winnerTeam?: CharacterTeam;
private messageInterval = 1 / 30; public onGameEnded(winnerTeam: CharacterTeam) {
private timeUntilRespawn = 0; this.winnerTeam = winnerTeam;
public step(deltaTimeInSeconds: number) { }
this.timeSinceLastMessage += deltaTimeInSeconds;
public step(deltaTimeInSeconds: number) {
if (this.character) { if (this.character) {
this.center = this.character?.center; this.center = this.character?.center;
@ -85,20 +86,22 @@ export class Player extends PlayerBase {
this.timeUntilRespawn = settings.playerDiedTimeout; this.timeUntilRespawn = settings.playerDiedTimeout;
} }
} else { } else {
if (this.timeSinceLastMessage > this.messageInterval) {
this.queueCommandSend(
new ServerAnnouncement(`Reviving in ${Math.round(this.timeUntilRespawn)}`),
);
}
if ((this.timeUntilRespawn -= deltaTimeInSeconds) < 0) { if ((this.timeUntilRespawn -= deltaTimeInSeconds) < 0) {
this.createCharacter(); this.createCharacter();
this.center = this.character!.center; this.center = this.character!.center;
this.queueCommandSend(new ServerAnnouncement(''));
} }
} }
if (this.timeSinceLastMessage > this.messageInterval) { const remoteCalls = this.objectsPreviouslyInViewArea
.map((g) => new RemoteCallsForObject(g.id, g.getRemoteCalls()))
.filter((c) => c.calls.length > 0);
if (remoteCalls.length > 0) {
this.queueCommandSend(new RemoteCallsForObjects(remoteCalls));
}
}
private handleViewAreaUpdate() {
const viewArea = calculateViewArea(this.center, this.aspectRatio, 1.2); const viewArea = calculateViewArea(this.center, this.aspectRatio, 1.2);
const bb = new BoundingBox(); const bb = new BoundingBox();
bb.topLeft = viewArea.topLeft; bb.topLeft = viewArea.topLeft;
@ -139,20 +142,6 @@ export class Player extends PlayerBase {
); );
} }
this.queueCommandSend(
new RemoteCallsForObjects(
this.objectsPreviouslyInViewArea.map(
(g) => new RemoteCallsForObject(g.id, g.getRemoteCalls()),
),
),
);
if (this.timeSinceLastMessage > this.messageInterval) {
this.sendQueuedCommandsToClient();
this.timeSinceLastMessage = 0;
}
}
private getOtherPlayers(): Array<OtherPlayerDirection> { private getOtherPlayers(): Array<OtherPlayerDirection> {
if (!this.character) { if (!this.character) {
return []; return [];
@ -190,11 +179,33 @@ export class Player extends PlayerBase {
this.commandsToBeSent.push(command); this.commandsToBeSent.push(command);
} }
public stepCommunications(deltaTime: number) {
if ((this.timeSinceLastMessage += deltaTime) > settings.updateMessageInterval) {
this.handleAnnouncements();
this.handleViewAreaUpdate();
this.sendQueuedCommandsToClient();
this.timeSinceLastMessage = 0;
}
}
public sendQueuedCommandsToClient() { public sendQueuedCommandsToClient() {
this.socket.emit(TransportEvents.ServerToPlayer, serialize(this.commandsToBeSent)); this.socket.emit(TransportEvents.ServerToPlayer, serialize(this.commandsToBeSent));
this.commandsToBeSent = []; this.commandsToBeSent = [];
} }
private handleAnnouncements() {
let announcement = '';
if (this.winnerTeam) {
announcement = `Team <span class="${this.winnerTeam}">${this.winnerTeam}</span> won 🎉`;
} else if (!this.character) {
announcement = `Reviving in ${Math.round(this.timeUntilRespawn)}`;
}
if (announcement) {
this.queueCommandSend(new ServerAnnouncement(announcement));
}
}
public destroy() { public destroy() {
super.destroy(); super.destroy();
} }

View file

@ -81,6 +81,7 @@ export class Game extends CommandReceiver {
this.lastAnnouncementText = ''; this.lastAnnouncementText = '';
this.overlay.appendChild(this.progressBar); this.overlay.appendChild(this.progressBar);
this.announcementText.innerText = ''; this.announcementText.innerText = '';
this.timeScaling = 1;
this.overlay.appendChild(this.announcementText); this.overlay.appendChild(this.announcementText);
this.socket = io(this.playerDecision.server, { this.socket = io(this.playerDecision.server, {
@ -128,16 +129,16 @@ export class Game extends CommandReceiver {
private lastGameState?: UpdateGameState; private lastGameState?: UpdateGameState;
private isEnding = false; private isEnding = false;
private timeScaling = 1;
private lastAnnouncementText = ''; private lastAnnouncementText = '';
protected commandExecutors: CommandExecutors = { protected commandExecutors: CommandExecutors = {
[ServerAnnouncement.type]: (c: ServerAnnouncement) => [ServerAnnouncement.type]: (c: ServerAnnouncement) => {
(this.lastAnnouncementText = c.text), this.lastAnnouncementText = c.text;
[UpdateGameState.type]: (c: UpdateGameState) => (this.lastGameState = c), this.timeSinceLastAnnouncement = 0;
[GameEndCommand.type]: (c: GameEndCommand) => {
const team = `<span class="${c.winningTeam}">${c.winningTeam}</span>`;
this.lastAnnouncementText = `Team ${team} won 🎉`;
this.isEnding = true;
}, },
[UpdateGameState.type]: (c: UpdateGameState) => (this.lastGameState = c),
[GameEndCommand.type]: () => (this.isEnding = true),
[UpdateOtherPlayerDirections.type]: (c: UpdateOtherPlayerDirections) => [UpdateOtherPlayerDirections.type]: (c: UpdateOtherPlayerDirections) =>
(this.lastOtherPlayerDirections = c), (this.lastOtherPlayerDirections = c),
[GameStartCommand.type]: this.initialize.bind(this), [GameStartCommand.type]: this.initialize.bind(this),
@ -255,6 +256,7 @@ export class Game extends CommandReceiver {
this.isActive = false; this.isActive = false;
} }
private timeSinceLastAnnouncement = 0;
private framesSinceLastLayoutUpdate = 0; private framesSinceLastLayoutUpdate = 0;
private gameLoop( private gameLoop(
renderer: Renderer, renderer: Renderer,
@ -271,10 +273,19 @@ export class Game extends CommandReceiver {
this.draw(); this.draw();
} }
if ((this.timeSinceLastAnnouncement += deltaTime) > 0.5) {
this.lastAnnouncementText = '';
}
if (this.isEnding) {
this.timeScaling *= Math.pow(settings.endGameDeltaScaling, deltaTime);
deltaTime /= this.timeScaling;
}
this.renderer = renderer; this.renderer = renderer;
this.socketReceiver.sendQueuedCommands(); this.socketReceiver.sendQueuedCommands();
this.gameObjects.stepObjects(this.isEnding ? 0 : deltaTime); this.gameObjects.stepObjects(deltaTime);
this.gameObjects.drawObjects(this.renderer, this.overlay, shouldChangeLayout); this.gameObjects.drawObjects(this.renderer, this.overlay, shouldChangeLayout);
return this.isActive; return this.isActive;

View file

@ -15,19 +15,20 @@ export class JoinFormHandler {
private waitingForDecision: Promise<PlayerDecision>; private waitingForDecision: Promise<PlayerDecision>;
private resolvePlayerDecision!: (d: PlayerDecision) => void; private resolvePlayerDecision!: (d: PlayerDecision) => void;
private pollServersTimer: any; private pollServersTimer: any;
private keyUpListener = (e: KeyboardEvent) => {
if (e.key === 'enter') {
this.form.submit();
}
};
constructor(form: HTMLFormElement, private readonly container: HTMLElement) { constructor(private form: HTMLFormElement, private readonly container: HTMLElement) {
this.joinButton = form.querySelector('button[type="submit"]') as HTMLButtonElement; this.joinButton = form.querySelector('button[type="submit"]') as HTMLButtonElement;
this.joinButton.disabled = true; this.joinButton.disabled = true;
this.waitingForDecision = new Promise((r) => (this.resolvePlayerDecision = r)); this.waitingForDecision = new Promise((r) => (this.resolvePlayerDecision = r));
new FormData(form); new FormData(form);
document.addEventListener('keyup', (e) => { addEventListener('keyup', this.keyUpListener);
if (e.key === 'enter') {
form.submit();
}
});
form.onsubmit = (e) => { form.onsubmit = (e) => {
SoundHandler.play(Sounds.click); SoundHandler.play(Sounds.click);
@ -54,6 +55,7 @@ export class JoinFormHandler {
} }
private destroy() { private destroy() {
removeEventListener('keyup', this.keyUpListener);
clearInterval(this.pollServersTimer); clearInterval(this.pollServersTimer);
this.servers.forEach((s) => s.destroy()); this.servers.forEach((s) => s.destroy());
} }

View file

@ -7,7 +7,6 @@ import { ViewObject } from './view-object';
export class Camera extends GameObject implements ViewObject { export class Camera extends GameObject implements ViewObject {
public center: vec2 = vec2.create(); public center: vec2 = vec2.create();
private aspectRatio?: number; private aspectRatio?: number;
constructor(private game: Game) { constructor(private game: Game) {
@ -15,12 +14,10 @@ export class Camera extends GameObject implements ViewObject {
} }
public updateProperties(update: UpdateProperty[]): void {} public updateProperties(update: UpdateProperty[]): void {}
public step(deltaTimeInSeconds: number): void {}
public beforeDestroy(): void {} public beforeDestroy(): void {}
public step(deltaTimeInSeconds: number): void {} public draw(renderer: Renderer) {
public draw(renderer: Renderer, overlay: HTMLElement, shouldChangeLayout: boolean) {
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;

View file

@ -14,19 +14,17 @@ type FallingPoint = {
export class PlanetView extends PlanetBase implements ViewObject { export class PlanetView extends PlanetBase implements ViewObject {
private shape: PlanetShape; private shape: PlanetShape;
private ownershipProgess: HTMLElement; private ownershipProgress: HTMLElement;
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);
(this.shape as any).randomOffset = Random.getRandom(); (this.shape as any).randomOffset = Random.getRandom();
this.ownershipProgess = document.createElement('div'); this.ownershipProgress = document.createElement('div');
this.ownershipProgess.className = 'ownership'; this.ownershipProgress.className = 'ownership';
} }
public updateProperties(update: UpdateProperty[]): void {}
public step(deltaTimeInSeconds: number): void { public step(deltaTimeInSeconds: number): void {
this.shape.randomOffset += deltaTimeInSeconds / 4; this.shape.randomOffset += deltaTimeInSeconds / 4;
this.shape.colorMixQ = this.ownership; this.shape.colorMixQ = this.ownership;
@ -56,20 +54,26 @@ export class PlanetView extends PlanetBase implements ViewObject {
} }
public beforeDestroy(): void { public beforeDestroy(): void {
this.ownershipProgess.parentElement?.removeChild(this.ownershipProgess); 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 {
update.forEach((u) => {
this.ownership = u.propertyValue;
});
}
public draw( public draw(
renderer: Renderer, renderer: Renderer,
overlay: HTMLElement, overlay: HTMLElement,
shouldChangeLayout: boolean, shouldChangeLayout: boolean,
): void { ): void {
if (shouldChangeLayout) { if (shouldChangeLayout) {
if (!this.ownershipProgess.parentElement) { if (!this.ownershipProgress.parentElement) {
overlay.appendChild(this.ownershipProgess); overlay.appendChild(this.ownershipProgress);
} }
const screenPosition = renderer.worldToDisplayCoordinates(this.center); const screenPosition = renderer.worldToDisplayCoordinates(this.center);
@ -94,8 +98,8 @@ export class PlanetView extends PlanetBase implements ViewObject {
(p) => p.timeToLive > 0, (p) => p.timeToLive > 0,
); );
this.ownershipProgess.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%)`; this.ownershipProgress.style.transform = `translateX(${screenPosition.x}px) translateY(${screenPosition.y}px) translateX(-50%) translateY(-50%)`;
this.ownershipProgess.style.background = this.getGradient(); this.ownershipProgress.style.background = this.getGradient();
if (this.lastGeneratedPoint !== undefined) { if (this.lastGeneratedPoint !== undefined) {
const element = document.createElement('div'); const element = document.createElement('div');

View file

@ -4,10 +4,11 @@ const TsConfigWebpackPlugin = require('ts-config-webpack-plugin');
const HtmlWebpackInlineSVGPlugin = require('html-webpack-inline-svg-plugin'); const HtmlWebpackInlineSVGPlugin = require('html-webpack-inline-svg-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserJSPlugin = require('terser-webpack-plugin'); const TerserJSPlugin = require('terser-webpack-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin'); const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const Sass = require('sass'); const Sass = require('sass');
module.exports = { module.exports = (env, argv) => ({
devServer: { devServer: {
host: '0.0.0.0', host: '0.0.0.0',
disableHostCheck: true, disableHostCheck: true,
@ -18,9 +19,10 @@ module.exports = {
plugins: [ plugins: [
new CleanWebpackPlugin(), new CleanWebpackPlugin(),
new MiniCssExtractPlugin(), new MiniCssExtractPlugin(),
//new BundleAnalyzerPlugin(),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: './src/index.html', template: './src/index.html',
//inlineSource: '.(css)$', inlineSource: argv.mode === 'development' ? '' : '.(css)$',
}), }),
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin), new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
new HtmlWebpackInlineSVGPlugin({ new HtmlWebpackInlineSVGPlugin({
@ -31,7 +33,6 @@ module.exports = {
}, },
], ],
}), }),
new TsConfigWebpackPlugin(), new TsConfigWebpackPlugin(),
], ],
optimization: { optimization: {
@ -106,4 +107,4 @@ module.exports = {
}, },
], ],
}, },
}; });

View file

@ -1,4 +1,3 @@
import { Command } from '../commands/command';
import { Id } from '../communication/id'; import { Id } from '../communication/id';
import { serializable } from '../serialization/serializable'; import { serializable } from '../serialization/serializable';

View file

@ -19,10 +19,6 @@ export class PlanetBase extends GameObject {
vec2.scale(this.center, this.center, 1 / vertices.length); vec2.scale(this.center, this.center, 1 / vertices.length);
} }
public setOwnership(value: number) {
this.ownership = value;
}
public generatedPoints(value: number) {} public generatedPoints(value: number) {}
public static createPlanetVertices( public static createPlanetVertices(

View file

@ -12,12 +12,11 @@ const redPlanetColor = redColorDim;
export const settings = { export const settings = {
lightCutoffDistance: 600, lightCutoffDistance: 600,
maxVelocityX: 1000,
maxVelocityY: 1000,
radiusSteps: 500, radiusSteps: 500,
spawnDespawnTime: 0.7, spawnDespawnTime: 0.7,
worldRadius: 10000, worldRadius: 10000,
objectsOnCircleLength: 0.002, objectsOnCircleLength: 0.002,
updateMessageInterval: 1 / 25,
planetEdgeCount: 7, planetEdgeCount: 7,
playerKillPoint: 10, playerKillPoint: 10,
takeControlTimeInSeconds: 4, takeControlTimeInSeconds: 4,
@ -29,9 +28,9 @@ export const settings = {
planetControlThreshold: 0.2, planetControlThreshold: 0.2,
playerMaxHealth: 100, playerMaxHealth: 100,
maxGravityStrength: 10000, maxGravityStrength: 10000,
maxAcceleration: 60000, maxAcceleration: 120000,
playerMaxStrength: 80, playerMaxStrength: 80,
endGameDeltaScaling: 4, endGameDeltaScaling: 2,
playerDiedTimeout: 5, playerDiedTimeout: 5,
playerStrengthRegenerationPerSeconds: 80, playerStrengthRegenerationPerSeconds: 80,
projectileMaxStrength: 40, projectileMaxStrength: 40,
@ -47,58 +46,50 @@ export const settings = {
npcNames: [ npcNames: [
'Adam', 'Adam',
'Andrew', 'Andrew',
'Blaise',
'Clarence', 'Clarence',
'Dean',
'Dustin',
'Elliot', 'Elliot',
'Elmer',
'Ernie', 'Ernie',
'Eugene', 'Ethan',
'Fergus',
'Ferris',
'Frank', 'Frank',
'Frasier',
'Fred', 'Fred',
'George', 'George',
'Graham', 'Graham',
'Harold',
'Harvey', 'Harvey',
'Henry',
'Mingan',
'Irving',
'Irwin', 'Irwin',
'Jason',
'Jenssen',
'Josh',
'Ladislaus',
'Larry', 'Larry',
'Lester', 'Lester',
'Martin',
'Marvin', 'Marvin',
'Neil', 'Neil',
'Nick',
'Niles', 'Niles',
'Norm',
'Oliver', 'Oliver',
'Blaise', 'Orin',
'Opie', 'Pat',
'Perry',
'Ron',
'Ryan', 'Ryan',
'Sisyphus',
'Tim',
'Toby', 'Toby',
'Ulric',
'Ulysses', 'Ulysses',
'Uri', 'Uri',
'Waldo', 'Waldo',
'Wally', 'Wally',
'Walt', 'Walt',
'Wesley', 'Wesley',
'Yanni',
'Yogi',
'Yuri',
'Dean',
'Dustin',
'Ethan',
'Harold',
'Henry',
'Irving',
'Jason',
'Jenssen',
'Josh',
'Martin',
'Nick',
'Norm',
'Orin',
'Pat',
'Perry',
'Ron',
'Shawn',
'Tim',
'Will', 'Will',
'Wyatt', 'Wyatt',
], ],
@ -111,6 +102,6 @@ export const settings = {
}, },
palette: [declaColor, neutralColor, redColor], palette: [declaColor, neutralColor, redColor],
paletteDim: [declaColorDim, neutralColor, redColorDim], paletteDim: [declaColorDim, neutralColor, redColorDim],
targetPhysicsDeltaTimeInSeconds: 1 / 100, targetPhysicsDeltaTimeInSeconds: 1 / 200,
inViewAreaSize: 1920 * 1080 * 4, inViewAreaSize: 1920 * 1080 * 4,
}; };