refactor(client): decouple dialogue_box from GameState and AudioManager (#558)

Replace 3 direct GameState.dialogue_active mutations and all
AudioManager.apply_dip/clear_dip calls with signals:
dialogue_state_changed, audio_dip_requested, audio_dip_cleared.
dialogue_box.gd now has zero references to GameState or AudioManager.
main.gd wires coordinator handlers in _ready() (D-020).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 21:59:47 +01:00
co-authored by Claude Opus 4.6
parent c8de1a0629
commit 6f9c97ca03
3 changed files with 234 additions and 12 deletions
+17 -12
View File
@@ -15,6 +15,11 @@ signal dialogue_dismissed # Walk-away or conversation end
signal confrontation_monologue(text: String, duration: float) # D-063: beat monologue
signal pause_requested # D-061: auto-pause — main.gd routes through input recording (#507)
signal unpause_requested # D-061: auto-unpause
# D-020 (#558): Decoupled signals — dialogue_box emits, main.gd (coordinator) handles.
# Replaces direct GameState.dialogue_active mutation and AudioManager calls.
signal dialogue_state_changed(active: bool)
signal audio_dip_requested(profile: String)
signal audio_dip_cleared
@onready var panel: PanelContainer = $PanelContainer
@onready var dialogue_log: RichTextLabel = $PanelContainer/MarginContainer/VBoxContainer/DialogueLog
@@ -286,10 +291,10 @@ func show_dialogue(npc_name: String, speech: String, options: Array = []) -> voi
# Show panel
_ensure_visible()
mouse_filter = Control.MOUSE_FILTER_STOP
GameState.dialogue_active = true # D-064: block movement while in conversation
dialogue_state_changed.emit(true) # D-064: coordinator blocks movement
# D-069: Dialogue dip
AudioManager.apply_dip("dialogue")
# D-069: Dialogue dip — coordinator routes to AudioManager
audio_dip_requested.emit("dialogue")
# D-061: auto-pause — signal to main.gd for input recording (#507)
pause_requested.emit()
@@ -311,14 +316,14 @@ func _end_player_conversation() -> void:
entry.timestamp_msec = now
_log_dirty = true
# D-069: Clear dialogue/confrontation dip
AudioManager.clear_dip()
# D-069: Clear dialogue/confrontation dip — coordinator routes to AudioManager
audio_dip_cleared.emit()
# D-061: unpause — signal to main.gd for input recording (#507)
unpause_requested.emit()
# D-064: unblock movement immediately — log entries stay visible but don't block input.
GameState.dialogue_active = false
# D-064: unblock movement immediately — coordinator handles GameState update.
dialogue_state_changed.emit(false)
# If no entries remain, hide the panel with fade.
if _log_entries.is_empty():
@@ -331,11 +336,11 @@ func hide_dialogue() -> void:
_end_player_conversation()
return # _end_player_conversation may call hide_dialogue if log is empty
GameState.dialogue_active = false
dialogue_state_changed.emit(false) # D-020: coordinator handles GameState update
func is_dialogue_active() -> bool:
return _in_player_conversation or GameState.dialogue_active
return _in_player_conversation
func has_active_entries() -> bool:
@@ -428,12 +433,12 @@ func _start_confrontation_beat(response_id: String, text: String) -> void:
_active_tween.tween_property(panel, "modulate:a", CONFRONTATION_DIM_ALPHA, 0.2)
confrontation_monologue.emit(UIStrings.get_text(CONFRONTATION_MONOLOGUE_KEY), CONFRONTATION_BEAT_DURATION)
AudioManager.apply_dip("confrontation")
audio_dip_requested.emit("confrontation") # D-069: coordinator routes to AudioManager
_beat_tween = create_tween()
_beat_tween.tween_interval(CONFRONTATION_BEAT_DURATION)
_beat_tween.tween_callback(func():
AudioManager.clear_dip()
audio_dip_cleared.emit() # D-069: coordinator routes to AudioManager
option_selected.emit(response_id, text)
_end_player_conversation()
)
@@ -443,7 +448,7 @@ func _cancel_beat() -> void:
if _beat_tween and _beat_tween.is_valid():
_beat_tween.kill()
_beat_tween = null
AudioManager.clear_dip()
audio_dip_cleared.emit() # D-069: coordinator routes to AudioManager
# -- Log rendering --