feat(ui): unified dialogue log with overheard NPC conversations (#535)
Refactors dialogue box into a scrolling conversation log. All dialogue (player-NPC and overheard NPC-NPC) flows chronologically, oldest at top. Player response options at the bottom during active conversations. - Entries expire after configurable timeout (equal for all message types) - Walk-away clears options but preserves log entries (fair information) - Per-character name colors from dialogue-theme.yaml (hash-indexed palette) - Overheard lines render at 90% opacity (D-078) - Protocol decode for conversation_events + conversation_ended - GameState fields for conversation_events, conversation_ended, dialogue_response - Mock Mira/Soren NPC-NPC conversation in test snapshot - Also wires #511 debug overlay into main.gd and main.tscn Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,10 +14,12 @@ extends Node2D
|
||||
@onready var cursor_renderer = $UILayer/CursorRenderer # D-056: z-layer 7
|
||||
@onready var gauntlet_hud = $UILayer/GauntletHUD # #496: room timer + personal bests
|
||||
@onready var checklist_overlay = $UILayer/ChecklistOverlay # #503: auto-checklist progress
|
||||
@onready var debug_overlay = $UILayer/DebugOverlay # #511: F3 debug overlay
|
||||
@onready var bug_report_dialog = $ModalLayer/BugReportDialog # #495: F12 WRONG button
|
||||
@onready var settings_dialog = $ModalLayer/SettingsDialog # #528: audio settings (ESC/OPEN_MENU)
|
||||
|
||||
var _last_dialogue_npc_id: int = -1 # D-064: NPC entity_id for WalkAway input
|
||||
var _last_dialogue_npc_name: String = "" # #535: NPC name for dialogue_response attribution
|
||||
var _camera_anchored: bool = false
|
||||
var _last_monologue_tick: int = -1 # Prevent re-consuming monologue when same tick polled twice
|
||||
var _last_dialogue_tick: int = -1
|
||||
@@ -130,6 +132,10 @@ func _process(_delta: float) -> void:
|
||||
if checklist_overlay and checklist_overlay.has_method("update_from_state"):
|
||||
checklist_overlay.update_from_state()
|
||||
|
||||
# #511: Update debug overlay (F3 toggle, dev tool)
|
||||
if debug_overlay and debug_overlay.has_method("update_from_state"):
|
||||
debug_overlay.update_from_state()
|
||||
|
||||
# D-018 #125: Play close-range sound events via positional 2D audio
|
||||
_play_close_sound_events()
|
||||
|
||||
@@ -146,6 +152,11 @@ func _process(_delta: float) -> void:
|
||||
# D-061: Show dialogue if server sent one this tick (#434)
|
||||
_consume_dialogue()
|
||||
|
||||
# #535: Consume overheard conversation events and responses
|
||||
_consume_conversation_events()
|
||||
_consume_conversation_ended()
|
||||
_consume_dialogue_response()
|
||||
|
||||
# Track camera to player position every frame (D-015: locked, no panning)
|
||||
if _camera_anchored:
|
||||
camera.global_position = GameState.player_position * Constants.TILE_SIZE
|
||||
@@ -316,6 +327,7 @@ func _consume_dialogue() -> void:
|
||||
_last_dialogue_tick = GameState.current_tick
|
||||
var dlg: Dictionary = GameState.current_dialogue
|
||||
_last_dialogue_npc_id = dlg.get("npc_entity_id", -1)
|
||||
_last_dialogue_npc_name = dlg.get("npc_name", "")
|
||||
dialogue_box.show_dialogue(
|
||||
dlg.get("npc_name", ""),
|
||||
dlg.get("speech", ""),
|
||||
@@ -324,6 +336,34 @@ func _consume_dialogue() -> void:
|
||||
GameState.current_dialogue = null
|
||||
|
||||
|
||||
# #535: Consume overheard NPC-NPC conversation events (D-078).
|
||||
# Each event carries pre-occluded text — render verbatim in the dialogue log.
|
||||
func _consume_conversation_events() -> void:
|
||||
if not dialogue_box:
|
||||
return
|
||||
for event in GameState.conversation_events:
|
||||
dialogue_box.append_conversation_event(event)
|
||||
GameState.conversation_events = []
|
||||
|
||||
|
||||
# #535: Handle conversation_ended events — notify dialogue box to stop tracking pairs.
|
||||
func _consume_conversation_ended() -> void:
|
||||
if not dialogue_box:
|
||||
return
|
||||
for event in GameState.conversation_ended:
|
||||
dialogue_box.on_conversation_ended(event)
|
||||
GameState.conversation_ended = []
|
||||
|
||||
|
||||
# #535: Consume dialogue_response — NPC follow-up line after player picks an option.
|
||||
func _consume_dialogue_response() -> void:
|
||||
if GameState.dialogue_response == null or not dialogue_box:
|
||||
return
|
||||
var dr: Dictionary = GameState.dialogue_response
|
||||
dialogue_box.append_dialogue_response(_last_dialogue_npc_name, dr.get("text", ""))
|
||||
GameState.dialogue_response = null
|
||||
|
||||
|
||||
# D-061: Handle dialogue option selection → send to server
|
||||
func _on_dialogue_option_selected(response_id: String, text: String) -> void:
|
||||
SimBridge.send_input({
|
||||
|
||||
Reference in New Issue
Block a user