Add procedural crowd ambience and radio crackle
Full build / Import + headless tests (push) Successful in 45s
Full build / Full desktop build + optional release (push) Failing after 3m53s

- AudioFeedback.start_crowd_bed() builds a 4 s looping AudioStreamWAV at runtime
  (one-pole filtered noise plus a 68/103 Hz drone, seeded) and set_crowd_level()
  maps crowd mood 0..1 to -34..-12 dB, so the street gets louder as it gets
  agitated or panics. The bed starts with the shift and stops at shift end.
- play_radio_open()/play_radio_close() push short filtered-noise bursts; wired to
  opening the radio panel, submitting a report, cancelling, and closing the panel.
- Still asset-free and headless-safe: with a dummy audio server the calls no-op.
  _free_player() returns null before _ready and callers handle it.

Tests/docs
- Headless suite 127 -> 134 (test_audio_feedback.gd).
- New smoke_audio.gd: bed playing, level follows mood events, radio crackles once
  per open/close, bed stops at shift end. Verified windowed (real audio server).
- README, TODO section moved to DONE, MEMORY. Version 0.2.8.
This commit is contained in:
zwitschi committed 2026-10-31 21:08:36 +01:00
1 parent e2c2a0bf4c
commit 294ac17333
13 files changed
+305 -17

No files matched your search

+15
View File
@@ -233,6 +233,9 @@ func _reset_shift_state() -> void:
## Begin (or resume) the shift clock and recapture the mouse.
func _start_shift() -> void:
_shift_running = true
if audio:
audio.start_crowd_bed()
audio.set_crowd_level(coordinator.crowd.mood)
coordinator.start()
if hud:
hud.set_shift_state(coordinator, selected_scenario)
@@ -322,6 +325,8 @@ func _build_ui() -> void:
radio_panel.submitted.connect(_on_radio_submitted)
radio_panel.cancelled.connect(func():
radio_panel.hide()
if audio:
audio.play_radio_close()
hud.set_action_hint("")
_refresh_freeze())
shift_review_panel = ShiftReview.new()
@@ -566,6 +571,8 @@ func _connect_crowd() -> void:
func _on_crowd_changed(payload: Dictionary) -> void:
var event_type := str(payload.get("event", ""))
var panic := bool(payload.get("panic", false))
if audio:
audio.set_crowd_level(float(payload.get("mood", 0.0)))
# Panic wins: people clear away from the entrance.
if panic or event_type == "crowd_panic":
_flee_crowd()
@@ -609,6 +616,8 @@ func _responder_profile() -> NpcProfile:
func _on_shift_ended(review: Dictionary) -> void:
_shift_running = false
if audio:
audio.stop_crowd_bed()
# Persist the finished shift locally (Save autoload, user:// JSON).
if Save:
Save.save_shift_result(review)
@@ -801,6 +810,8 @@ func _close_top_modal() -> bool:
return true
if radio_panel and radio_panel.visible:
radio_panel.hide()
if audio:
audio.play_radio_close()
hud.set_action_hint("")
_refresh_freeze()
return true
@@ -1126,9 +1137,13 @@ func _open_radio(emergency: bool) -> void:
return
radio_panel.open(controller, emergency, current_npc_truth)
hud.set_action_hint("Fill in the radio report on screen.")
if audio:
audio.play_radio_open()
_refresh_freeze()
func _on_radio_submitted(report: Dictionary, emergency: bool) -> void:
if audio:
audio.play_radio_close()
hud.set_action_hint("")
_refresh_freeze()
var result: Dictionary = coordinator.radio.request_emergency(report) if emergency else coordinator.radio.request_assistance(report)