import EventEmitter from "node:events" import type { PlayerBroadcast } from "../shared/broadcast" import { script } from "../shared/script" import type { SceneDefinition } from "../shared/script/types" import { type SceneEvent, sceneTypesById } from "./scene-types" import type { Tagged } from "type-fest" import type { SessionId } from "./session" interface Events { "player-broadcast": [PlayerBroadcast] } export interface CurrentScene { id: string definition: SceneDefinition & { type: Type } state: ReturnType } export class Game { // @ts-expect-error private currentScene: CurrentScene public eventBus = new EventEmitter({ captureRejections: false }) constructor() { this.switchScene("pre-start") setInterval(() => { this.eventBus.emit("player-broadcast", { type: "keep-alive" }) }, 10000) } getConnectionPlayerBroadcasts(): PlayerBroadcast[] { const events: PlayerBroadcast[] = [] events.push({ type: "scene-changed", sceneId: this.currentScene.id }) events.push(...this.currentScene.state.getConnectionEvents().map((event: SceneEvent) => ({ type: "scene-event", event }))) return events } removePlayer(sessionId: SessionId) { this.currentScene.state.removePlayer(sessionId) } switchScene(sceneId: string) { const definition = script.scenesById.get(sceneId) if (definition === undefined) throw new Error(`Unknown scene: ${sceneId}`) const type = sceneTypesById[definition.type] this.eventBus.emit("player-broadcast", { type: "scene-changed", sceneId: sceneId }) this.currentScene = { id: sceneId, definition, state: type.createState(this, definition as any) } } withSceneState(type: Type, block: (state: ReturnType) => R): R | null { if (this.currentScene.definition.type === type) { return block(this.currentScene.state) } else { return null } } } export const game = new Game()