import { type GameDefinition } from './gameDefinition' import { type GameEvent } from './gameEvent' import { type GameResult } from './gameResult' import { type GameSessionState } from './gameSessionState' import { EMPTY_GAME_PRESENTATION_STATE, type GamePresentationState } from '../presentation/presentationState' import { EMPTY_HUD_PRESENTATION_STATE, type HudPresentationState } from '../presentation/hudPresentationState' import { EMPTY_MAP_PRESENTATION_STATE, type MapPresentationState } from '../presentation/mapPresentationState' import { ClassicSequentialRule } from '../rules/classicSequentialRule' import { ScoreORule } from '../rules/scoreORule' import { type RulePlugin } from '../rules/rulePlugin' export class GameRuntime { definition: GameDefinition | null plugin: RulePlugin | null state: GameSessionState | null presentation: GamePresentationState mapPresentation: MapPresentationState hudPresentation: HudPresentationState lastResult: GameResult | null constructor() { this.definition = null this.plugin = null this.state = null this.presentation = EMPTY_GAME_PRESENTATION_STATE this.mapPresentation = EMPTY_MAP_PRESENTATION_STATE this.hudPresentation = EMPTY_HUD_PRESENTATION_STATE this.lastResult = null } clear(): void { this.definition = null this.plugin = null this.state = null this.presentation = EMPTY_GAME_PRESENTATION_STATE this.mapPresentation = EMPTY_MAP_PRESENTATION_STATE this.hudPresentation = EMPTY_HUD_PRESENTATION_STATE this.lastResult = null } loadDefinition(definition: GameDefinition): GameResult { this.definition = definition this.plugin = this.resolvePlugin(definition) this.state = this.plugin.initialize(definition) const result: GameResult = { nextState: this.state, presentation: this.plugin.buildPresentation(definition, this.state), effects: [], } this.presentation = result.presentation this.mapPresentation = result.presentation.map this.hudPresentation = result.presentation.hud this.lastResult = result return result } startSession(startAt = Date.now()): GameResult { return this.dispatch({ type: 'session_started', at: startAt }) } dispatch(event: GameEvent): GameResult { if (!this.definition || !this.plugin || !this.state) { const emptyState: GameSessionState = { status: 'idle', startedAt: null, endedAt: null, completedControlIds: [], skippedControlIds: [], currentTargetControlId: null, inRangeControlId: null, score: 0, guidanceState: 'searching', modeState: null, } const result: GameResult = { nextState: emptyState, presentation: EMPTY_GAME_PRESENTATION_STATE, effects: [], } this.lastResult = result this.presentation = result.presentation this.mapPresentation = result.presentation.map this.hudPresentation = result.presentation.hud return result } const result = this.plugin.reduce(this.definition, this.state, event) this.state = result.nextState this.presentation = result.presentation this.mapPresentation = result.presentation.map this.hudPresentation = result.presentation.hud this.lastResult = result return result } getPresentation(): GamePresentationState { return this.presentation } getMapPresentation(): MapPresentationState { return this.mapPresentation } getHudPresentation(): HudPresentationState { return this.hudPresentation } resolvePlugin(definition: GameDefinition): RulePlugin { if (definition.mode === 'classic-sequential') { return new ClassicSequentialRule() } if (definition.mode === 'score-o') { return new ScoreORule() } throw new Error(`未支持的玩法模式: ${definition.mode}`) } }