Phase 18 — Audio (music director + SFX catalog + bus wiring)

Adds an AudioManager autoload with three buses (Master, Music routed to
Master, SFX routed to Master), a small catalog of looping music + one-shot
SFX, and a single persistent AudioStreamPlayer for the music director.

Music
* Day and night loops swap on Clock.phase_changed (night during the night
  phase, day everywhere else). Tracks pulled from Retro Farming Music 1
  (day) and Cozy Melodies Pack 1 (night), both loopable OGG.

SFX
* Tree.fell, Rock.mined, BigRock.mined → tree_fell / mine_tick.
* EventBus.pawn_took_damage → combat_hit (Sword Pack 1).
* EventBus.storyteller_event_fired → ui_confirm sting.
* EventBus.alert_added → ui_click.
* play_sfx is rate-limited per key (80ms cooldown) so fast-sim doesn't
  saturate the mixer.

Settings + suspend
* SettingsMenu master/music/sfx sliders now live-bind to the bus dB via
  Audio.set_*_linear (linear → dB internally, 0 → -80dB silence). The
  ambient slider is intentionally unwired; no ambient bus this pass.
* NOTIFICATION_APPLICATION_PAUSED + FOCUS_OUT mute the Master bus to
  match the existing "no background sim" rule. Resume + focus restore it.

Bundle housekeeping
* Two zipped packs in the ElvGames bundle (Cozy Melodies Pack 1, Retro
  Farming Music 1) extracted in place to keep pack identity intact for
  the license/credits string. 8 OGG files curated into audio/ at ~5.3MB.

Verified end-to-end via MCP runtime: buses online, day_loop plays at
boot, manual phase swap day→night→day round-trips, slider linear→dB
mapping correct (0.5 → -6.02dB, 0.0 → -80dB), tree_fell SFX triggers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-15 18:54:36 +01:00
parent fb07a3fa15
commit d819c13a9d
25 changed files with 437 additions and 8 deletions

View file

@ -206,6 +206,20 @@ func _load_from_game_state() -> void:
_sl_sfx.value = float(s.get("audio_sfx", 1.0))
_sl_ambient.value = float(s.get("audio_ambient", 1.0))
# Push initial slider values into the live audio buses + bind live updates.
# Done here rather than _ready because _load_from_game_state fires after
# GameState has restored settings from disk.
if Audio != null:
Audio.set_master_linear(_sl_master.value)
Audio.set_music_linear(_sl_music.value)
Audio.set_sfx_linear(_sl_sfx.value)
if not _sl_master.value_changed.is_connected(Audio.set_master_linear):
_sl_master.value_changed.connect(Audio.set_master_linear)
_sl_music.value_changed.connect(Audio.set_music_linear)
_sl_sfx.value_changed.connect(Audio.set_sfx_linear)
# Ambient slider isn't wired to a bus yet (no ambient bus in Phase 18).
# Phase 19 onboarding pass may add a third bus for nature SFX.
_cb_large_text.button_pressed = bool(s.get("accessibility_large_text", false))
_cb_reduce_motion.button_pressed = bool(s.get("accessibility_reduce_motion", false))