964 lines
26 KiB
TypeScript
964 lines
26 KiB
TypeScript
import {
|
|
MapEngine,
|
|
type MapEngineGameInfoRow,
|
|
type MapEngineGameInfoSnapshot,
|
|
type MapEngineStageRect,
|
|
type MapEngineViewState,
|
|
} from '../../engine/map/mapEngine'
|
|
import { loadRemoteMapConfig } from '../../utils/remoteMapConfig'
|
|
type CompassTickData = {
|
|
angle: number
|
|
long: boolean
|
|
major: boolean
|
|
}
|
|
type CompassLabelData = {
|
|
text: string
|
|
angle: number
|
|
rotateBack: number
|
|
radius: number
|
|
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
|
|
panelProgressText: string
|
|
panelSpeedValueText: string
|
|
compassTicks: CompassTickData[]
|
|
compassLabels: CompassLabelData[]
|
|
sideButtonMode: SideButtonMode
|
|
sideToggleIconSrc: string
|
|
sideButton2Class: string
|
|
sideButton4Class: string
|
|
sideButton11Class: string
|
|
sideButton16Class: string
|
|
showLeftButtonGroup: boolean
|
|
showRightButtonGroups: boolean
|
|
showBottomDebugButton: boolean
|
|
}
|
|
const INTERNAL_BUILD_VERSION = 'map-build-213'
|
|
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) {
|
|
return {
|
|
sideButtonMode: mode,
|
|
showLeftButtonGroup: mode === 'all' || mode === 'left' || mode === 'right',
|
|
showRightButtonGroups: mode === 'all' || mode === 'right',
|
|
showBottomDebugButton: mode !== 'hidden',
|
|
}
|
|
}
|
|
|
|
function getNextSideButtonMode(currentMode: SideButtonMode): SideButtonMode {
|
|
if (currentMode === 'all') {
|
|
return 'left'
|
|
}
|
|
if (currentMode === 'left') {
|
|
return 'right'
|
|
}
|
|
if (currentMode === 'right') {
|
|
return 'hidden'
|
|
}
|
|
return 'left'
|
|
}
|
|
function buildCompassTicks(): CompassTickData[] {
|
|
const ticks: CompassTickData[] = []
|
|
for (let angle = 0; angle < 360; angle += 5) {
|
|
ticks.push({
|
|
angle,
|
|
long: angle % 15 === 0,
|
|
major: angle % 45 === 0,
|
|
})
|
|
}
|
|
return ticks
|
|
}
|
|
function buildCompassLabels(): CompassLabelData[] {
|
|
return [
|
|
{ text: '\u5317', angle: 0, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal compass-widget__mark--north' },
|
|
{ text: '\u4e1c\u5317', angle: 45, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northeast' },
|
|
{ text: '\u4e1c', angle: 90, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
|
|
{ text: '\u4e1c\u5357', angle: 135, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
|
|
{ text: '\u5357', angle: 180, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
|
|
{ text: '\u897f\u5357', angle: 225, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
|
|
{ text: '\u897f', angle: 270, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
|
|
{ text: '\u897f\u5317', angle: 315, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northwest' },
|
|
]
|
|
}
|
|
function getFallbackStageRect(): MapEngineStageRect {
|
|
const systemInfo = wx.getSystemInfoSync()
|
|
const width = Math.max(320, systemInfo.windowWidth)
|
|
const height = Math.max(280, systemInfo.windowHeight)
|
|
|
|
return {
|
|
width,
|
|
height,
|
|
left: 0,
|
|
top: 0,
|
|
}
|
|
}
|
|
|
|
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' | 'gpsLockEnabled' | 'gpsLockAvailable'>) {
|
|
const sideButton2State: SideActionButtonState = !data.gpsLockAvailable
|
|
? 'muted'
|
|
: data.gpsLockEnabled
|
|
? 'active'
|
|
: 'default'
|
|
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),
|
|
sideButton2Class: getSideActionButtonClass(sideButton2State),
|
|
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: '目标',
|
|
panelDistanceTagText: '点距',
|
|
panelDistanceValueText: '--',
|
|
panelDistanceUnitText: '',
|
|
panelProgressText: '0/0',
|
|
gameSessionStatus: 'idle',
|
|
gameModeText: '顺序赛',
|
|
gpsLockEnabled: false,
|
|
gpsLockAvailable: false,
|
|
locationSourceMode: 'real',
|
|
locationSourceText: '真实定位',
|
|
mockBridgeConnected: false,
|
|
mockBridgeStatusText: '未连接',
|
|
mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockCoordText: '--',
|
|
mockSpeedText: '--',
|
|
heartRateSourceMode: 'real',
|
|
heartRateSourceText: '真实心率',
|
|
mockHeartRateBridgeConnected: false,
|
|
mockHeartRateBridgeStatusText: '未连接',
|
|
mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockHeartRateBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockHeartRateText: '--',
|
|
heartRateScanText: '未扫描',
|
|
heartRateDiscoveredDevices: [],
|
|
panelSpeedValueText: '0',
|
|
panelTelemetryTone: 'blue',
|
|
panelHeartRateZoneNameText: '--',
|
|
panelHeartRateZoneRangeText: '',
|
|
heartRateConnected: false,
|
|
heartRateStatusText: '心率带未连接',
|
|
heartRateDeviceText: '--',
|
|
panelHeartRateValueText: '--',
|
|
panelHeartRateUnitText: '',
|
|
panelCaloriesValueText: '0',
|
|
panelCaloriesUnitText: 'kcal',
|
|
panelAverageSpeedValueText: '0',
|
|
panelAverageSpeedUnitText: 'km/h',
|
|
panelAccuracyValueText: '--',
|
|
panelAccuracyUnitText: '',
|
|
punchButtonText: '打点',
|
|
punchButtonEnabled: false,
|
|
skipButtonEnabled: false,
|
|
punchHintText: '等待进入检查点范围',
|
|
punchFeedbackVisible: false,
|
|
punchFeedbackText: '',
|
|
punchFeedbackTone: 'neutral',
|
|
contentCardVisible: false,
|
|
contentCardTitle: '',
|
|
contentCardBody: '',
|
|
punchButtonFxClass: '',
|
|
punchFeedbackFxClass: '',
|
|
contentCardFxClass: '',
|
|
mapPulseVisible: false,
|
|
mapPulseLeftPx: 0,
|
|
mapPulseTopPx: 0,
|
|
mapPulseFxClass: '',
|
|
stageFxVisible: false,
|
|
stageFxClass: '',
|
|
compassTicks: buildCompassTicks(),
|
|
compassLabels: buildCompassLabels(),
|
|
...buildSideButtonVisibility('left'),
|
|
...buildSideButtonState({
|
|
sideButtonMode: 'left',
|
|
showGameInfoPanel: false,
|
|
skipButtonEnabled: false,
|
|
gameSessionStatus: 'idle',
|
|
gpsLockEnabled: false,
|
|
gpsLockAvailable: false,
|
|
}),
|
|
} as unknown as MapPageData,
|
|
|
|
onLoad() {
|
|
const systemInfo = wx.getSystemInfoSync()
|
|
const statusBarHeight = systemInfo.statusBarHeight || 0
|
|
const menuButtonRect = wx.getMenuButtonBoundingClientRect()
|
|
const menuButtonBottom = menuButtonRect && typeof menuButtonRect.bottom === 'number' ? menuButtonRect.bottom : statusBarHeight
|
|
|
|
mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, {
|
|
onData: (patch) => {
|
|
const nextPatch = patch as Partial<MapPageData>
|
|
const nextData: Partial<MapPageData> = {
|
|
...nextPatch,
|
|
}
|
|
|
|
if (
|
|
typeof nextPatch.mockBridgeUrlText === 'string'
|
|
&& this.data.mockBridgeUrlDraft === this.data.mockBridgeUrlText
|
|
) {
|
|
nextData.mockBridgeUrlDraft = nextPatch.mockBridgeUrlText
|
|
}
|
|
|
|
if (
|
|
typeof nextPatch.mockHeartRateBridgeUrlText === 'string'
|
|
&& this.data.mockHeartRateBridgeUrlDraft === this.data.mockHeartRateBridgeUrlText
|
|
) {
|
|
nextData.mockHeartRateBridgeUrlDraft = nextPatch.mockHeartRateBridgeUrlText
|
|
}
|
|
|
|
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: '目标',
|
|
panelDistanceTagText: '点距',
|
|
panelDistanceValueText: '--',
|
|
panelDistanceUnitText: '',
|
|
panelProgressText: '0/0',
|
|
gameSessionStatus: 'idle',
|
|
gameModeText: '顺序赛',
|
|
gpsLockEnabled: false,
|
|
gpsLockAvailable: false,
|
|
locationSourceMode: 'real',
|
|
locationSourceText: '真实定位',
|
|
mockBridgeConnected: false,
|
|
mockBridgeStatusText: '未连接',
|
|
mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockCoordText: '--',
|
|
mockSpeedText: '--',
|
|
heartRateSourceMode: 'real',
|
|
heartRateSourceText: '真实心率',
|
|
mockHeartRateBridgeConnected: false,
|
|
mockHeartRateBridgeStatusText: '未连接',
|
|
mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockHeartRateBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
|
|
mockHeartRateText: '--',
|
|
panelSpeedValueText: '0',
|
|
panelTelemetryTone: 'blue',
|
|
panelHeartRateZoneNameText: '--',
|
|
panelHeartRateZoneRangeText: '',
|
|
heartRateConnected: false,
|
|
heartRateStatusText: '心率带未连接',
|
|
heartRateDeviceText: '--',
|
|
panelHeartRateValueText: '--',
|
|
panelHeartRateUnitText: '',
|
|
panelCaloriesValueText: '0',
|
|
panelCaloriesUnitText: 'kcal',
|
|
panelAverageSpeedValueText: '0',
|
|
panelAverageSpeedUnitText: 'km/h',
|
|
panelAccuracyValueText: '--',
|
|
panelAccuracyUnitText: '',
|
|
punchButtonText: '打点',
|
|
punchButtonEnabled: false,
|
|
skipButtonEnabled: false,
|
|
punchHintText: '等待进入检查点范围',
|
|
punchFeedbackVisible: false,
|
|
punchFeedbackText: '',
|
|
punchFeedbackTone: 'neutral',
|
|
contentCardVisible: false,
|
|
contentCardTitle: '',
|
|
contentCardBody: '',
|
|
punchButtonFxClass: '',
|
|
punchFeedbackFxClass: '',
|
|
contentCardFxClass: '',
|
|
mapPulseVisible: false,
|
|
mapPulseLeftPx: 0,
|
|
mapPulseTopPx: 0,
|
|
mapPulseFxClass: '',
|
|
stageFxVisible: false,
|
|
stageFxClass: '',
|
|
compassTicks: buildCompassTicks(),
|
|
compassLabels: buildCompassLabels(),
|
|
...buildSideButtonVisibility('left'),
|
|
...buildSideButtonState({
|
|
sideButtonMode: 'left',
|
|
showGameInfoPanel: false,
|
|
skipButtonEnabled: false,
|
|
gameSessionStatus: 'idle',
|
|
gpsLockEnabled: false,
|
|
gpsLockAvailable: false,
|
|
}),
|
|
})
|
|
},
|
|
|
|
onReady() {
|
|
stageCanvasAttached = false
|
|
this.measureStageAndCanvas()
|
|
this.loadMapConfigFromRemote(CLASSIC_REMOTE_GAME_CONFIG_URL, '顺序赛配置')
|
|
},
|
|
|
|
onShow() {
|
|
if (mapEngine) {
|
|
mapEngine.handleAppShow()
|
|
}
|
|
},
|
|
|
|
onHide() {
|
|
if (mapEngine) {
|
|
mapEngine.handleAppHide()
|
|
}
|
|
},
|
|
|
|
onUnload() {
|
|
if (mapEngine) {
|
|
mapEngine.destroy()
|
|
mapEngine = null
|
|
}
|
|
stageCanvasAttached = false
|
|
},
|
|
|
|
loadMapConfigFromRemote(configUrl: string, configLabel: string) {
|
|
const currentEngine = mapEngine
|
|
if (!currentEngine) {
|
|
return
|
|
}
|
|
|
|
this.setData({
|
|
configSourceText: configLabel,
|
|
configStatusText: `加载中: ${configLabel}`,
|
|
})
|
|
|
|
loadRemoteMapConfig(configUrl)
|
|
.then((config) => {
|
|
if (mapEngine !== currentEngine) {
|
|
return
|
|
}
|
|
|
|
currentEngine.applyRemoteMapConfig(config)
|
|
})
|
|
.catch((error) => {
|
|
if (mapEngine !== currentEngine) {
|
|
return
|
|
}
|
|
|
|
const errorMessage = error && error.message ? error.message : '未知错误'
|
|
this.setData({
|
|
configStatusText: `载入失败: ${errorMessage}`,
|
|
statusText: `远程地图配置载入失败: ${errorMessage} (${INTERNAL_BUILD_VERSION})`,
|
|
})
|
|
})
|
|
},
|
|
|
|
measureStageAndCanvas() {
|
|
const page = this
|
|
const applyStage = (rawRect?: Partial<WechatMiniprogram.BoundingClientRectCallbackResult>) => {
|
|
const fallbackRect = getFallbackStageRect()
|
|
const rect: MapEngineStageRect = {
|
|
width: rawRect && typeof rawRect.width === 'number' ? rawRect.width : fallbackRect.width,
|
|
height: rawRect && typeof rawRect.height === 'number' ? rawRect.height : fallbackRect.height,
|
|
left: rawRect && typeof rawRect.left === 'number' ? rawRect.left : fallbackRect.left,
|
|
top: rawRect && typeof rawRect.top === 'number' ? rawRect.top : fallbackRect.top,
|
|
}
|
|
|
|
const currentEngine = mapEngine
|
|
if (!currentEngine) {
|
|
return
|
|
}
|
|
|
|
currentEngine.setStage(rect)
|
|
|
|
if (stageCanvasAttached) {
|
|
return
|
|
}
|
|
|
|
const canvasQuery = wx.createSelectorQuery().in(page)
|
|
canvasQuery.select('#mapCanvas').fields({ node: true, size: true })
|
|
canvasQuery.select('#routeLabelCanvas').fields({ node: true, size: true })
|
|
canvasQuery.exec((canvasRes) => {
|
|
const canvasRef = canvasRes[0] as any
|
|
const labelCanvasRef = canvasRes[1] as any
|
|
if (!canvasRef || !canvasRef.node) {
|
|
page.setData({
|
|
statusText: `WebGL 引擎初始化失败 (${INTERNAL_BUILD_VERSION})`,
|
|
})
|
|
return
|
|
}
|
|
|
|
const dpr = wx.getSystemInfoSync().pixelRatio || 1
|
|
try {
|
|
currentEngine.attachCanvas(
|
|
canvasRef.node,
|
|
rect.width,
|
|
rect.height,
|
|
dpr,
|
|
labelCanvasRef && labelCanvasRef.node ? labelCanvasRef.node : undefined,
|
|
)
|
|
stageCanvasAttached = true
|
|
} catch (error) {
|
|
page.setData({
|
|
statusText: `WebGL 鍒濆鍖栧け璐?(${INTERNAL_BUILD_VERSION})`,
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
const query = wx.createSelectorQuery().in(page)
|
|
query.select('.map-stage').boundingClientRect()
|
|
query.exec((res) => {
|
|
const rect = res[0] as WechatMiniprogram.BoundingClientRectCallbackResult | undefined
|
|
applyStage(rect)
|
|
})
|
|
},
|
|
|
|
handleTouchStart(event: WechatMiniprogram.TouchEvent) {
|
|
if (mapEngine) {
|
|
mapEngine.handleTouchStart(event)
|
|
}
|
|
},
|
|
|
|
handleTouchMove(event: WechatMiniprogram.TouchEvent) {
|
|
if (mapEngine) {
|
|
mapEngine.handleTouchMove(event)
|
|
}
|
|
},
|
|
|
|
handleTouchEnd(event: WechatMiniprogram.TouchEvent) {
|
|
if (mapEngine) {
|
|
mapEngine.handleTouchEnd(event)
|
|
}
|
|
},
|
|
|
|
handleTouchCancel() {
|
|
if (mapEngine) {
|
|
mapEngine.handleTouchCancel()
|
|
}
|
|
},
|
|
|
|
handleRecenter() {
|
|
if (mapEngine) {
|
|
mapEngine.handleRecenter()
|
|
}
|
|
},
|
|
|
|
handleRotateStep() {
|
|
if (mapEngine) {
|
|
mapEngine.handleRotateStep()
|
|
}
|
|
},
|
|
|
|
handleRotationReset() {
|
|
if (mapEngine) {
|
|
mapEngine.handleRotationReset()
|
|
}
|
|
},
|
|
|
|
handleSetManualMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetManualMode()
|
|
}
|
|
},
|
|
|
|
handleSetNorthUpMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetNorthUpMode()
|
|
}
|
|
},
|
|
|
|
handleSetHeadingUpMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetHeadingUpMode()
|
|
}
|
|
},
|
|
|
|
handleCycleNorthReferenceMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleCycleNorthReferenceMode()
|
|
}
|
|
},
|
|
|
|
handleAutoRotateCalibrate() {
|
|
if (mapEngine) {
|
|
mapEngine.handleAutoRotateCalibrate()
|
|
}
|
|
},
|
|
|
|
handleToggleGpsTracking() {
|
|
if (mapEngine) {
|
|
mapEngine.handleToggleGpsTracking()
|
|
}
|
|
},
|
|
|
|
handleSetRealLocationMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetRealLocationMode()
|
|
}
|
|
},
|
|
|
|
handleSetMockLocationMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetMockLocationMode()
|
|
}
|
|
},
|
|
|
|
handleConnectMockLocationBridge() {
|
|
if (mapEngine) {
|
|
mapEngine.handleConnectMockLocationBridge()
|
|
}
|
|
},
|
|
|
|
handleMockBridgeUrlInput(event: WechatMiniprogram.Input) {
|
|
this.setData({
|
|
mockBridgeUrlDraft: event.detail.value,
|
|
})
|
|
},
|
|
|
|
handleSaveMockBridgeUrl() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetMockLocationBridgeUrl(this.data.mockBridgeUrlDraft)
|
|
}
|
|
},
|
|
|
|
handleDisconnectMockLocationBridge() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDisconnectMockLocationBridge()
|
|
}
|
|
},
|
|
|
|
handleSetRealHeartRateMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetRealHeartRateMode()
|
|
}
|
|
},
|
|
|
|
handleSetMockHeartRateMode() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetMockHeartRateMode()
|
|
}
|
|
},
|
|
|
|
handleMockHeartRateBridgeUrlInput(event: WechatMiniprogram.Input) {
|
|
this.setData({
|
|
mockHeartRateBridgeUrlDraft: event.detail.value,
|
|
})
|
|
},
|
|
|
|
handleSaveMockHeartRateBridgeUrl() {
|
|
if (mapEngine) {
|
|
mapEngine.handleSetMockHeartRateBridgeUrl(this.data.mockHeartRateBridgeUrlDraft)
|
|
}
|
|
},
|
|
|
|
handleConnectMockHeartRateBridge() {
|
|
if (mapEngine) {
|
|
mapEngine.handleConnectMockHeartRateBridge()
|
|
}
|
|
},
|
|
|
|
handleDisconnectMockHeartRateBridge() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDisconnectMockHeartRateBridge()
|
|
}
|
|
},
|
|
|
|
handleConnectHeartRate() {
|
|
if (mapEngine) {
|
|
mapEngine.handleConnectHeartRate()
|
|
}
|
|
},
|
|
|
|
handleDisconnectHeartRate() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDisconnectHeartRate()
|
|
}
|
|
},
|
|
|
|
handleConnectHeartRateDevice(event: WechatMiniprogram.BaseEvent<{ deviceId?: string }>) {
|
|
if (mapEngine && event.currentTarget && event.currentTarget.dataset && event.currentTarget.dataset.deviceId) {
|
|
mapEngine.handleConnectHeartRateDevice(event.currentTarget.dataset.deviceId)
|
|
}
|
|
},
|
|
|
|
handleClearPreferredHeartRateDevice() {
|
|
if (mapEngine) {
|
|
mapEngine.handleClearPreferredHeartRateDevice()
|
|
}
|
|
},
|
|
|
|
handleDebugHeartRateBlue() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDebugHeartRateTone('blue')
|
|
}
|
|
},
|
|
|
|
handleDebugHeartRatePurple() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDebugHeartRateTone('purple')
|
|
}
|
|
},
|
|
|
|
handleDebugHeartRateGreen() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDebugHeartRateTone('green')
|
|
}
|
|
},
|
|
|
|
handleDebugHeartRateYellow() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDebugHeartRateTone('yellow')
|
|
}
|
|
},
|
|
|
|
handleDebugHeartRateOrange() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDebugHeartRateTone('orange')
|
|
}
|
|
},
|
|
|
|
handleDebugHeartRateRed() {
|
|
if (mapEngine) {
|
|
mapEngine.handleDebugHeartRateTone('red')
|
|
}
|
|
},
|
|
|
|
handleClearDebugHeartRate() {
|
|
if (mapEngine) {
|
|
mapEngine.handleClearDebugHeartRate()
|
|
}
|
|
},
|
|
|
|
handleToggleOsmReference() {
|
|
if (mapEngine) {
|
|
mapEngine.handleToggleOsmReference()
|
|
}
|
|
},
|
|
|
|
handleStartGame() {
|
|
if (mapEngine) {
|
|
mapEngine.handleStartGame()
|
|
}
|
|
},
|
|
|
|
handleLoadClassicConfig() {
|
|
this.loadMapConfigFromRemote(CLASSIC_REMOTE_GAME_CONFIG_URL, '顺序赛配置')
|
|
},
|
|
|
|
handleLoadScoreOConfig() {
|
|
this.loadMapConfigFromRemote(SCORE_O_REMOTE_GAME_CONFIG_URL, '积分赛配置')
|
|
},
|
|
|
|
handleForceExitGame() {
|
|
if (!mapEngine || this.data.gameSessionStatus === 'idle') {
|
|
return
|
|
}
|
|
|
|
wx.showModal({
|
|
title: '确认退出',
|
|
content: '确认强制结束当前对局并返回开始前状态?',
|
|
confirmText: '确认退出',
|
|
cancelText: '取消',
|
|
success: (result) => {
|
|
if (result.confirm && mapEngine) {
|
|
mapEngine.handleForceExitGame()
|
|
}
|
|
},
|
|
})
|
|
},
|
|
|
|
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() {
|
|
if (mapEngine) {
|
|
mapEngine.handleClearMapTestArtifacts()
|
|
}
|
|
},
|
|
|
|
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,
|
|
gpsLockEnabled: this.data.gpsLockEnabled,
|
|
gpsLockAvailable: this.data.gpsLockAvailable,
|
|
}),
|
|
})
|
|
},
|
|
|
|
handleCloseGameInfoPanel() {
|
|
this.setData({
|
|
showGameInfoPanel: false,
|
|
...buildSideButtonState({
|
|
sideButtonMode: this.data.sideButtonMode,
|
|
showGameInfoPanel: false,
|
|
skipButtonEnabled: this.data.skipButtonEnabled,
|
|
gameSessionStatus: this.data.gameSessionStatus,
|
|
gpsLockEnabled: this.data.gpsLockEnabled,
|
|
gpsLockAvailable: this.data.gpsLockAvailable,
|
|
}),
|
|
})
|
|
},
|
|
|
|
handleGameInfoPanelTap() {},
|
|
|
|
handleOverlayTouch() {},
|
|
|
|
handlePunchAction() {
|
|
if (!this.data.punchButtonEnabled) {
|
|
return
|
|
}
|
|
|
|
if (mapEngine) {
|
|
mapEngine.handlePunchAction()
|
|
}
|
|
},
|
|
|
|
handleCloseContentCard() {
|
|
if (mapEngine) {
|
|
mapEngine.closeContentCard()
|
|
}
|
|
},
|
|
|
|
handleHudPanelChange(event: WechatMiniprogram.CustomEvent<{ current: number }>) {
|
|
this.setData({
|
|
hudPanelIndex: event.detail.current || 0,
|
|
})
|
|
},
|
|
|
|
handleCycleSideButtons() {
|
|
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,
|
|
gpsLockEnabled: this.data.gpsLockEnabled,
|
|
gpsLockAvailable: this.data.gpsLockAvailable,
|
|
}),
|
|
})
|
|
},
|
|
handleToggleGpsLock() {
|
|
if (mapEngine) {
|
|
mapEngine.handleToggleGpsLock()
|
|
}
|
|
},
|
|
handleToggleMapRotateMode() {
|
|
if (!mapEngine) {
|
|
return
|
|
}
|
|
|
|
if (this.data.orientationMode === 'heading-up') {
|
|
mapEngine.handleSetManualMode()
|
|
return
|
|
}
|
|
|
|
mapEngine.handleSetHeadingUpMode()
|
|
},
|
|
handleToggleDebugPanel() {
|
|
this.setData({
|
|
showDebugPanel: !this.data.showDebugPanel,
|
|
showGameInfoPanel: false,
|
|
...buildSideButtonState({
|
|
sideButtonMode: this.data.sideButtonMode,
|
|
showGameInfoPanel: false,
|
|
skipButtonEnabled: this.data.skipButtonEnabled,
|
|
gameSessionStatus: this.data.gameSessionStatus,
|
|
gpsLockEnabled: this.data.gpsLockEnabled,
|
|
gpsLockAvailable: this.data.gpsLockAvailable,
|
|
}),
|
|
})
|
|
},
|
|
|
|
handleCloseDebugPanel() {
|
|
this.setData({
|
|
showDebugPanel: false,
|
|
...buildSideButtonState({
|
|
sideButtonMode: this.data.sideButtonMode,
|
|
showGameInfoPanel: this.data.showGameInfoPanel,
|
|
skipButtonEnabled: this.data.skipButtonEnabled,
|
|
gameSessionStatus: this.data.gameSessionStatus,
|
|
gpsLockEnabled: this.data.gpsLockEnabled,
|
|
gpsLockAvailable: this.data.gpsLockAvailable,
|
|
}),
|
|
})
|
|
},
|
|
|
|
handleDebugPanelTap() {},
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|