diff --git a/client/public/version.js b/client/public/version.js index e0f2494..7ce6339 100644 --- a/client/public/version.js +++ b/client/public/version.js @@ -1,5 +1,5 @@ // Maintainer-controlled web client version. // Format: YYYY.MM.DD Rn (example: 2026.02.20 R2) -window.CHGRID_WEB_VERSION = "2026.02.22 R181"; +window.CHGRID_WEB_VERSION = "2026.02.22 R182"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/main.ts b/client/src/main.ts index f1d5a55..27b0621 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -227,7 +227,6 @@ let activeTeleport: targetY: number; startedAtMs: number; durationMs: number; - lastStepAtMs: number; lastSyncAtMs: number; lastSentX: number; lastSentY: number; @@ -557,12 +556,12 @@ async function applyAudioLayerState(): Promise { await itemEmitRuntime.setLayerEnabled(audioLayers.item, state.items.values(), listenerPosition); } -/** Refreshes distance-gated radio/item stream subscriptions on movement or timer cadence. */ -async function refreshAudioSubscriptions(force = false): Promise { +/** Refreshes distance-gated radio/item stream subscriptions for a listener position. */ +async function refreshAudioSubscriptionsAt(listenerPosition: { x: number; y: number }, force = false): Promise { if (!state.running) return; const now = Date.now(); - const tileX = Math.round(state.player.x); - const tileY = Math.round(state.player.y); + const tileX = Math.round(listenerPosition.x); + const tileY = Math.round(listenerPosition.y); const moved = tileX !== lastSubscriptionRefreshTileX || tileY !== lastSubscriptionRefreshTileY; if (!force && !moved && now - lastSubscriptionRefreshAt < AUDIO_SUBSCRIPTION_REFRESH_MS) { return; @@ -575,7 +574,6 @@ async function refreshAudioSubscriptions(force = false): Promise { lastSubscriptionRefreshAt = now; lastSubscriptionRefreshTileX = tileX; lastSubscriptionRefreshTileY = tileY; - const listenerPosition = { x: state.player.x, y: state.player.y }; try { await radioRuntime.sync(state.items.values(), listenerPosition); await itemEmitRuntime.sync(state.items.values(), listenerPosition); @@ -588,6 +586,15 @@ async function refreshAudioSubscriptions(force = false): Promise { } } +/** Refreshes distance-gated radio/item stream subscriptions on movement or timer cadence. */ +async function refreshAudioSubscriptions(force = false): Promise { + if (activeTeleport) { + await refreshAudioSubscriptionsAt({ x: activeTeleport.targetX, y: activeTeleport.targetY }, force); + return; + } + await refreshAudioSubscriptionsAt({ x: state.player.x, y: state.player.y }, force); +} + /** Toggles a single audio layer and applies the change immediately. */ function toggleAudioLayer(layer: keyof AudioLayerState): void { audioLayers = { ...audioLayers, [layer]: !audioLayers[layer] }; @@ -1103,12 +1110,12 @@ function startTeleportTo(targetX: number, targetY: number, completionStatus: str targetY, startedAtMs: nowMs, durationMs, - lastStepAtMs: nowMs, lastSyncAtMs: nowMs, lastSentX: Math.round(startX), lastSentY: Math.round(startY), completionStatus, }; + void refreshAudioSubscriptionsAt({ x: targetX, y: targetY }, true); state.keysPressed.ArrowUp = false; state.keysPressed.ArrowDown = false; state.keysPressed.ArrowLeft = false; @@ -1125,11 +1132,6 @@ function updateTeleport(): void { state.player.x = activeTeleport.startX + (activeTeleport.targetX - activeTeleport.startX) * progress; state.player.y = activeTeleport.startY + (activeTeleport.targetY - activeTeleport.startY) * progress; - if (nowMs - activeTeleport.lastStepAtMs >= MOVE_COOLDOWN_MS) { - activeTeleport.lastStepAtMs = nowMs; - void audio.playSample(randomFootstepUrl(), FOOTSTEP_GAIN, MOVE_COOLDOWN_MS); - } - if (nowMs - activeTeleport.lastSyncAtMs >= TELEPORT_SYNC_INTERVAL_MS) { activeTeleport.lastSyncAtMs = nowMs; const syncX = Math.round(state.player.x); @@ -1160,7 +1162,9 @@ function gameLoop(): void { if (!state.running) return; updateTeleport(); handleMovement(); - void refreshAudioSubscriptions(); + if (!activeTeleport) { + void refreshAudioSubscriptions(); + } audio.updateSpatialAudio(peerManager.getPeers(), { x: state.player.x, y: state.player.y }); radioRuntime.updateSpatialAudio(state.items, { x: state.player.x, y: state.player.y }); itemEmitRuntime.updateSpatialAudio(state.items, { x: state.player.x, y: state.player.y });