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:
@@ -59,6 +59,11 @@ var rng_seed: Variant = null
|
||||
# v7 fields (#431, D-059/D-060)
|
||||
var pending_recognitions: Array = [] # [{entity_id, x, y, z, remaining_ticks, total_delay_ticks}]
|
||||
|
||||
# v9 fields (#535, D-078): Overheard NPC-to-NPC conversations
|
||||
var conversation_events: Array = [] # [{speaker_id, target_id, speaker_name, target_name, occluded_line}]
|
||||
var conversation_ended: Array = [] # [{speaker_id, target_id}]
|
||||
var dialogue_response: Variant = null # {line_id, text, speaker_entity_id} — NPC follow-up after player choice
|
||||
|
||||
# #126, D-018: Medium-range sound events for fog-edge directional indicators.
|
||||
# Format: [{x, y, event_type, range_category}] — server sends current medium events per tick.
|
||||
var medium_sound_events: Array = []
|
||||
@@ -169,6 +174,24 @@ func apply_snapshot(snapshot: Dictionary) -> void:
|
||||
else:
|
||||
pending_recognitions = []
|
||||
|
||||
# v9: conversation_events (#535, D-078) — overheard NPC-to-NPC lines
|
||||
if snapshot.has("conversation_events") and snapshot.conversation_events is Array:
|
||||
conversation_events = snapshot.conversation_events
|
||||
else:
|
||||
conversation_events = []
|
||||
|
||||
# v9: conversation_ended (#535, D-078) — pairs whose conversation ended
|
||||
if snapshot.has("conversation_ended") and snapshot.conversation_ended is Array:
|
||||
conversation_ended = snapshot.conversation_ended
|
||||
else:
|
||||
conversation_ended = []
|
||||
|
||||
# v9: dialogue_response (#535, D-028) — NPC follow-up after player choice
|
||||
if snapshot.has("dialogue_response") and snapshot.dialogue_response is Dictionary:
|
||||
dialogue_response = snapshot.dialogue_response
|
||||
else:
|
||||
dialogue_response = null
|
||||
|
||||
# v8: gauntlet mode (#496) — room_id and gauntlet_mode
|
||||
if snapshot.has("gauntlet_mode") and snapshot.gauntlet_mode == true:
|
||||
gauntlet_mode = true
|
||||
|
||||
@@ -396,6 +396,37 @@ func _test_snapshot() -> Dictionary:
|
||||
"total_delay_ticks": total_delay,
|
||||
})
|
||||
|
||||
# #535: Mock overheard NPC-NPC conversation (D-078)
|
||||
# Two NPCs (Mira and Soren) trade lines every 5 ticks starting at tick 3.
|
||||
# Conversation ends after 6 exchanges (~30 ticks).
|
||||
var conv_events: Array = []
|
||||
var conv_ended: Array = []
|
||||
var conv_start := 3
|
||||
var conv_lines := [
|
||||
{"speaker": "Mira", "target": "Soren", "line": "The cargo manifests don't add up. Three containers unaccounted for."},
|
||||
{"speaker": "Soren", "target": "Mira", "line": "Could be a logging error. Happens every... cycle."},
|
||||
{"speaker": "Mira", "target": "Soren", "line": "Not like this. Someone moved them after... check."},
|
||||
{"speaker": "Soren", "target": "Mira", "line": "You're reading too much into it. The docks are... these days."},
|
||||
{"speaker": "Mira", "target": "Soren", "line": "Then explain the weight discrepancy. Two hundred kilos... just gone."},
|
||||
{"speaker": "Soren", "target": "Mira", "line": "Fine. I'll pull the bay... tonight. But keep this between us."},
|
||||
]
|
||||
var conv_tick_interval := 5
|
||||
var conv_total_ticks := conv_lines.size() * conv_tick_interval
|
||||
if _test_tick >= conv_start and _test_tick < conv_start + conv_total_ticks:
|
||||
var conv_index := (_test_tick - conv_start) / conv_tick_interval
|
||||
var within_tick := (_test_tick - conv_start) % conv_tick_interval
|
||||
if within_tick == 0 and conv_index < conv_lines.size():
|
||||
var cl: Dictionary = conv_lines[conv_index]
|
||||
conv_events.append({
|
||||
"speaker_id": 10,
|
||||
"target_id": 11,
|
||||
"speaker_name": cl.speaker,
|
||||
"target_name": cl.target,
|
||||
"occluded_line": cl.line,
|
||||
})
|
||||
elif _test_tick == conv_start + conv_total_ticks:
|
||||
conv_ended.append({"speaker_id": 10, "target_id": 11})
|
||||
|
||||
return {
|
||||
"tick": _test_tick,
|
||||
"version": Protocol.PROTOCOL_VERSION,
|
||||
@@ -417,6 +448,8 @@ func _test_snapshot() -> Dictionary:
|
||||
"current_dialogue": dialogue,
|
||||
"pending_recognitions": pending_recs,
|
||||
"gauntlet_mode": _test_gauntlet_mode,
|
||||
"conversation_events": conv_events,
|
||||
"conversation_ended": conv_ended,
|
||||
}
|
||||
|
||||
# Generate a small test room: 8x6 room with walls, a door, and floor
|
||||
|
||||
Reference in New Issue
Block a user