Add score-o mode and split game map HUD presentation
This commit is contained in:
@@ -5,8 +5,15 @@ import { type GameEvent } from '../core/gameEvent'
|
||||
import { type GameEffect, type GameResult } from '../core/gameResult'
|
||||
import { type GameSessionState } from '../core/gameSessionState'
|
||||
import { EMPTY_GAME_PRESENTATION_STATE, type GamePresentationState } from '../presentation/presentationState'
|
||||
import { type HudPresentationState } from '../presentation/hudPresentationState'
|
||||
import { type MapPresentationState } from '../presentation/mapPresentationState'
|
||||
import { type RulePlugin } from './rulePlugin'
|
||||
|
||||
type ClassicSequentialModeState = {
|
||||
mode: 'classic-sequential'
|
||||
phase: 'start' | 'course' | 'finish' | 'done'
|
||||
}
|
||||
|
||||
function getApproxDistanceMeters(a: LonLatPoint, b: LonLatPoint): number {
|
||||
const avgLatRad = ((a.lat + b.lat) / 2) * Math.PI / 180
|
||||
const dx = (b.lon - a.lon) * 111320 * Math.cos(avgLatRad)
|
||||
@@ -132,43 +139,84 @@ function buildPresentation(definition: GameDefinition, state: GameSessionState):
|
||||
: '打点'
|
||||
: '打点'
|
||||
const revealFullCourse = completedStart
|
||||
const hudPresentation: HudPresentationState = {
|
||||
actionTagText: '目标',
|
||||
distanceTagText: '点距',
|
||||
hudTargetControlId: currentTarget ? currentTarget.id : null,
|
||||
progressText: '0/0',
|
||||
punchButtonText,
|
||||
punchableControlId: punchButtonEnabled && currentTarget ? currentTarget.id : null,
|
||||
punchButtonEnabled,
|
||||
punchHintText: buildPunchHintText(definition, state, currentTarget),
|
||||
}
|
||||
|
||||
if (!scoringControls.length) {
|
||||
return {
|
||||
...EMPTY_GAME_PRESENTATION_STATE,
|
||||
activeStart,
|
||||
completedStart,
|
||||
activeFinish,
|
||||
completedFinish,
|
||||
revealFullCourse,
|
||||
activeLegIndices,
|
||||
completedLegIndices,
|
||||
progressText: '0/0',
|
||||
punchButtonText,
|
||||
punchableControlId: punchButtonEnabled && currentTarget ? currentTarget.id : null,
|
||||
punchButtonEnabled,
|
||||
punchHintText: buildPunchHintText(definition, state, currentTarget),
|
||||
map: {
|
||||
...EMPTY_GAME_PRESENTATION_STATE.map,
|
||||
controlVisualMode: 'single-target',
|
||||
showCourseLegs: true,
|
||||
guidanceLegAnimationEnabled: true,
|
||||
focusableControlIds: [],
|
||||
focusedControlId: null,
|
||||
focusedControlSequences: [],
|
||||
activeStart,
|
||||
completedStart,
|
||||
activeFinish,
|
||||
focusedFinish: false,
|
||||
completedFinish,
|
||||
revealFullCourse,
|
||||
activeLegIndices,
|
||||
completedLegIndices,
|
||||
},
|
||||
hud: hudPresentation,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
const mapPresentation: MapPresentationState = {
|
||||
controlVisualMode: 'single-target',
|
||||
showCourseLegs: true,
|
||||
guidanceLegAnimationEnabled: true,
|
||||
focusableControlIds: [],
|
||||
focusedControlId: null,
|
||||
focusedControlSequences: [],
|
||||
activeControlIds: running && currentTarget ? [currentTarget.id] : [],
|
||||
activeControlSequences: running && currentTarget && currentTarget.kind === 'control' && typeof currentTarget.sequence === 'number' ? [currentTarget.sequence] : [],
|
||||
activeStart,
|
||||
completedStart,
|
||||
activeFinish,
|
||||
focusedFinish: false,
|
||||
completedFinish,
|
||||
revealFullCourse,
|
||||
activeLegIndices,
|
||||
completedLegIndices,
|
||||
completedControlIds: completedControls.map((control) => control.id),
|
||||
completedControlSequences: getCompletedControlSequences(definition, state),
|
||||
progressText: `${completedControls.length}/${scoringControls.length}`,
|
||||
punchableControlId: punchButtonEnabled && currentTarget ? currentTarget.id : null,
|
||||
punchButtonEnabled,
|
||||
punchButtonText,
|
||||
punchHintText: buildPunchHintText(definition, state, currentTarget),
|
||||
}
|
||||
|
||||
return {
|
||||
map: mapPresentation,
|
||||
hud: {
|
||||
...hudPresentation,
|
||||
progressText: `${completedControls.length}/${scoringControls.length}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function resolveClassicPhase(nextTarget: GameControl | null, currentTarget: GameControl, finished: boolean): ClassicSequentialModeState['phase'] {
|
||||
if (finished || currentTarget.kind === 'finish') {
|
||||
return 'done'
|
||||
}
|
||||
|
||||
if (currentTarget.kind === 'start') {
|
||||
return nextTarget && nextTarget.kind === 'finish' ? 'finish' : 'course'
|
||||
}
|
||||
|
||||
if (nextTarget && nextTarget.kind === 'finish') {
|
||||
return 'finish'
|
||||
}
|
||||
|
||||
return 'course'
|
||||
}
|
||||
|
||||
function getInitialTargetId(definition: GameDefinition): string | null {
|
||||
@@ -237,6 +285,10 @@ function applyCompletion(definition: GameDefinition, state: GameSessionState, cu
|
||||
status: finished ? 'finished' : state.status,
|
||||
endedAt: finished ? at : state.endedAt,
|
||||
guidanceState: nextTarget ? 'searching' : 'searching',
|
||||
modeState: {
|
||||
mode: 'classic-sequential',
|
||||
phase: resolveClassicPhase(nextTarget, currentTarget, finished),
|
||||
},
|
||||
}
|
||||
const effects: GameEffect[] = [buildCompletedEffect(currentTarget)]
|
||||
|
||||
@@ -266,6 +318,10 @@ export class ClassicSequentialRule implements RulePlugin {
|
||||
inRangeControlId: null,
|
||||
score: 0,
|
||||
guidanceState: 'searching',
|
||||
modeState: {
|
||||
mode: 'classic-sequential',
|
||||
phase: 'start',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +338,10 @@ export class ClassicSequentialRule implements RulePlugin {
|
||||
endedAt: null,
|
||||
inRangeControlId: null,
|
||||
guidanceState: 'searching',
|
||||
modeState: {
|
||||
mode: 'classic-sequential',
|
||||
phase: 'start',
|
||||
},
|
||||
}
|
||||
return {
|
||||
nextState,
|
||||
@@ -296,6 +356,10 @@ export class ClassicSequentialRule implements RulePlugin {
|
||||
status: 'finished',
|
||||
endedAt: event.at,
|
||||
guidanceState: 'searching',
|
||||
modeState: {
|
||||
mode: 'classic-sequential',
|
||||
phase: 'done',
|
||||
},
|
||||
}
|
||||
return {
|
||||
nextState,
|
||||
|
||||
Reference in New Issue
Block a user