| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import {
- type GameContentExperienceConfig,
- type GameContentExperienceConfigOverride,
- type GameDefinition,
- type GameControl,
- type GameControlDisplayContent,
- type GameControlDisplayContentOverride,
- type PunchPolicyType,
- } from '../core/gameDefinition'
- import { type OrienteeringCourseData } from '../../utils/orienteeringCourse'
- function sortBySequence<T extends { sequence: number | null }>(items: T[]): T[] {
- return [...items].sort((a, b) => (a.sequence || 0) - (b.sequence || 0))
- }
- function buildDisplayBody(label: string, sequence: number | null): string {
- if (typeof sequence === 'number') {
- return `检查点 ${sequence} · ${label || String(sequence)}`
- }
- return label
- }
- function applyExperienceOverride(
- baseExperience: GameContentExperienceConfig | null,
- override: GameContentExperienceConfigOverride | undefined,
- ): GameContentExperienceConfig | null {
- if (!override) {
- return baseExperience
- }
- if (override.type === 'native') {
- return {
- type: 'native',
- url: null,
- bridge: 'content-v1',
- fallback: 'native',
- }
- }
- if (override.type === 'h5' && override.url) {
- return {
- type: 'h5',
- url: override.url,
- bridge: override.bridge || (baseExperience ? baseExperience.bridge : 'content-v1'),
- fallback: override.fallback || 'native',
- }
- }
- return baseExperience
- }
- function applyDisplayContentOverride(
- baseContent: GameControlDisplayContent,
- override: GameControlDisplayContentOverride | undefined,
- ): GameControlDisplayContent {
- if (!override) {
- return baseContent
- }
- return {
- title: override.title || baseContent.title,
- body: override.body || baseContent.body,
- autoPopup: override.autoPopup !== undefined ? override.autoPopup : baseContent.autoPopup,
- once: override.once !== undefined ? override.once : baseContent.once,
- priority: override.priority !== undefined ? override.priority : baseContent.priority,
- clickTitle: override.clickTitle !== undefined ? override.clickTitle : baseContent.clickTitle,
- clickBody: override.clickBody !== undefined ? override.clickBody : baseContent.clickBody,
- contentExperience: applyExperienceOverride(baseContent.contentExperience, override.contentExperience),
- clickExperience: applyExperienceOverride(baseContent.clickExperience, override.clickExperience),
- }
- }
- export function buildGameDefinitionFromCourse(
- course: OrienteeringCourseData,
- controlRadiusMeters: number,
- mode: GameDefinition['mode'] = 'classic-sequential',
- autoFinishOnLastControl = true,
- punchPolicy: PunchPolicyType = 'enter-confirm',
- punchRadiusMeters = 5,
- requiresFocusSelection = false,
- skipEnabled = false,
- skipRadiusMeters = 30,
- skipRequiresConfirm = true,
- controlScoreOverrides: Record<string, number> = {},
- controlContentOverrides: Record<string, GameControlDisplayContentOverride> = {},
- defaultControlScore: number | null = null,
- ): GameDefinition {
- const controls: GameControl[] = []
- for (let startIndex = 0; startIndex < course.layers.starts.length; startIndex += 1) {
- const start = course.layers.starts[startIndex]
- const startId = `start-${startIndex + 1}`
- controls.push({
- id: startId,
- code: start.label || 'S',
- label: start.label || 'Start',
- kind: 'start',
- point: start.point,
- sequence: null,
- score: null,
- displayContent: applyDisplayContentOverride({
- title: '比赛开始',
- body: `${start.label || '开始点'}已激活,按提示前往下一个目标点。`,
- autoPopup: true,
- once: false,
- priority: 1,
- clickTitle: '比赛开始',
- clickBody: `${start.label || '开始点'}已激活,按提示前往下一个目标点。`,
- contentExperience: null,
- clickExperience: null,
- }, controlContentOverrides[startId]),
- })
- }
- for (const control of sortBySequence(course.layers.controls)) {
- const label = control.label || String(control.sequence)
- const controlId = `control-${control.sequence}`
- const score = controlId in controlScoreOverrides
- ? controlScoreOverrides[controlId]
- : defaultControlScore
- controls.push({
- id: controlId,
- code: label,
- label,
- kind: 'control',
- point: control.point,
- sequence: control.sequence,
- score,
- displayContent: applyDisplayContentOverride({
- title: score !== null ? `收集 ${label} (+${score}分)` : `收集 ${label}`,
- body: score !== null ? `${buildDisplayBody(label, control.sequence)} · ${score}分` : buildDisplayBody(label, control.sequence),
- autoPopup: true,
- once: false,
- priority: 1,
- clickTitle: score !== null ? `收集 ${label} (+${score}分)` : `收集 ${label}`,
- clickBody: score !== null ? `${buildDisplayBody(label, control.sequence)} · ${score}分` : buildDisplayBody(label, control.sequence),
- contentExperience: null,
- clickExperience: null,
- }, controlContentOverrides[controlId]),
- })
- }
- for (let finishIndex = 0; finishIndex < course.layers.finishes.length; finishIndex += 1) {
- const finish = course.layers.finishes[finishIndex]
- const finishId = `finish-${finishIndex + 1}`
- const legacyFinishId = `finish-${controls.length + 1}`
- controls.push({
- id: finishId,
- code: finish.label || 'F',
- label: finish.label || 'Finish',
- kind: 'finish',
- point: finish.point,
- sequence: null,
- score: null,
- displayContent: applyDisplayContentOverride({
- title: '完成路线',
- body: `${finish.label || '结束点'}已完成,准备查看本局结果。`,
- autoPopup: true,
- once: false,
- priority: 2,
- clickTitle: '完成路线',
- clickBody: `${finish.label || '结束点'}已完成,准备查看本局结果。`,
- contentExperience: null,
- clickExperience: null,
- }, controlContentOverrides[finishId] || controlContentOverrides[legacyFinishId]),
- })
- }
- return {
- id: `course-${course.title || 'default'}`,
- mode,
- title: course.title || (mode === 'score-o' ? 'Score-O' : 'Classic Sequential'),
- controlRadiusMeters,
- punchRadiusMeters,
- punchPolicy,
- requiresFocusSelection,
- skipEnabled,
- skipRadiusMeters,
- skipRequiresConfirm,
- controls,
- autoFinishOnLastControl,
- }
- }
|