Add config-driven game host updates

This commit is contained in:
2026-03-25 13:58:51 +08:00
parent f0ced54805
commit d1cc6cc473
28 changed files with 3247 additions and 105 deletions

View File

@@ -1,4 +1,10 @@
import { MapEngine, type MapEngineStageRect, type MapEngineViewState } from '../../engine/map/mapEngine'
import {
MapEngine,
type MapEngineGameInfoRow,
type MapEngineGameInfoSnapshot,
type MapEngineStageRect,
type MapEngineViewState,
} from '../../engine/map/mapEngine'
import { loadRemoteMapConfig } from '../../utils/remoteMapConfig'
type CompassTickData = {
angle: number
@@ -13,13 +19,20 @@ type CompassLabelData = {
className: string
}
type SideButtonMode = 'all' | 'left' | 'right' | 'hidden'
type SideActionButtonState = 'muted' | 'default' | 'active'
type MapPageData = MapEngineViewState & {
showDebugPanel: boolean
showGameInfoPanel: boolean
statusBarHeight: number
topInsetHeight: number
hudPanelIndex: number
configSourceText: string
mockBridgeUrlDraft: string
mockHeartRateBridgeUrlDraft: string
gameInfoTitle: string
gameInfoSubtitle: string
gameInfoLocalRows: MapEngineGameInfoRow[]
gameInfoGlobalRows: MapEngineGameInfoRow[]
panelTimerText: string
panelMileageText: string
panelDistanceValueText: string
@@ -28,12 +41,17 @@ type MapPageData = MapEngineViewState & {
compassTicks: CompassTickData[]
compassLabels: CompassLabelData[]
sideButtonMode: SideButtonMode
sideToggleIconSrc: string
sideButton4Class: string
sideButton11Class: string
sideButton16Class: string
showLeftButtonGroup: boolean
showRightButtonGroups: boolean
showBottomDebugButton: boolean
}
const INTERNAL_BUILD_VERSION = 'map-build-196'
const REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/wxmini/test/game.json'
const INTERNAL_BUILD_VERSION = 'map-build-207'
const CLASSIC_REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/gotomars/event/classic-sequential.json'
const SCORE_O_REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/gotomars/event/score-o.json'
let mapEngine: MapEngine | null = null
let stageCanvasAttached = false
function buildSideButtonVisibility(mode: SideButtonMode) {
@@ -93,12 +111,66 @@ function getFallbackStageRect(): MapEngineStageRect {
}
}
function getSideToggleIconSrc(mode: SideButtonMode): string {
if (mode === 'left') {
return '../../assets/btn_more2.png'
}
if (mode === 'hidden') {
return '../../assets/btn_more1.png'
}
return '../../assets/btn_more3.png'
}
function getSideActionButtonClass(state: SideActionButtonState): string {
if (state === 'muted') {
return 'map-side-button map-side-button--muted'
}
if (state === 'active') {
return 'map-side-button map-side-button--active'
}
return 'map-side-button map-side-button--default'
}
function buildSideButtonState(data: Pick<MapPageData, 'sideButtonMode' | 'showGameInfoPanel' | 'skipButtonEnabled' | 'gameSessionStatus'>) {
const sideButton4State: SideActionButtonState = data.gameSessionStatus === 'idle' ? 'default' : 'active'
const sideButton11State: SideActionButtonState = data.showGameInfoPanel ? 'active' : 'default'
const sideButton16State: SideActionButtonState = data.skipButtonEnabled ? 'default' : 'muted'
return {
sideToggleIconSrc: getSideToggleIconSrc(data.sideButtonMode),
sideButton4Class: getSideActionButtonClass(sideButton4State),
sideButton11Class: getSideActionButtonClass(sideButton11State),
sideButton16Class: getSideActionButtonClass(sideButton16State),
}
}
function buildEmptyGameInfoSnapshot(): MapEngineGameInfoSnapshot {
return {
title: '当前游戏',
subtitle: '未开始',
localRows: [],
globalRows: [
{ label: '全球积分', value: '未接入' },
{ label: '全球排名', value: '未接入' },
{ label: '在线人数', value: '未接入' },
{ label: '队伍状态', value: '未接入' },
{ label: '实时广播', value: '未接入' },
],
}
}
Page({
data: {
showDebugPanel: false,
showGameInfoPanel: false,
statusBarHeight: 0,
topInsetHeight: 12,
hudPanelIndex: 0,
configSourceText: '顺序赛配置',
gameInfoTitle: '当前游戏',
gameInfoSubtitle: '未开始',
gameInfoLocalRows: [],
gameInfoGlobalRows: buildEmptyGameInfoSnapshot().globalRows,
panelTimerText: '00:00:00',
panelMileageText: '0m',
panelActionTagText: '目标',
@@ -142,6 +214,7 @@ Page({
panelAccuracyUnitText: '',
punchButtonText: '打点',
punchButtonEnabled: false,
skipButtonEnabled: false,
punchHintText: '等待进入检查点范围',
punchFeedbackVisible: false,
punchFeedbackText: '',
@@ -161,6 +234,12 @@ Page({
compassTicks: buildCompassTicks(),
compassLabels: buildCompassLabels(),
...buildSideButtonVisibility('left'),
...buildSideButtonState({
sideButtonMode: 'left',
showGameInfoPanel: false,
skipButtonEnabled: false,
gameSessionStatus: 'idle',
}),
} as unknown as MapPageData,
onLoad() {
@@ -190,16 +269,34 @@ Page({
nextData.mockHeartRateBridgeUrlDraft = nextPatch.mockHeartRateBridgeUrlText
}
this.setData(nextData)
const mergedData = {
...this.data,
...nextData,
} as MapPageData
this.setData({
...nextData,
...buildSideButtonState(mergedData),
})
if (this.data.showGameInfoPanel) {
this.syncGameInfoPanelSnapshot()
}
},
})
this.setData({
...mapEngine.getInitialData(),
showDebugPanel: false,
showGameInfoPanel: false,
statusBarHeight,
topInsetHeight: Math.max(statusBarHeight + 12, menuButtonBottom + 20),
hudPanelIndex: 0,
configSourceText: '顺序赛配置',
gameInfoTitle: '当前游戏',
gameInfoSubtitle: '未开始',
gameInfoLocalRows: [],
gameInfoGlobalRows: buildEmptyGameInfoSnapshot().globalRows,
panelTimerText: '00:00:00',
panelMileageText: '0m',
panelActionTagText: '目标',
@@ -241,6 +338,7 @@ Page({
panelAccuracyUnitText: '',
punchButtonText: '打点',
punchButtonEnabled: false,
skipButtonEnabled: false,
punchHintText: '等待进入检查点范围',
punchFeedbackVisible: false,
punchFeedbackText: '',
@@ -260,13 +358,19 @@ Page({
compassTicks: buildCompassTicks(),
compassLabels: buildCompassLabels(),
...buildSideButtonVisibility('left'),
...buildSideButtonState({
sideButtonMode: 'left',
showGameInfoPanel: false,
skipButtonEnabled: false,
gameSessionStatus: 'idle',
}),
})
},
onReady() {
stageCanvasAttached = false
this.measureStageAndCanvas()
this.loadMapConfigFromRemote()
this.loadMapConfigFromRemote(CLASSIC_REMOTE_GAME_CONFIG_URL, '顺序赛配置')
},
onShow() {
@@ -289,13 +393,18 @@ Page({
stageCanvasAttached = false
},
loadMapConfigFromRemote() {
loadMapConfigFromRemote(configUrl: string, configLabel: string) {
const currentEngine = mapEngine
if (!currentEngine) {
return
}
loadRemoteMapConfig(REMOTE_GAME_CONFIG_URL)
this.setData({
configSourceText: configLabel,
configStatusText: `加载中: ${configLabel}`,
})
loadRemoteMapConfig(configUrl)
.then((config) => {
if (mapEngine !== currentEngine) {
return
@@ -605,16 +714,41 @@ Page({
}
},
handleSetClassicMode() {
handleLoadClassicConfig() {
this.loadMapConfigFromRemote(CLASSIC_REMOTE_GAME_CONFIG_URL, '顺序赛配置')
},
handleLoadScoreOConfig() {
this.loadMapConfigFromRemote(SCORE_O_REMOTE_GAME_CONFIG_URL, '积分赛配置')
},
handleForceExitGame() {
if (mapEngine) {
mapEngine.handleSetGameMode('classic-sequential')
mapEngine.handleForceExitGame()
}
},
handleSetScoreOMode() {
if (mapEngine) {
mapEngine.handleSetGameMode('score-o')
handleSkipAction() {
if (!mapEngine || !this.data.skipButtonEnabled) {
return
}
if (!mapEngine.shouldConfirmSkipAction()) {
mapEngine.handleSkipAction()
return
}
wx.showModal({
title: '确认跳点',
content: '确认跳过当前检查点并切换到下一个目标点?',
confirmText: '确认跳过',
cancelText: '取消',
success: (result) => {
if (result.confirm && mapEngine) {
mapEngine.handleSkipAction()
}
},
})
},
handleClearMapTestArtifacts() {
@@ -623,6 +757,48 @@ Page({
}
},
syncGameInfoPanelSnapshot() {
if (!mapEngine) {
return
}
const snapshot = mapEngine.getGameInfoSnapshot()
this.setData({
gameInfoTitle: snapshot.title,
gameInfoSubtitle: snapshot.subtitle,
gameInfoLocalRows: snapshot.localRows,
gameInfoGlobalRows: snapshot.globalRows,
})
},
handleOpenGameInfoPanel() {
this.syncGameInfoPanelSnapshot()
this.setData({
showDebugPanel: false,
showGameInfoPanel: true,
...buildSideButtonState({
sideButtonMode: this.data.sideButtonMode,
showGameInfoPanel: true,
skipButtonEnabled: this.data.skipButtonEnabled,
gameSessionStatus: this.data.gameSessionStatus,
}),
})
},
handleCloseGameInfoPanel() {
this.setData({
showGameInfoPanel: false,
...buildSideButtonState({
sideButtonMode: this.data.sideButtonMode,
showGameInfoPanel: false,
skipButtonEnabled: this.data.skipButtonEnabled,
gameSessionStatus: this.data.gameSessionStatus,
}),
})
},
handleGameInfoPanelTap() {},
handleOverlayTouch() {},
handlePunchAction() {
@@ -648,7 +824,16 @@ Page({
},
handleCycleSideButtons() {
this.setData(buildSideButtonVisibility(getNextSideButtonMode(this.data.sideButtonMode)))
const nextMode = getNextSideButtonMode(this.data.sideButtonMode)
this.setData({
...buildSideButtonVisibility(nextMode),
...buildSideButtonState({
sideButtonMode: nextMode,
showGameInfoPanel: this.data.showGameInfoPanel,
skipButtonEnabled: this.data.skipButtonEnabled,
gameSessionStatus: this.data.gameSessionStatus,
}),
})
},
handleToggleMapRotateMode() {
if (!mapEngine) {
@@ -665,12 +850,25 @@ Page({
handleToggleDebugPanel() {
this.setData({
showDebugPanel: !this.data.showDebugPanel,
showGameInfoPanel: false,
...buildSideButtonState({
sideButtonMode: this.data.sideButtonMode,
showGameInfoPanel: false,
skipButtonEnabled: this.data.skipButtonEnabled,
gameSessionStatus: this.data.gameSessionStatus,
}),
})
},
handleCloseDebugPanel() {
this.setData({
showDebugPanel: false,
...buildSideButtonState({
sideButtonMode: this.data.sideButtonMode,
showGameInfoPanel: this.data.showGameInfoPanel,
skipButtonEnabled: this.data.skipButtonEnabled,
gameSessionStatus: this.data.gameSessionStatus,
}),
})
},