feat: 收敛玩法运行时配置并加入故障恢复
This commit is contained in:
292
miniprogram/game/core/systemSettingsState.ts
Normal file
292
miniprogram/game/core/systemSettingsState.ts
Normal file
@@ -0,0 +1,292 @@
|
||||
import { type AnimationLevel } from '../../utils/animationLevel'
|
||||
import { type TrackColorPreset, type TrackDisplayMode, type TrackStyleProfile, type TrackTailLengthPreset } from '../presentation/trackStyleConfig'
|
||||
import { type GpsMarkerColorPreset, type GpsMarkerSizePreset, type GpsMarkerStyleId } from '../presentation/gpsMarkerStyleConfig'
|
||||
|
||||
export type SideButtonPlacement = 'left' | 'right'
|
||||
export type CenterScaleRulerAnchorMode = 'screen-center' | 'compass-center'
|
||||
export type UserNorthReferenceMode = 'magnetic' | 'true'
|
||||
export type CompassTuningProfile = 'smooth' | 'balanced' | 'responsive'
|
||||
|
||||
export type SettingLockKey =
|
||||
| 'lockAnimationLevel'
|
||||
| 'lockTrackMode'
|
||||
| 'lockTrackTailLength'
|
||||
| 'lockTrackColor'
|
||||
| 'lockTrackStyle'
|
||||
| 'lockGpsMarkerVisible'
|
||||
| 'lockGpsMarkerStyle'
|
||||
| 'lockGpsMarkerSize'
|
||||
| 'lockGpsMarkerColor'
|
||||
| 'lockSideButtonPlacement'
|
||||
| 'lockAutoRotate'
|
||||
| 'lockCompassTuning'
|
||||
| 'lockScaleRulerVisible'
|
||||
| 'lockScaleRulerAnchor'
|
||||
| 'lockNorthReference'
|
||||
| 'lockHeartRateDevice'
|
||||
|
||||
export type StoredUserSettings = {
|
||||
animationLevel?: AnimationLevel
|
||||
trackDisplayMode?: TrackDisplayMode
|
||||
trackTailLength?: TrackTailLengthPreset
|
||||
trackColorPreset?: TrackColorPreset
|
||||
trackStyleProfile?: TrackStyleProfile
|
||||
gpsMarkerVisible?: boolean
|
||||
gpsMarkerStyle?: GpsMarkerStyleId
|
||||
gpsMarkerSize?: GpsMarkerSizePreset
|
||||
gpsMarkerColorPreset?: GpsMarkerColorPreset
|
||||
autoRotateEnabled?: boolean
|
||||
compassTuningProfile?: CompassTuningProfile
|
||||
northReferenceMode?: UserNorthReferenceMode
|
||||
sideButtonPlacement?: SideButtonPlacement
|
||||
showCenterScaleRuler?: boolean
|
||||
centerScaleRulerAnchorMode?: CenterScaleRulerAnchorMode
|
||||
}
|
||||
|
||||
export interface SystemSettingsConfig {
|
||||
values: Partial<StoredUserSettings>
|
||||
locks: Partial<Record<SettingLockKey, boolean>>
|
||||
}
|
||||
|
||||
export type ResolvedSystemSettingsState = {
|
||||
values: Required<StoredUserSettings>
|
||||
locks: Record<SettingLockKey, boolean>
|
||||
}
|
||||
|
||||
export const USER_SETTINGS_STORAGE_KEY = 'cmr_user_settings_v1'
|
||||
|
||||
export const DEFAULT_STORED_USER_SETTINGS: Required<StoredUserSettings> = {
|
||||
animationLevel: 'standard',
|
||||
trackDisplayMode: 'full',
|
||||
trackTailLength: 'medium',
|
||||
trackColorPreset: 'mint',
|
||||
trackStyleProfile: 'neon',
|
||||
gpsMarkerVisible: true,
|
||||
gpsMarkerStyle: 'beacon',
|
||||
gpsMarkerSize: 'medium',
|
||||
gpsMarkerColorPreset: 'cyan',
|
||||
autoRotateEnabled: true,
|
||||
compassTuningProfile: 'balanced',
|
||||
northReferenceMode: 'magnetic',
|
||||
sideButtonPlacement: 'left',
|
||||
showCenterScaleRuler: false,
|
||||
centerScaleRulerAnchorMode: 'screen-center',
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTING_LOCKS: Record<SettingLockKey, boolean> = {
|
||||
lockAnimationLevel: false,
|
||||
lockTrackMode: false,
|
||||
lockTrackTailLength: false,
|
||||
lockTrackColor: false,
|
||||
lockTrackStyle: false,
|
||||
lockGpsMarkerVisible: false,
|
||||
lockGpsMarkerStyle: false,
|
||||
lockGpsMarkerSize: false,
|
||||
lockGpsMarkerColor: false,
|
||||
lockSideButtonPlacement: false,
|
||||
lockAutoRotate: false,
|
||||
lockCompassTuning: false,
|
||||
lockScaleRulerVisible: false,
|
||||
lockScaleRulerAnchor: false,
|
||||
lockNorthReference: false,
|
||||
lockHeartRateDevice: false,
|
||||
}
|
||||
|
||||
export const SETTING_LOCK_VALUE_MAP: Record<SettingLockKey, keyof StoredUserSettings | null> = {
|
||||
lockAnimationLevel: 'animationLevel',
|
||||
lockTrackMode: 'trackDisplayMode',
|
||||
lockTrackTailLength: 'trackTailLength',
|
||||
lockTrackColor: 'trackColorPreset',
|
||||
lockTrackStyle: 'trackStyleProfile',
|
||||
lockGpsMarkerVisible: 'gpsMarkerVisible',
|
||||
lockGpsMarkerStyle: 'gpsMarkerStyle',
|
||||
lockGpsMarkerSize: 'gpsMarkerSize',
|
||||
lockGpsMarkerColor: 'gpsMarkerColorPreset',
|
||||
lockSideButtonPlacement: 'sideButtonPlacement',
|
||||
lockAutoRotate: 'autoRotateEnabled',
|
||||
lockCompassTuning: 'compassTuningProfile',
|
||||
lockScaleRulerVisible: 'showCenterScaleRuler',
|
||||
lockScaleRulerAnchor: 'centerScaleRulerAnchorMode',
|
||||
lockNorthReference: 'northReferenceMode',
|
||||
lockHeartRateDevice: null,
|
||||
}
|
||||
|
||||
function normalizeStoredUserSettings(raw: unknown): StoredUserSettings {
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return {}
|
||||
}
|
||||
|
||||
const normalized = raw as Record<string, unknown>
|
||||
const settings: StoredUserSettings = {}
|
||||
if (normalized.animationLevel === 'standard' || normalized.animationLevel === 'lite') {
|
||||
settings.animationLevel = normalized.animationLevel
|
||||
}
|
||||
if (normalized.trackDisplayMode === 'none' || normalized.trackDisplayMode === 'full' || normalized.trackDisplayMode === 'tail') {
|
||||
settings.trackDisplayMode = normalized.trackDisplayMode
|
||||
}
|
||||
if (normalized.trackTailLength === 'short' || normalized.trackTailLength === 'medium' || normalized.trackTailLength === 'long') {
|
||||
settings.trackTailLength = normalized.trackTailLength
|
||||
}
|
||||
if (normalized.trackStyleProfile === 'classic' || normalized.trackStyleProfile === 'neon') {
|
||||
settings.trackStyleProfile = normalized.trackStyleProfile
|
||||
}
|
||||
if (typeof normalized.gpsMarkerVisible === 'boolean') {
|
||||
settings.gpsMarkerVisible = normalized.gpsMarkerVisible
|
||||
}
|
||||
if (
|
||||
normalized.gpsMarkerStyle === 'dot'
|
||||
|| normalized.gpsMarkerStyle === 'beacon'
|
||||
|| normalized.gpsMarkerStyle === 'disc'
|
||||
|| normalized.gpsMarkerStyle === 'badge'
|
||||
) {
|
||||
settings.gpsMarkerStyle = normalized.gpsMarkerStyle
|
||||
}
|
||||
if (normalized.gpsMarkerSize === 'small' || normalized.gpsMarkerSize === 'medium' || normalized.gpsMarkerSize === 'large') {
|
||||
settings.gpsMarkerSize = normalized.gpsMarkerSize
|
||||
}
|
||||
if (
|
||||
normalized.gpsMarkerColorPreset === 'mint'
|
||||
|| normalized.gpsMarkerColorPreset === 'cyan'
|
||||
|| normalized.gpsMarkerColorPreset === 'sky'
|
||||
|| normalized.gpsMarkerColorPreset === 'blue'
|
||||
|| normalized.gpsMarkerColorPreset === 'violet'
|
||||
|| normalized.gpsMarkerColorPreset === 'pink'
|
||||
|| normalized.gpsMarkerColorPreset === 'orange'
|
||||
|| normalized.gpsMarkerColorPreset === 'yellow'
|
||||
) {
|
||||
settings.gpsMarkerColorPreset = normalized.gpsMarkerColorPreset
|
||||
}
|
||||
if (
|
||||
normalized.trackColorPreset === 'mint'
|
||||
|| normalized.trackColorPreset === 'cyan'
|
||||
|| normalized.trackColorPreset === 'sky'
|
||||
|| normalized.trackColorPreset === 'blue'
|
||||
|| normalized.trackColorPreset === 'violet'
|
||||
|| normalized.trackColorPreset === 'pink'
|
||||
|| normalized.trackColorPreset === 'orange'
|
||||
|| normalized.trackColorPreset === 'yellow'
|
||||
) {
|
||||
settings.trackColorPreset = normalized.trackColorPreset
|
||||
}
|
||||
if (normalized.northReferenceMode === 'magnetic' || normalized.northReferenceMode === 'true') {
|
||||
settings.northReferenceMode = normalized.northReferenceMode
|
||||
}
|
||||
if (typeof normalized.autoRotateEnabled === 'boolean') {
|
||||
settings.autoRotateEnabled = normalized.autoRotateEnabled
|
||||
}
|
||||
if (normalized.compassTuningProfile === 'smooth' || normalized.compassTuningProfile === 'balanced' || normalized.compassTuningProfile === 'responsive') {
|
||||
settings.compassTuningProfile = normalized.compassTuningProfile
|
||||
}
|
||||
if (normalized.sideButtonPlacement === 'left' || normalized.sideButtonPlacement === 'right') {
|
||||
settings.sideButtonPlacement = normalized.sideButtonPlacement
|
||||
}
|
||||
if (typeof normalized.showCenterScaleRuler === 'boolean') {
|
||||
settings.showCenterScaleRuler = normalized.showCenterScaleRuler
|
||||
}
|
||||
if (normalized.centerScaleRulerAnchorMode === 'screen-center' || normalized.centerScaleRulerAnchorMode === 'compass-center') {
|
||||
settings.centerScaleRulerAnchorMode = normalized.centerScaleRulerAnchorMode
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
export function loadStoredUserSettings(storageKey = USER_SETTINGS_STORAGE_KEY): StoredUserSettings {
|
||||
try {
|
||||
return normalizeStoredUserSettings(wx.getStorageSync(storageKey))
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export function persistStoredUserSettings(
|
||||
settings: StoredUserSettings,
|
||||
storageKey = USER_SETTINGS_STORAGE_KEY,
|
||||
): void {
|
||||
try {
|
||||
wx.setStorageSync(storageKey, settings)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function mergeStoredUserSettings(
|
||||
current: StoredUserSettings,
|
||||
patch: Partial<StoredUserSettings>,
|
||||
): StoredUserSettings {
|
||||
return {
|
||||
...current,
|
||||
...patch,
|
||||
}
|
||||
}
|
||||
|
||||
export function buildInitialSystemSettingsState(
|
||||
stored: StoredUserSettings,
|
||||
config?: Partial<SystemSettingsConfig>,
|
||||
): ResolvedSystemSettingsState {
|
||||
const values = {
|
||||
...DEFAULT_STORED_USER_SETTINGS,
|
||||
...(config && config.values ? config.values : {}),
|
||||
}
|
||||
const locks = {
|
||||
...DEFAULT_SETTING_LOCKS,
|
||||
...(config && config.locks ? config.locks : {}),
|
||||
}
|
||||
|
||||
const resolvedValues: Required<StoredUserSettings> = {
|
||||
...values,
|
||||
}
|
||||
|
||||
for (const [lockKey, isLocked] of Object.entries(locks) as Array<[SettingLockKey, boolean]>) {
|
||||
const valueKey = SETTING_LOCK_VALUE_MAP[lockKey]
|
||||
if (!valueKey) {
|
||||
continue
|
||||
}
|
||||
if (!isLocked && stored[valueKey] !== undefined) {
|
||||
;(resolvedValues as Record<string, unknown>)[valueKey] = stored[valueKey]
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(stored) as Array<[keyof StoredUserSettings, StoredUserSettings[keyof StoredUserSettings]]>) {
|
||||
const matchingLockKey = (Object.keys(SETTING_LOCK_VALUE_MAP) as SettingLockKey[])
|
||||
.find((lockKey) => SETTING_LOCK_VALUE_MAP[lockKey] === key)
|
||||
if (matchingLockKey && locks[matchingLockKey]) {
|
||||
continue
|
||||
}
|
||||
if (value !== undefined) {
|
||||
;(resolvedValues as Record<string, unknown>)[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
values: resolvedValues,
|
||||
locks,
|
||||
}
|
||||
}
|
||||
|
||||
export function buildRuntimeSettingLocks(
|
||||
locks: Partial<Record<SettingLockKey, boolean>> | undefined,
|
||||
runtimeActive: boolean,
|
||||
): Partial<Record<SettingLockKey, boolean>> {
|
||||
const sourceLocks = locks || {}
|
||||
if (runtimeActive) {
|
||||
return { ...sourceLocks }
|
||||
}
|
||||
|
||||
const unlocked: Partial<Record<SettingLockKey, boolean>> = {}
|
||||
for (const key of Object.keys(sourceLocks) as SettingLockKey[]) {
|
||||
unlocked[key] = false
|
||||
}
|
||||
return unlocked
|
||||
}
|
||||
|
||||
export function resolveSystemSettingsState(
|
||||
config?: Partial<SystemSettingsConfig>,
|
||||
storageKey = USER_SETTINGS_STORAGE_KEY,
|
||||
runtimeActive = false,
|
||||
): ResolvedSystemSettingsState {
|
||||
return buildInitialSystemSettingsState(
|
||||
loadStoredUserSettings(storageKey),
|
||||
{
|
||||
values: config && config.values ? config.values : {},
|
||||
locks: buildRuntimeSettingLocks(config && config.locks ? config.locks : {}, runtimeActive),
|
||||
},
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user