Add score-o mode and split game map HUD presentation

This commit is contained in:
2026-03-24 12:27:45 +08:00
parent a117a25824
commit 0295893b56
18 changed files with 1121 additions and 113 deletions

View File

@@ -1,7 +1,7 @@
import { type LonLatPoint } from '../../utils/projection'
import { type GameAudioConfig } from '../audio/audioConfig'
export type GameMode = 'classic-sequential'
export type GameMode = 'classic-sequential' | 'score-o'
export type GameControlKind = 'start' | 'control' | 'finish'
export type PunchPolicyType = 'enter' | 'enter-confirm'

View File

@@ -2,4 +2,5 @@ export type GameEvent =
| { type: 'session_started'; at: number }
| { type: 'gps_updated'; at: number; lon: number; lat: number; accuracyMeters: number | null }
| { type: 'punch_requested'; at: number }
| { type: 'control_focused'; at: number; controlId: string | null }
| { type: 'session_ended'; at: number }

View File

@@ -3,7 +3,10 @@ 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 {
@@ -11,6 +14,8 @@ export class GameRuntime {
plugin: RulePlugin | null
state: GameSessionState | null
presentation: GamePresentationState
mapPresentation: MapPresentationState
hudPresentation: HudPresentationState
lastResult: GameResult | null
constructor() {
@@ -18,6 +23,8 @@ export class GameRuntime {
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
}
@@ -26,6 +33,8 @@ export class GameRuntime {
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
}
@@ -39,6 +48,8 @@ export class GameRuntime {
effects: [],
}
this.presentation = result.presentation
this.mapPresentation = result.presentation.map
this.hudPresentation = result.presentation.hud
this.lastResult = result
return result
}
@@ -58,6 +69,7 @@ export class GameRuntime {
inRangeControlId: null,
score: 0,
guidanceState: 'searching',
modeState: null,
}
const result: GameResult = {
nextState: emptyState,
@@ -66,12 +78,16 @@ export class GameRuntime {
}
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
}
@@ -80,11 +96,23 @@ export class GameRuntime {
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}`)
}
}

View File

@@ -1,5 +1,6 @@
export type GameSessionStatus = 'idle' | 'running' | 'finished' | 'failed'
export type GuidanceState = 'searching' | 'approaching' | 'ready'
export type GameModeState = Record<string, unknown> | null
export interface GameSessionState {
status: GameSessionStatus
@@ -10,4 +11,5 @@ export interface GameSessionState {
inRangeControlId: string | null
score: number
guidanceState: GuidanceState
modeState: GameModeState
}