level-up/backend/game.ts
Moritz Ruth 3498bbb8ad
All checks were successful
Build / build (push) Successful in 1m20s
Add virtual curtain
2025-04-14 17:37:03 +02:00

76 lines
No EOL
2.6 KiB
TypeScript

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<Type extends keyof typeof sceneTypesById> {
id: string
definition: SceneDefinition & { type: Type }
state: ReturnType<typeof sceneTypesById[Type]["createState"]>
}
export class Game {
// @ts-expect-error
private currentScene: CurrentScene
public eventBus = new EventEmitter<Events>({ captureRejections: false })
private isVirtualCurtainOpen = 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 })))
events.push({ type: "virtual-curtain-changed", isOpen: this.isVirtualCurtainOpen })
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 extends keyof typeof sceneTypesById, R>(type: Type, block: (state: ReturnType<typeof sceneTypesById[Type]["createState"]>) => R): R | null {
if (this.currentScene.definition.type === type) {
return block(this.currentScene.state)
} else {
return null
}
}
setVirtualCurtainOpen(isOpen: boolean) {
if (this.isVirtualCurtainOpen === isOpen) return
this.isVirtualCurtainOpen = isOpen
this.eventBus.emit("player-broadcast", { type: "virtual-curtain-changed", isOpen: this.isVirtualCurtainOpen })
}
}
export const game = new Game()