From 54cfffc5cf71188c7f0e002a3c71725ec7968c1c Mon Sep 17 00:00:00 2001 From: Jage9 Date: Sun, 22 Feb 2026 20:23:38 -0500 Subject: [PATCH] Add moving teleport start audio loop and end cut-off --- client/public/sounds/teleport_start.ogg | Bin 0 -> 32030 bytes client/public/version.js | 2 +- client/src/audio/audioEngine.ts | 28 ++++++++++++++++++++++++ client/src/main.ts | 24 ++++++++++++++++++++ client/src/network/messageHandlers.ts | 3 ++- 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 client/public/sounds/teleport_start.ogg diff --git a/client/public/sounds/teleport_start.ogg b/client/public/sounds/teleport_start.ogg new file mode 100644 index 0000000000000000000000000000000000000000..cb95014d9b83b1d51ebb524b927083357f4e2063 GIT binary patch literal 32030 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 Y$&Ro8^Yw!>O2ns44t_>q9A%OM0Ci%RT>t<8 literal 0 HcmV?d00001 diff --git a/client/public/version.js b/client/public/version.js index 7ce6339..141ead1 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 R182"; +window.CHGRID_WEB_VERSION = "2026.02.22 R183"; // Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid. window.CHGRID_TIME_ZONE = "America/Detroit"; diff --git a/client/src/audio/audioEngine.ts b/client/src/audio/audioEngine.ts index c761a0a..69b38b4 100644 --- a/client/src/audio/audioEngine.ts +++ b/client/src/audio/audioEngine.ts @@ -394,6 +394,34 @@ export class AudioEngine { } } + /** Starts a looping sample and returns a stop callback for explicit teardown. */ + async startLoopingSample(url: string, gain = 1): Promise<(() => void) | null> { + await this.ensureContext(); + const { audioCtx, sfxGainNode } = this; + if (!audioCtx || !sfxGainNode || gain <= 0) return null; + try { + const buffer = await this.getSampleBuffer(url); + const source = audioCtx.createBufferSource(); + source.buffer = buffer; + source.loop = true; + const gainNode = audioCtx.createGain(); + gainNode.gain.value = gain; + source.connect(gainNode).connect(sfxGainNode); + source.start(); + return () => { + try { + source.stop(); + } catch { + // Ignore already-stopped source. + } + source.disconnect(); + gainNode.disconnect(); + }; + } catch { + return null; + } + } + cleanupPeerAudio(peer: SpatialPeerRuntime): void { if (peer.audioElement) { peer.audioElement.pause(); diff --git a/client/src/main.ts b/client/src/main.ts index 27b0621..f991c17 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -177,6 +177,7 @@ const SYSTEM_SOUND_URLS = { } as const; const FOOTSTEP_SOUND_URLS = Array.from({ length: 11 }, (_, index) => withBase(`sounds/step-${index + 1}.ogg`)); const FOOTSTEP_GAIN = 0.7; +const TELEPORT_START_SOUND_URL = withBase('sounds/teleport_start.ogg'); const TELEPORT_SOUND_URL = withBase('sounds/teleport.ogg'); const WALL_SOUND_URL = withBase('sounds/wall.ogg'); @@ -219,6 +220,8 @@ let lastSubscriptionRefreshTileX = Math.round(state.player.x); let lastSubscriptionRefreshTileY = Math.round(state.player.y); let subscriptionRefreshInFlight = false; let subscriptionRefreshPending = false; +let activeTeleportLoopStop: (() => void) | null = null; +let activeTeleportLoopToken = 0; let activeTeleport: | { startX: number; @@ -1096,6 +1099,13 @@ function randomFootstepUrl(): string { return FOOTSTEP_SOUND_URLS[Math.floor(Math.random() * FOOTSTEP_SOUND_URLS.length)]; } +/** Stops active teleport loop audio, if one is running. */ +function stopTeleportLoopAudio(): void { + if (!activeTeleportLoopStop) return; + activeTeleportLoopStop(); + activeTeleportLoopStop = null; +} + /** Starts animated teleport movement toward a target tile at fixed squares-per-second pace. */ function startTeleportTo(targetX: number, targetY: number, completionStatus: string): void { const startX = state.player.x; @@ -1115,6 +1125,17 @@ function startTeleportTo(targetX: number, targetY: number, completionStatus: str lastSentY: Math.round(startY), completionStatus, }; + stopTeleportLoopAudio(); + activeTeleportLoopToken += 1; + const loopToken = activeTeleportLoopToken; + void audio.startLoopingSample(TELEPORT_START_SOUND_URL, FOOTSTEP_GAIN).then((stopLoop) => { + if (!stopLoop) return; + if (activeTeleport && loopToken === activeTeleportLoopToken) { + activeTeleportLoopStop = stopLoop; + return; + } + stopLoop(); + }); void refreshAudioSubscriptionsAt({ x: targetX, y: targetY }, true); state.keysPressed.ArrowUp = false; state.keysPressed.ArrowDown = false; @@ -1151,6 +1172,7 @@ function updateTeleport(): void { state.player.y = activeTeleport.targetY; signaling.send({ type: 'update_position', x: activeTeleport.targetX, y: activeTeleport.targetY }); activeTeleport = null; + stopTeleportLoopAudio(); persistPlayerPosition(); void refreshAudioSubscriptions(true); void audio.playSample(TELEPORT_SOUND_URL, FOOTSTEP_GAIN); @@ -1392,6 +1414,7 @@ function disconnect(): void { lastSubscriptionRefreshAt = 0; lastSubscriptionRefreshTileX = Math.round(state.player.x); lastSubscriptionRefreshTileY = Math.round(state.player.y); + stopTeleportLoopAudio(); activeTeleport = null; } @@ -1427,6 +1450,7 @@ const onAppMessage = createOnMessageHandler({ ); }, TELEPORT_SOUND_URL, + TELEPORT_START_SOUND_URL, audioLayers, pushChatMessage, classifySystemMessageSound, diff --git a/client/src/network/messageHandlers.ts b/client/src/network/messageHandlers.ts index c561fd7..b746a55 100644 --- a/client/src/network/messageHandlers.ts +++ b/client/src/network/messageHandlers.ts @@ -47,6 +47,7 @@ type MessageHandlerDeps = { randomFootstepUrl: () => string; playRemoteSpatialStepOrTeleport: (url: string, peerX: number, peerY: number) => void; TELEPORT_SOUND_URL: string; + TELEPORT_START_SOUND_URL: string; audioLayers: { world: boolean }; pushChatMessage: (message: string) => void; classifySystemMessageSound: (message: string) => 'logon' | 'logout' | 'notify' | null; @@ -135,7 +136,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco deps.peerManager.setPeerPosition(message.id, message.x, message.y); if (peer) { const movementDelta = Math.hypot(message.x - prevX, message.y - prevY); - const soundUrl = movementDelta > 1.5 ? deps.TELEPORT_SOUND_URL : deps.randomFootstepUrl(); + const soundUrl = movementDelta > 1.5 ? deps.TELEPORT_START_SOUND_URL : deps.randomFootstepUrl(); if (deps.audioLayers.world) { deps.playRemoteSpatialStepOrTeleport(soundUrl, peer.x, peer.y); }