Sound Phase 2 — Weapon Fire Sounds
Sound Phase 2 — Weapon Fire Sounds
Section titled “Sound Phase 2 — Weapon Fire Sounds”Date: 2026-07-01
Status: Design — approved, ready for planning
Goal: Give every one of the 6 base weapons a distinct fire sound, sourced from a licensed sample pack, per the sound-effects overhaul’s Phase 2 (see audio/SOUND_MAP.md and the 5-phase plan in docs/superpowers/specs/2026-07-01-sound-effects-audit-plan-design.md).
1. Context
Section titled “1. Context”Phase 1 (bus layout + bug fixes) shipped 2026-07-01. Per SOUND_MAP.md, every weapon fire event is currently SILENT, and Orbit/Turret/Scatter don’t even emit an fx_events kind at the moment of firing (their projectiles/shards ARE already visible via existing renderers — the gap is specifically the firing-moment cue, audio and, for Turret/Scatter, a muzzle accent).
2. Sourced assets
Section titled “2. Sourced assets”Chris downloaded the free 99Sounds Sci-Fi Sound Effects bundle (two Rescopic Sound libraries: “Parallax” and “Sci-Fi Energy Weapons”), confirmed under a royalty-free commercial license that explicitly covers “computer/video games” (no attribution required beyond not claiming authorship of the raw files).
Seven source files were selected from the ~140 available, trimmed to short punchy one-shots (mono, 44.1kHz, 16-bit — matching every existing sound in the game) using ffmpeg, with cut points chosen from measured RMS envelopes (not guessed timestamps — two earlier guessed trims were audibly wrong and had to be redone once the envelope was actually measured). Final files, staged at /private/tmp/.../scratchpad/audition/ pending copy into audio/:
File (→ audio/ name) |
Weapon | Sound type | Source |
|---|---|---|---|
pulse_zap.wav |
Pulse | zap-cast | Parallax “Zap Short 02” |
melee_swish.wav |
Melee/Blade | whoosh-cast | Parallax “Swish Crisp Large 01” |
nova_whoosh.wav |
Nova | whoosh-cast | Energy Weapons “Whoosh Energy 06” |
beam_zap.wav |
Beam | zap-cast | Energy Weapons “Shot Plasma PP 01” |
turret_impact.wav |
Turret | impact | Parallax “Hit Small Tight 02” |
scatter_explosion.wav |
Scatter | explosion | Parallax “Blast 03” |
orbit_ambience.wav |
Orbit | drone-ambient | Parallax “Ambience Low 02” — 10s loop-source segment from a stable (non-fading) region of the original 36s ambience; the exact seamless loop point is set via Godot’s audio-import loop settings at implementation time, not baked into the file |
Remaining ~130 files in the pack are not used by this phase — they’re candidate material for Phase 3 (enemy/boss telegraphs: Pings, more Hits/Blasts variants, Robotic Glitch/Morph textures), sourced then, not now.
3. Architecture
Section titled “3. Architecture”Six one-shot weapons (Pulse, Melee, Nova, Beam, Turret, Scatter) all funnel through the existing AudioManager.consume(fx_events) path (audio/audio_manager.gd). No new architecture needed for these — just:
- Extend
SOUND_FOR_FXwith entries for the fx kinds Pulse/Melee/Nova/Beam already emit (bolt,slash,nova,beam— currently emitted but unmapped, hence silent). - Add two brand-new fx kinds,
turret_fireandscatter_fire, emitted once per discrete shot (WeaponTurret._fire()andWeaponScatter.update()respectively) — these did not exist before. Each needs both anAudioManagermapping AND a small visual accent inFxManager.consume(a brief muzzle spark, reusing the existing pooled-sprite pattern from the cycle-22 hit-flash) so the firing moment isn’t silent AND invisible — matching this project’s established “every fx kind needs a visual case” rule. - Known accepted overlap: the
beamfx kind is already shared between the Beam weapon and the Lancer enemy / Eye boss telegraph (perSOUND_MAP.md). Mappingbeam→beam_zap.wavnow means enemy beams incidentally get the same sound until Phase 3 gives them a distinct one. This mirrors the existing GENERIC-sound pattern elsewhere in the map and is not a new problem introduced by this phase. - Evolutions layer, not replace (already-decided project convention, matches the “power grows, doesn’t replace” balance philosophy): an evolved weapon’s fire event still plays its base sound; no separate evolution sound is added in this phase. (Layering a second texture on top of the base sound for evolved weapons is an explicit future nice-to-have, not built now.)
Orbit is architecturally different — it’s continuous, not event-based. WeaponOrbit.update() runs every tick with no cooldown (constant shard rotation), so funneling it through the one-shot fx_events/pool system would either need a per-tick event (spam) or an awkward “is this still ongoing” heuristic. Instead:
AudioManagergains a small looping-audio concern separate from the one-shot pool: a single dedicatedAudioStreamPlayer(busSFX,stream.loop_modeset on the importedorbit_ambience.wav) thatmain.gdstarts/stops once per frame based on whether"orbit"is insim.active_weapon_ids(a plain boolean check, not an event) — start if active and not already playing, stop if inactive and still playing. No/siminvolvement; this is 100% render-side state, matching howQualityManagerand other main-driven per-frame checks already work in this codebase.- This is a new, small addition to
AudioManager’s public surface (e.g.set_orbit_active(bool)), not a rework of the existing one-shot path.
4. Testing
Section titled “4. Testing”Extend tests/test_audio_manager.gd per the project’s existing pattern:
- Every one-shot weapon fx kind (
bolt/slash/nova/beam/turret_fire/scatter_fire) has aSOUND_FOR_FXentry. set_orbit_active(true)starts the loop player exactly once (idempotent on repeated calls);set_orbit_active(false)stops it.- New fx kinds (
turret_fire,scatter_fire) each have a matchingFxManager.consumevisual case (boot-check/playtest-verified, per this codebase’s convention that render/UI visual classes aren’t unit-tested — seefunzo_renderer.gdprecedent — but the fx-kind-has-a-case invariant itself is testable by asserting the match statement’s known kinds, same shape as prior fx-coverage tests).
5. Determinism
Section titled “5. Determinism”Every change is render-side (AudioManager, FxManager, main.gd) plus new fx_events entries — fx_events is excluded from both snapshot_string() and state_checksum() by design (established in Phase 1 and every prior fx-adding cycle). No /sim logic changes beyond appending to fx_events at existing call sites (WeaponTurret._fire, WeaponScatter.update), which is the same shape as every other weapon’s existing fx emission. Determinism re-verification is expected to be a no-op confirmation, matching the original Phase plan’s framing.
6. Out of scope (this phase)
Section titled “6. Out of scope (this phase)”- Phase 3 content (enemy/boss telegraph sounds, the remaining pack files).
- Evolution-specific layered sounds (noted above as a future nice-to-have).
- A precise crossfade-authored seamless loop baked into
orbit_ambience.wav— loop points are a Godot import-setting decision made during implementation, not a pre-baked audio edit. - Any UI sound (Phase 4) or music (Phase 5).