From 88697955b74e8b3cebf580a771f74304e0e96c40 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Wed, 3 Jun 2026 20:43:12 -0700 Subject: [PATCH] Add SoundEffectController to restore v31 sound effects Ports the sound-emission logic from v31's deleted FxLayer into a new controller registered with the game renderer. Per-tick it scans the worker's update stream and emits PlaySoundEffectEvent for unit launches, nuke detonations, structure/warship builds (local player only), and conquests where the local player is the conqueror. --- .../controllers/SoundEffectController.ts | 87 +++++++++++++++++++ src/client/hud/GameRenderer.ts | 2 + 2 files changed, 89 insertions(+) create mode 100644 src/client/controllers/SoundEffectController.ts diff --git a/src/client/controllers/SoundEffectController.ts b/src/client/controllers/SoundEffectController.ts new file mode 100644 index 000000000..fc4bb91fd --- /dev/null +++ b/src/client/controllers/SoundEffectController.ts @@ -0,0 +1,87 @@ +import { EventBus } from "../../core/EventBus"; +import { UnitType } from "../../core/game/Game"; +import { GameUpdateType } from "../../core/game/GameUpdates"; +import { GameView, UnitView } from "../../core/game/GameView"; +import { Controller } from "../Controller"; +import { PlaySoundEffectEvent, SoundEffect } from "../sound/Sounds"; + +export class SoundEffectController implements Controller { + constructor( + private readonly game: GameView, + private readonly eventBus: EventBus, + ) {} + + tick(): void { + const updates = this.game.updatesSinceLastTick(); + if (!updates) return; + + for (const u of updates[GameUpdateType.Unit] ?? []) { + const unit = this.game.unit(u.id); + if (unit === undefined) continue; + this.handleUnit(unit); + } + + const myPlayer = this.game.myPlayer(); + if (myPlayer === null) return; + for (const c of updates[GameUpdateType.ConquestEvent] ?? []) { + if (c.conquerorId === myPlayer.id()) { + this.emit("ka-ching"); + } + } + } + + private handleUnit(unit: UnitView): void { + if (unit.isActive() && unit.createdAt() === this.game.ticks()) { + this.onCreated(unit); + } + switch (unit.type()) { + case UnitType.AtomBomb: + case UnitType.MIRVWarhead: + this.onNukeDetonation(unit, "atom-hit"); + break; + case UnitType.HydrogenBomb: + this.onNukeDetonation(unit, "hydrogen-hit"); + break; + } + } + + private onCreated(unit: UnitView): void { + const myPlayer = this.game.myPlayer(); + switch (unit.type()) { + case UnitType.AtomBomb: + this.emit("atom-launch"); + break; + case UnitType.HydrogenBomb: + this.emit("hydrogen-launch"); + break; + case UnitType.MIRV: + this.emit("mirv-launch"); + break; + case UnitType.Warship: + if (unit.owner() === myPlayer) this.emit("build-warship"); + break; + case UnitType.City: + if (unit.owner() === myPlayer) this.emit("build-city"); + break; + case UnitType.Port: + if (unit.owner() === myPlayer) this.emit("build-port"); + break; + case UnitType.DefensePost: + if (unit.owner() === myPlayer) this.emit("build-defense-post"); + break; + case UnitType.SAMLauncher: + if (unit.owner() === myPlayer) this.emit("sam-built"); + break; + } + } + + private onNukeDetonation(unit: UnitView, sound: SoundEffect): void { + if (unit.isActive()) return; + if (!unit.reachedTarget()) return; + this.emit(sound); + } + + private emit(sound: SoundEffect): void { + this.eventBus.emit(new PlaySoundEffectEvent(sound)); + } +} diff --git a/src/client/hud/GameRenderer.ts b/src/client/hud/GameRenderer.ts index 3c08c477c..ea48a58a1 100644 --- a/src/client/hud/GameRenderer.ts +++ b/src/client/hud/GameRenderer.ts @@ -8,6 +8,7 @@ import { UIState } from "../UIState"; import { AttackingTroopsController } from "../controllers/AttackingTroopsController"; import { BuildPreviewController } from "../controllers/BuildPreviewController"; import { HoverHighlightController } from "../controllers/HoverHighlightController"; +import { SoundEffectController } from "../controllers/SoundEffectController"; import { StructureHighlightController } from "../controllers/StructureHighlightController"; import { ViewModeController } from "../controllers/ViewModeController"; import { WarshipSelectionController } from "../controllers/WarshipSelectionController"; @@ -294,6 +295,7 @@ export function createRenderer( new StructureHighlightController(eventBus, view), new ViewModeController(eventBus, view), new AttackingTroopsController(game, eventBus, userSettings, view), + new SoundEffectController(game, eventBus), eventsDisplay, actionableEvents, attacksDisplay,