151 lines
4.9 KiB
TypeScript
151 lines
4.9 KiB
TypeScript
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
|
|
}
|
|
|
|
restoreDefinition(definition: GameDefinition, state: GameSessionState): GameResult {
|
|
this.definition = definition
|
|
this.plugin = this.resolvePlugin(definition)
|
|
this.state = {
|
|
status: state.status,
|
|
endReason: state.endReason,
|
|
startedAt: state.startedAt,
|
|
endedAt: state.endedAt,
|
|
completedControlIds: state.completedControlIds.slice(),
|
|
skippedControlIds: state.skippedControlIds.slice(),
|
|
currentTargetControlId: state.currentTargetControlId,
|
|
inRangeControlId: state.inRangeControlId,
|
|
score: state.score,
|
|
guidanceState: state.guidanceState,
|
|
modeState: state.modeState
|
|
? JSON.parse(JSON.stringify(state.modeState)) as Record<string, unknown>
|
|
: null,
|
|
}
|
|
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',
|
|
endReason: null,
|
|
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}`)
|
|
}
|
|
}
|