resultSummary.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { type GameDefinition } from '../core/gameDefinition'
  2. import { type GameSessionState } from '../core/gameSessionState'
  3. import { type TelemetryPresentation } from '../telemetry/telemetryPresentation'
  4. export interface ResultSummaryRow {
  5. label: string
  6. value: string
  7. }
  8. export interface ResultSummarySnapshot {
  9. title: string
  10. subtitle: string
  11. heroLabel: string
  12. heroValue: string
  13. rows: ResultSummaryRow[]
  14. }
  15. export interface ResultSummaryMetrics {
  16. totalScore?: number
  17. baseScore?: number
  18. bonusScore?: number
  19. quizCorrectCount?: number
  20. quizWrongCount?: number
  21. quizTimeoutCount?: number
  22. }
  23. function resolveTitle(definition: GameDefinition | null, mapTitle: string): string {
  24. if (mapTitle) {
  25. return mapTitle
  26. }
  27. if (definition && definition.title) {
  28. return definition.title
  29. }
  30. return '本局结果'
  31. }
  32. function buildHeroValue(
  33. definition: GameDefinition | null,
  34. sessionState: GameSessionState,
  35. telemetryPresentation: TelemetryPresentation,
  36. metrics?: ResultSummaryMetrics,
  37. ): string {
  38. const totalScore = metrics && typeof metrics.totalScore === 'number'
  39. ? metrics.totalScore
  40. : sessionState.score
  41. if (definition && definition.mode === 'score-o') {
  42. return `${totalScore}`
  43. }
  44. return telemetryPresentation.elapsedTimerText
  45. }
  46. function buildHeroLabel(definition: GameDefinition | null): string {
  47. return definition && definition.mode === 'score-o' ? '本局得分' : '本局用时'
  48. }
  49. function buildSubtitle(sessionState: GameSessionState): string {
  50. if (sessionState.status === 'finished') {
  51. return '本局已完成'
  52. }
  53. if (sessionState.endReason === 'timed_out') {
  54. return '本局超时结束'
  55. }
  56. if (sessionState.status === 'failed') {
  57. return '本局已结束'
  58. }
  59. return '对局摘要'
  60. }
  61. export function buildResultSummarySnapshot(
  62. definition: GameDefinition | null,
  63. sessionState: GameSessionState | null,
  64. telemetryPresentation: TelemetryPresentation,
  65. mapTitle: string,
  66. metrics?: ResultSummaryMetrics,
  67. ): ResultSummarySnapshot {
  68. const resolvedSessionState: GameSessionState = sessionState || {
  69. status: 'idle',
  70. endReason: null,
  71. startedAt: null,
  72. endedAt: null,
  73. completedControlIds: [],
  74. skippedControlIds: [],
  75. currentTargetControlId: null,
  76. inRangeControlId: null,
  77. score: 0,
  78. guidanceState: 'searching',
  79. modeState: null,
  80. }
  81. const skippedCount = resolvedSessionState.skippedControlIds.length
  82. const totalControlCount = definition
  83. ? definition.controls.filter((control) => control.kind === 'control').length
  84. : 0
  85. const averageHeartRateText = telemetryPresentation.heartRateValueText !== '--'
  86. ? `${telemetryPresentation.heartRateValueText} ${telemetryPresentation.heartRateUnitText || 'bpm'}`
  87. : '--'
  88. const totalScore = metrics && typeof metrics.totalScore === 'number' ? metrics.totalScore : resolvedSessionState.score
  89. const baseScore = metrics && typeof metrics.baseScore === 'number' ? metrics.baseScore : resolvedSessionState.score
  90. const bonusScore = metrics && typeof metrics.bonusScore === 'number' ? metrics.bonusScore : 0
  91. const quizCorrectCount = metrics && typeof metrics.quizCorrectCount === 'number' ? metrics.quizCorrectCount : 0
  92. const quizWrongCount = metrics && typeof metrics.quizWrongCount === 'number' ? metrics.quizWrongCount : 0
  93. const quizTimeoutCount = metrics && typeof metrics.quizTimeoutCount === 'number' ? metrics.quizTimeoutCount : 0
  94. const includeQuizRows = bonusScore > 0 || quizCorrectCount > 0 || quizWrongCount > 0 || quizTimeoutCount > 0
  95. const rows: ResultSummaryRow[] = [
  96. {
  97. label: '状态',
  98. value: resolvedSessionState.endReason === 'timed_out'
  99. ? '超时结束'
  100. : resolvedSessionState.status === 'finished'
  101. ? '完成'
  102. : (resolvedSessionState.status === 'failed' ? '结束' : '进行中'),
  103. },
  104. { label: '完成点数', value: totalControlCount > 0 ? `${resolvedSessionState.completedControlIds.length}/${totalControlCount}` : `${resolvedSessionState.completedControlIds.length}` },
  105. { label: '跳过点数', value: `${skippedCount}` },
  106. { label: '总分', value: `${totalScore}` },
  107. ]
  108. if (includeQuizRows) {
  109. rows.push({ label: '基础积分', value: `${baseScore}` })
  110. rows.push({ label: '答题奖励积分', value: `${bonusScore}` })
  111. rows.push({ label: '答题正确数', value: `${quizCorrectCount}` })
  112. rows.push({ label: '答题错误数', value: `${quizWrongCount}` })
  113. rows.push({ label: '答题超时数', value: `${quizTimeoutCount}` })
  114. }
  115. rows.push({ label: '累计里程', value: telemetryPresentation.mileageText })
  116. rows.push({ label: '平均速度', value: `${telemetryPresentation.averageSpeedValueText}${telemetryPresentation.averageSpeedUnitText}` })
  117. rows.push({ label: '累计消耗', value: `${telemetryPresentation.caloriesValueText}${telemetryPresentation.caloriesUnitText}` })
  118. rows.push({ label: '平均心率', value: averageHeartRateText })
  119. return {
  120. title: resolveTitle(definition, mapTitle),
  121. subtitle: buildSubtitle(resolvedSessionState),
  122. heroLabel: buildHeroLabel(definition),
  123. heroValue: buildHeroValue(definition, resolvedSessionState, telemetryPresentation, metrics),
  124. rows,
  125. }
  126. }