Refine telemetry-driven HUD and fitness feedback
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { lonLatToWorldTile, webMercatorToLonLat, type LonLatPoint } from './projection'
|
||||
import { parseOrienteeringCourseKml, type OrienteeringCourseData } from './orienteeringCourse'
|
||||
import { mergeGameAudioConfig, type AudioCueKey, type GameAudioConfig, type GameAudioConfigOverrides, type PartialAudioCueConfig } from '../game/audio/audioConfig'
|
||||
import { mergeTelemetryConfig, type TelemetryConfig } from '../game/telemetry/telemetryConfig'
|
||||
import {
|
||||
mergeGameHapticsConfig,
|
||||
mergeGameUiEffectsConfig,
|
||||
@@ -45,6 +46,7 @@ export interface RemoteMapConfig {
|
||||
punchPolicy: 'enter' | 'enter-confirm'
|
||||
punchRadiusMeters: number
|
||||
autoFinishOnLastControl: boolean
|
||||
telemetryConfig: TelemetryConfig
|
||||
audioConfig: GameAudioConfig
|
||||
hapticsConfig: GameHapticsConfig
|
||||
uiEffectsConfig: GameUiEffectsConfig
|
||||
@@ -59,6 +61,7 @@ interface ParsedGameConfig {
|
||||
punchPolicy: 'enter' | 'enter-confirm'
|
||||
punchRadiusMeters: number
|
||||
autoFinishOnLastControl: boolean
|
||||
telemetryConfig: TelemetryConfig
|
||||
audioConfig: GameAudioConfig
|
||||
hapticsConfig: GameHapticsConfig
|
||||
uiEffectsConfig: GameUiEffectsConfig
|
||||
@@ -206,6 +209,40 @@ function parsePunchPolicy(rawValue: unknown): 'enter' | 'enter-confirm' {
|
||||
return rawValue === 'enter' ? 'enter' : 'enter-confirm'
|
||||
}
|
||||
|
||||
function parseTelemetryConfig(rawValue: unknown): TelemetryConfig {
|
||||
const normalized = normalizeObjectRecord(rawValue)
|
||||
if (!Object.keys(normalized).length) {
|
||||
return mergeTelemetryConfig()
|
||||
}
|
||||
|
||||
const rawHeartRate = getFirstDefined(normalized, ['heartrate', 'heart_rate'])
|
||||
const normalizedHeartRate = normalizeObjectRecord(rawHeartRate)
|
||||
const ageRaw = getFirstDefined(normalizedHeartRate, ['age', 'userage', 'heartrateage', 'hrage']) !== undefined
|
||||
? getFirstDefined(normalizedHeartRate, ['age', 'userage', 'heartrateage', 'hrage'])
|
||||
: getFirstDefined(normalized, ['age', 'userage', 'heartrateage', 'hrage'])
|
||||
const restingHeartRateRaw = getFirstDefined(normalizedHeartRate, ['restingheartratebpm', 'restingheartrate', 'restinghr', 'resting'])
|
||||
!== undefined
|
||||
? getFirstDefined(normalizedHeartRate, ['restingheartratebpm', 'restingheartrate', 'restinghr', 'resting'])
|
||||
: getFirstDefined(normalized, ['restingheartratebpm', 'restingheartrate', 'restinghr', 'resting'])
|
||||
const userWeightRaw = getFirstDefined(normalizedHeartRate, ['userweightkg', 'weightkg', 'weight'])
|
||||
!== undefined
|
||||
? getFirstDefined(normalizedHeartRate, ['userweightkg', 'weightkg', 'weight'])
|
||||
: getFirstDefined(normalized, ['userweightkg', 'weightkg', 'weight'])
|
||||
|
||||
const telemetryOverrides: Partial<TelemetryConfig> = {}
|
||||
if (ageRaw !== undefined) {
|
||||
telemetryOverrides.heartRateAge = Number(ageRaw)
|
||||
}
|
||||
if (restingHeartRateRaw !== undefined) {
|
||||
telemetryOverrides.restingHeartRateBpm = Number(restingHeartRateRaw)
|
||||
}
|
||||
if (userWeightRaw !== undefined) {
|
||||
telemetryOverrides.userWeightKg = Number(userWeightRaw)
|
||||
}
|
||||
|
||||
return mergeTelemetryConfig(telemetryOverrides)
|
||||
}
|
||||
|
||||
|
||||
function normalizeObjectRecord(rawValue: unknown): Record<string, unknown> {
|
||||
if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
|
||||
@@ -622,6 +659,7 @@ function parseGameConfigFromJson(text: string, gameConfigUrl: string): ParsedGam
|
||||
}
|
||||
}
|
||||
const rawAudio = rawGame && rawGame.audio !== undefined ? rawGame.audio : parsed.audio
|
||||
const rawTelemetry = rawGame && rawGame.telemetry !== undefined ? rawGame.telemetry : parsed.telemetry
|
||||
const rawHaptics = rawGame && rawGame.haptics !== undefined ? rawGame.haptics : parsed.haptics
|
||||
const rawUiEffects = rawGame && rawGame.uiEffects !== undefined
|
||||
? rawGame.uiEffects
|
||||
@@ -668,6 +706,7 @@ function parseGameConfigFromJson(text: string, gameConfigUrl: string): ParsedGam
|
||||
normalizedGame.autofinishonlastcontrol !== undefined ? normalizedGame.autofinishonlastcontrol : normalized.autofinishonlastcontrol,
|
||||
true,
|
||||
),
|
||||
telemetryConfig: parseTelemetryConfig(rawTelemetry),
|
||||
audioConfig: parseAudioConfig(rawAudio, gameConfigUrl),
|
||||
hapticsConfig: parseHapticsConfig(rawHaptics),
|
||||
uiEffectsConfig: parseUiEffectsConfig(rawUiEffects),
|
||||
@@ -716,6 +755,18 @@ function parseGameConfigFromYaml(text: string, gameConfigUrl: string): ParsedGam
|
||||
5,
|
||||
),
|
||||
autoFinishOnLastControl: parseBoolean(config.autofinishonlastcontrol, true),
|
||||
telemetryConfig: parseTelemetryConfig({
|
||||
heartRate: {
|
||||
age: config.heartrateage !== undefined ? config.heartrateage : config.telemetryheartrateage,
|
||||
restingHeartRateBpm: config.restingheartratebpm !== undefined
|
||||
? config.restingheartratebpm
|
||||
: config.restingheartrate !== undefined
|
||||
? config.restingheartrate
|
||||
: config.telemetryrestingheartratebpm !== undefined
|
||||
? config.telemetryrestingheartratebpm
|
||||
: config.telemetryrestingheartrate,
|
||||
},
|
||||
}),
|
||||
audioConfig: parseAudioConfig({
|
||||
enabled: config.audioenabled,
|
||||
masterVolume: config.audiomastervolume,
|
||||
@@ -979,6 +1030,7 @@ export async function loadRemoteMapConfig(gameConfigUrl: string): Promise<Remote
|
||||
punchPolicy: gameConfig.punchPolicy,
|
||||
punchRadiusMeters: gameConfig.punchRadiusMeters,
|
||||
autoFinishOnLastControl: gameConfig.autoFinishOnLastControl,
|
||||
telemetryConfig: gameConfig.telemetryConfig,
|
||||
audioConfig: gameConfig.audioConfig,
|
||||
hapticsConfig: gameConfig.hapticsConfig,
|
||||
uiEffectsConfig: gameConfig.uiEffectsConfig,
|
||||
|
||||
Reference in New Issue
Block a user