Files
Cozypaw-Hospital/docs/superpowers/specs/2026-05-10-sprint-22-character-ambient-sfx.md
T
Steven Wroblewski 52ebb78862 chore(audio): add download script, audio credits, and sprint 21/22 docs
- docs/download_audio.py: freesound batch downloader with all 22 confirmed IDs
  (API key removed — fill in locally from freesound.org)
- docs/credits-audio.md: generated CC-BY attribution table
- docs/superpowers/plans+specs: sprint 15, 21, 22 implementation plan/spec docs
- .claude/settings.json: enable experimental agent teams env var

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 14:57:27 +02:00

3.3 KiB

Sprint 22 — Character & Ambient SFX Design Spec

Goal

Two deferred SFX items from Sprint 21:

  1. UltrasoundMachine ambient heartbeat — continuous looping audio that starts when the player enters the ultrasound room and stops when they leave.
  2. Character SFX — pickup, place, and tap sounds wired into character.gd.

New SFX Events

AudioManager._SFX_MAP additions (3 new keys)

Event key Trigger
character_pickup Character._on_drag_picked_up()
character_place Character._on_drag_released() — drag distance ≥ tap threshold
character_tap Character._on_drag_released() — drag distance < tap threshold

UltrasoundMachine (self-managed, not via _SFX_MAP)

The heartbeat is a looping ambient sound owned by the UltrasoundMachine node itself. It does not go through AudioManager.play_sfx() — that path is for one-shot SFX. Instead, UltrasoundMachine creates its own AudioStreamPlayer child, sets stream.loop = true at runtime, and starts/stops it in response to RoomNavigator.room_changed.

Asset Specification

New files:

assets/audio/sfx/ultrasound_heartbeat.ogg  — soft beep/blip, ~1s, loops seamlessly
assets/audio/sfx/character_pickup.ogg      — happy soft squeak / whoosh
assets/audio/sfx/character_place.ogg       — gentle thud / landing
assets/audio/sfx/character_tap.ogg         — short happy chime / pop

All CC0 or CC-BY from freesound.org. Placeholder 0-byte files committed; real downloads documented in docs/audio-assets-sprint19.md (extend existing file).

Integration Points

UltrasoundMachine (scripts/objects/ultrasound_machine.gd)

Full replacement. New behaviour:

  • @export var trigger_floor: int = 2 and @export var trigger_room: int = 0 (matches ultrasound room position in _ROOM_NAMES)
  • In _ready(): create AudioStreamPlayer child, load stream with loop = true, connect to RoomNavigator.room_changed
  • In _on_room_changed(floor_index, room_index): if matches trigger position → _audio.play(), else → _audio.stop()
  • AudioServer.get_driver_name() == "Dummy" guard wraps all audio operations
  • _exit_tree(): disconnect signal (mirrors Ambulance pattern)
  • Volume is inherited from the bus (no explicit volume set — ambient heartbeat is soft by design)

Character (scripts/characters/character.gd)

Three one-liner additions:

  • _on_drag_picked_up()AudioManager.play_sfx("character_pickup") as first line
  • _on_drag_released() tap branch → AudioManager.play_sfx("character_tap") before _handle_outfit_tap()
  • _on_drag_released() place branch → AudioManager.play_sfx("character_place") before character_placed.emit()

Testing

Append to test/unit/test_audio_manager.gd:

  • test_sfx_map_has_all_character_keys — verifies character_pickup, character_place, character_tap exist in _SFX_MAP

No unit test for UltrasoundMachine audio start/stop — the trigger is room-navigation-driven and mirrors the Ambulance pattern (which also has no per-SFX unit test).

Out of Scope

  • Per-state-transition character sounds (e.g., happy sound when healed — separate sprint)
  • Room-specific ambient audio for other rooms
  • UltrasoundMachine volume linked to GameState.sfx_volume (ambient bus handles this via AudioServer)