diff --git a/client/public/version.js b/client/public/version.js index 423285a..8bfd76e 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.25 R269"; +window.CHGRID_WEB_VERSION = "2026.02.25 R270"; // 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 6e02bf5..50ccaf4 100644 --- a/client/src/audio/audioEngine.ts +++ b/client/src/audio/audioEngine.ts @@ -35,6 +35,7 @@ const ONE_SHOT_ATTACK_SECONDS = 0.02; type ActiveSpatialSampleRuntime = { sourceX: number; sourceY: number; + range: number; baseGain: number; gainNode: GainNode; pannerNode: StereoPannerNode | null; @@ -361,6 +362,7 @@ export class AudioEngine { sourcePosition: { x: number; y: number }, playerPosition: { x: number; y: number }, gain = 1, + range = HEARING_RADIUS, ): Promise { await this.ensureContext(); const { audioCtx, sfxGainNode } = this; @@ -383,6 +385,7 @@ export class AudioEngine { const runtime: ActiveSpatialSampleRuntime = { sourceX: sourcePosition.x, sourceY: sourcePosition.y, + range: Math.max(1, range), baseGain: gain, gainNode, pannerNode, @@ -412,6 +415,7 @@ export class AudioEngine { sourcePosition: { x: number; y: number }, playerPosition: { x: number; y: number }, gain = 1, + range = HEARING_RADIUS, ): Promise { await this.ensureContext(); const { audioCtx, sfxGainNode } = this; @@ -434,6 +438,7 @@ export class AudioEngine { const runtime: ActiveSpatialSampleRuntime = { sourceX: sourcePosition.x, sourceY: sourcePosition.y, + range: Math.max(1, range), baseGain: gain, gainNode, pannerNode, @@ -619,7 +624,7 @@ export class AudioEngine { const mix = resolveSpatialMix({ dx: sample.sourceX - playerPosition.x, dy: sample.sourceY - playerPosition.y, - range: HEARING_RADIUS, + range: sample.range, baseGain: sample.baseGain, }); const gainValue = mix?.gain ?? 0; diff --git a/client/src/main.ts b/client/src/main.ts index 91263e5..9570a44 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -1657,8 +1657,8 @@ const onAppMessage = createOnMessageHandler({ shouldAnnounceItemPropertyEcho: () => Date.now() >= suppressItemPropertyEchoUntilMs, playLocateToneAt: (x, y) => audio.sfxLocate({ x: x - state.player.x, y: y - state.player.y }), resolveIncomingSoundUrl, - playIncomingItemUseSound: (url, x, y) => { - void audio.playSpatialSample(url, { x, y }, { x: state.player.x, y: state.player.y }, 1); + playIncomingItemUseSound: (url, x, y, range) => { + void audio.playSpatialSample(url, { x, y }, { x: state.player.x, y: state.player.y }, 1, range ?? HEARING_RADIUS); }, playClockAnnouncement: (sounds, x, y) => { void clockAnnouncer.playSequence(sounds.map(resolveIncomingSoundUrl), x, y); diff --git a/client/src/network/messageHandlers.ts b/client/src/network/messageHandlers.ts index f4079ed..b150793 100644 --- a/client/src/network/messageHandlers.ts +++ b/client/src/network/messageHandlers.ts @@ -68,7 +68,7 @@ type MessageHandlerDeps = { shouldAnnounceItemPropertyEcho: () => boolean; playLocateToneAt: (x: number, y: number) => void; resolveIncomingSoundUrl: (url: string) => string; - playIncomingItemUseSound: (url: string, x: number, y: number) => void; + playIncomingItemUseSound: (url: string, x: number, y: number, range?: number) => void; playClockAnnouncement: (sounds: string[], x: number, y: number) => void; handleAuthRequired: (message: Extract) => void; handleAuthResult: (message: Extract) => Promise; @@ -297,7 +297,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco const soundUrl = deps.resolveIncomingSoundUrl(message.sound); if (!soundUrl) break; if (deps.getAudioLayers().world) { - deps.playIncomingItemUseSound(soundUrl, message.x, message.y); + deps.playIncomingItemUseSound(soundUrl, message.x, message.y, message.range); } break; } diff --git a/client/src/network/protocol.ts b/client/src/network/protocol.ts index 0d6003a..2ba476d 100644 --- a/client/src/network/protocol.ts +++ b/client/src/network/protocol.ts @@ -213,6 +213,7 @@ export const itemUseSoundSchema = z.object({ sound: z.string(), x: z.number().int(), y: z.number().int(), + range: z.number().int().positive().optional(), }); export const itemClockAnnounceSchema = z.object({ diff --git a/server/app/models.py b/server/app/models.py index 1967990..b64202a 100644 --- a/server/app/models.py +++ b/server/app/models.py @@ -292,6 +292,7 @@ class ItemUseSoundPacket(BasePacket): sound: str x: int y: int + range: int | None = None class ItemClockAnnouncePacket(BasePacket): diff --git a/server/app/server.py b/server/app/server.py index 7ddc310..cb34877 100644 --- a/server/app/server.py +++ b/server/app/server.py @@ -1770,6 +1770,7 @@ class SignalingServer: use_sound = self._resolve_item_use_sound(item) if use_sound: sound_x, sound_y = self._get_item_sound_source_position(item) + sound_range = self._get_item_emit_range(item) await self._broadcast( ItemUseSoundPacket( type="item_use_sound", @@ -1777,6 +1778,7 @@ class SignalingServer: sound=use_sound, x=sound_x, y=sound_y, + range=sound_range, ) ) if item.type == "clock":