From 8ec7f5368232d59f344e1067e1bad5e48dbcb7ae Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sat, 7 Sep 2024 20:20:07 -0700 Subject: get "cats" up there --- static/src/engine/game.ts | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 static/src/engine/game.ts (limited to 'static/src/engine/game.ts') diff --git a/static/src/engine/game.ts b/static/src/engine/game.ts new file mode 100644 index 0000000..64f63b5 --- /dev/null +++ b/static/src/engine/game.ts @@ -0,0 +1,112 @@ +import { System, SystemType } from "./system"; +import { Entity } from "./entity"; +import { ComponentType } from "./component"; + +export class Game { + private running: boolean; + private last_update: number; + + private readonly entities: Map = new Map(); + private readonly component_entities: Map> = + new Map(); + private readonly systems: Map = new Map(); + private readonly system_order: SystemType[]; + + constructor( + public readonly client_id: string, + systems: System[], + ) { + this.last_update = performance.now(); + this.running = false; + + systems.forEach((system) => this.systems.set(system.system_type, system)); + this.system_order = systems.map(({ system_type }) => system_type); + } + + public start() { + if (this.running) return; + + console.log("starting game"); + this.running = true; + this.last_update = performance.now(); + + const game_loop = (timestamp: number) => { + if (!this.running) return; + + // rebuild component -> { entity } map + this.component_entities.clear(); + Array.from(this.entities.values()).forEach((entity) => + Object.values(entity.components).forEach((component) => { + const set = + this.component_entities.get(component.name) ?? new Set(); + set.add(entity.id); + this.component_entities.set(component.name, set); + }), + ); + + const dt = timestamp - this.last_update; + + this.system_order.forEach((system_type) => + this.systems.get(system_type)!.update(dt, this), + ); + + this.last_update = timestamp; + requestAnimationFrame(game_loop); // tail call recursion! /s + }; + requestAnimationFrame(game_loop); + } + + public stop() { + if (!this.running) return; + this.running = false; + } + + public for_each_entity_with_component( + component: ComponentType, + callback: (entity: Entity) => void, + ) { + this.component_entities.get(component)?.forEach((entity_id) => { + const entity = this.entities.get(entity_id); + if (!entity) return; + + callback(entity); + }); + } + + public get_entity(id: string) { + return this.entities.get(id); + } + + public put_entity(entity: Entity) { + const old_entity = this.entities.get(entity.id); + if (old_entity) this.clear_entity_components(old_entity); + + Object.values(entity.components).forEach((component) => { + const set = + this.component_entities.get(component.name) ?? new Set(); + set.add(entity.id); + this.component_entities.set(component.name, set); + }); + this.entities.set(entity.id, entity); + } + + public remove_entity(id: string) { + const entity = this.entities.get(id); + if (typeof entity === "undefined") return; + + this.clear_entity_components(entity); + this.entities.delete(id); + } + + private clear_entity_components(entity: Entity) { + Object.values(entity.components).forEach((component) => { + const set = this.component_entities.get(component.name); + if (typeof set === "undefined") return; + set.delete(entity.id); + }); + } + + public get_system(system_type: SystemType): T | undefined { + return this.systems.get(system_type) as T; + } +} -- cgit v1.3