gameRuntime.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { type GameDefinition } from './gameDefinition'
  2. import { type GameEvent } from './gameEvent'
  3. import { type GameResult } from './gameResult'
  4. import { type GameSessionState } from './gameSessionState'
  5. import { EMPTY_GAME_PRESENTATION_STATE, type GamePresentationState } from '../presentation/presentationState'
  6. import { EMPTY_HUD_PRESENTATION_STATE, type HudPresentationState } from '../presentation/hudPresentationState'
  7. import { EMPTY_MAP_PRESENTATION_STATE, type MapPresentationState } from '../presentation/mapPresentationState'
  8. import { ClassicSequentialRule } from '../rules/classicSequentialRule'
  9. import { ScoreORule } from '../rules/scoreORule'
  10. import { type RulePlugin } from '../rules/rulePlugin'
  11. export class GameRuntime {
  12. definition: GameDefinition | null
  13. plugin: RulePlugin | null
  14. state: GameSessionState | null
  15. presentation: GamePresentationState
  16. mapPresentation: MapPresentationState
  17. hudPresentation: HudPresentationState
  18. lastResult: GameResult | null
  19. constructor() {
  20. this.definition = null
  21. this.plugin = null
  22. this.state = null
  23. this.presentation = EMPTY_GAME_PRESENTATION_STATE
  24. this.mapPresentation = EMPTY_MAP_PRESENTATION_STATE
  25. this.hudPresentation = EMPTY_HUD_PRESENTATION_STATE
  26. this.lastResult = null
  27. }
  28. clear(): void {
  29. this.definition = null
  30. this.plugin = null
  31. this.state = null
  32. this.presentation = EMPTY_GAME_PRESENTATION_STATE
  33. this.mapPresentation = EMPTY_MAP_PRESENTATION_STATE
  34. this.hudPresentation = EMPTY_HUD_PRESENTATION_STATE
  35. this.lastResult = null
  36. }
  37. loadDefinition(definition: GameDefinition): GameResult {
  38. this.definition = definition
  39. this.plugin = this.resolvePlugin(definition)
  40. this.state = this.plugin.initialize(definition)
  41. const result: GameResult = {
  42. nextState: this.state,
  43. presentation: this.plugin.buildPresentation(definition, this.state),
  44. effects: [],
  45. }
  46. this.presentation = result.presentation
  47. this.mapPresentation = result.presentation.map
  48. this.hudPresentation = result.presentation.hud
  49. this.lastResult = result
  50. return result
  51. }
  52. restoreDefinition(definition: GameDefinition, state: GameSessionState): GameResult {
  53. this.definition = definition
  54. this.plugin = this.resolvePlugin(definition)
  55. this.state = {
  56. status: state.status,
  57. endReason: state.endReason,
  58. startedAt: state.startedAt,
  59. endedAt: state.endedAt,
  60. completedControlIds: state.completedControlIds.slice(),
  61. skippedControlIds: state.skippedControlIds.slice(),
  62. currentTargetControlId: state.currentTargetControlId,
  63. inRangeControlId: state.inRangeControlId,
  64. score: state.score,
  65. guidanceState: state.guidanceState,
  66. modeState: state.modeState
  67. ? JSON.parse(JSON.stringify(state.modeState)) as Record<string, unknown>
  68. : null,
  69. }
  70. const result: GameResult = {
  71. nextState: this.state,
  72. presentation: this.plugin.buildPresentation(definition, this.state),
  73. effects: [],
  74. }
  75. this.presentation = result.presentation
  76. this.mapPresentation = result.presentation.map
  77. this.hudPresentation = result.presentation.hud
  78. this.lastResult = result
  79. return result
  80. }
  81. startSession(startAt = Date.now()): GameResult {
  82. return this.dispatch({ type: 'session_started', at: startAt })
  83. }
  84. dispatch(event: GameEvent): GameResult {
  85. if (!this.definition || !this.plugin || !this.state) {
  86. const emptyState: GameSessionState = {
  87. status: 'idle',
  88. endReason: null,
  89. startedAt: null,
  90. endedAt: null,
  91. completedControlIds: [],
  92. skippedControlIds: [],
  93. currentTargetControlId: null,
  94. inRangeControlId: null,
  95. score: 0,
  96. guidanceState: 'searching',
  97. modeState: null,
  98. }
  99. const result: GameResult = {
  100. nextState: emptyState,
  101. presentation: EMPTY_GAME_PRESENTATION_STATE,
  102. effects: [],
  103. }
  104. this.lastResult = result
  105. this.presentation = result.presentation
  106. this.mapPresentation = result.presentation.map
  107. this.hudPresentation = result.presentation.hud
  108. return result
  109. }
  110. const result = this.plugin.reduce(this.definition, this.state, event)
  111. this.state = result.nextState
  112. this.presentation = result.presentation
  113. this.mapPresentation = result.presentation.map
  114. this.hudPresentation = result.presentation.hud
  115. this.lastResult = result
  116. return result
  117. }
  118. getPresentation(): GamePresentationState {
  119. return this.presentation
  120. }
  121. getMapPresentation(): MapPresentationState {
  122. return this.mapPresentation
  123. }
  124. getHudPresentation(): HudPresentationState {
  125. return this.hudPresentation
  126. }
  127. resolvePlugin(definition: GameDefinition): RulePlugin {
  128. if (definition.mode === 'classic-sequential') {
  129. return new ClassicSequentialRule()
  130. }
  131. if (definition.mode === 'score-o') {
  132. return new ScoreORule()
  133. }
  134. throw new Error(`未支持的玩法模式: ${definition.mode}`)
  135. }
  136. }