courseToGameDefinition.ts 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { type GameDefinition, type GameControl, type PunchPolicyType } from '../core/gameDefinition'
  2. import { type OrienteeringCourseData } from '../../utils/orienteeringCourse'
  3. function sortBySequence<T extends { sequence: number | null }>(items: T[]): T[] {
  4. return [...items].sort((a, b) => (a.sequence || 0) - (b.sequence || 0))
  5. }
  6. function buildDisplayBody(label: string, sequence: number | null): string {
  7. if (typeof sequence === 'number') {
  8. return `检查点 ${sequence} · ${label || String(sequence)}`
  9. }
  10. return label
  11. }
  12. export function buildGameDefinitionFromCourse(
  13. course: OrienteeringCourseData,
  14. controlRadiusMeters: number,
  15. mode: GameDefinition['mode'] = 'classic-sequential',
  16. autoFinishOnLastControl = true,
  17. punchPolicy: PunchPolicyType = 'enter-confirm',
  18. punchRadiusMeters = 5,
  19. ): GameDefinition {
  20. const controls: GameControl[] = []
  21. for (const start of course.layers.starts) {
  22. controls.push({
  23. id: `start-${controls.length + 1}`,
  24. code: start.label || 'S',
  25. label: start.label || 'Start',
  26. kind: 'start',
  27. point: start.point,
  28. sequence: null,
  29. displayContent: null,
  30. })
  31. }
  32. for (const control of sortBySequence(course.layers.controls)) {
  33. const label = control.label || String(control.sequence)
  34. controls.push({
  35. id: `control-${control.sequence}`,
  36. code: label,
  37. label,
  38. kind: 'control',
  39. point: control.point,
  40. sequence: control.sequence,
  41. displayContent: {
  42. title: `收集 ${label}`,
  43. body: buildDisplayBody(label, control.sequence),
  44. },
  45. })
  46. }
  47. for (const finish of course.layers.finishes) {
  48. controls.push({
  49. id: `finish-${controls.length + 1}`,
  50. code: finish.label || 'F',
  51. label: finish.label || 'Finish',
  52. kind: 'finish',
  53. point: finish.point,
  54. sequence: null,
  55. displayContent: null,
  56. })
  57. }
  58. return {
  59. id: `course-${course.title || 'default'}`,
  60. mode,
  61. title: course.title || (mode === 'score-o' ? 'Score-O' : 'Classic Sequential'),
  62. controlRadiusMeters,
  63. punchRadiusMeters,
  64. punchPolicy,
  65. controls,
  66. autoFinishOnLastControl,
  67. }
  68. }