feat(client): add fog entity visualization and wire Sprint 7 UI (#431, #434)

Fog entity cognitive delay rendering (D-059/D-060): sonar-style sound
pings (3 concentric rings, 1.5s fade), unrecognized grey blobs with
breathing pulse, D-033 color transition at 50% recognition progress,
±0.5 tile position drift. FogEntities node at z:950 between fog
shader and InsertOverlay.

Protocol v7 bump to match server PR #23 (pending_recognitions field).
Wires dialogue box and fog entities into game loop with mock test data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 01:19:24 +01:00
co-authored by Claude Opus 4.6
parent 2d56131221
commit 2659de0c28
7 changed files with 276 additions and 2 deletions
+18
View File
@@ -29,6 +29,12 @@ var current_monologue: Variant = null # {id, text, duration_seconds} or null
var player_stance: String = "Walk" # Sprint/Walk/Careful/Crouch
var player_inventory: Array = [] # [{item_id, name, slot}]
# v7 fields (#434, D-061)
var current_dialogue: Variant = null # {npc_name, speech, options: [String]} or null
# v7 fields (#431, D-059/D-060)
var pending_recognitions: Array = [] # [{entity_id, x, y, z, remaining_ticks, total_delay_ticks}]
func apply_snapshot(snapshot: Dictionary) -> void:
current_snapshot = snapshot
@@ -96,6 +102,18 @@ func apply_snapshot(snapshot: Dictionary) -> void:
else:
player_inventory = []
# v7: current_dialogue (#434, D-061)
if snapshot.has("current_dialogue") and snapshot.current_dialogue is Dictionary:
current_dialogue = snapshot.current_dialogue
else:
current_dialogue = null
# v7: pending_recognitions (#431, D-059/D-060)
if snapshot.has("pending_recognitions") and snapshot.pending_recognitions is Array:
pending_recognitions = snapshot.pending_recognitions
else:
pending_recognitions = []
# v2: visible_tiles with visibility sectors
# Derives visible_positions when not explicitly provided (real server mode)
if snapshot.has("visible_tiles") and snapshot.visible_tiles is Array and snapshot.visible_tiles.size() > 0:
+46
View File
@@ -9,6 +9,7 @@ var _test_tick: int = 0
var _test_player_pos: Vector2i = Vector2i(10, 10)
var _test_facing: String = "North"
var _test_input_queue: Array = [] # Queued actions for test mode
var _test_in_dialogue: bool = false # Mock dialogue state (#434)
var _last_snapshot: Variant = null # Most recent decoded snapshot (consumed by poll_snapshot)
var _outbound_buffer: Array[Dictionary] = [] # Raw inputs awaiting batch encode + transport
@@ -38,6 +39,7 @@ func reset_test_state() -> void:
_test_player_pos = Vector2i(10, 10)
_test_facing = "North"
_test_input_queue.clear()
_test_in_dialogue = false
# Change connection state and emit signal
func _set_state(new_state: ConnectionState) -> void:
@@ -263,12 +265,22 @@ func _test_snapshot() -> Dictionary:
# Process queued inputs
for action_name in _test_input_queue:
if action_name == "Interact":
# Mock dialogue trigger (#434): if near NPC, start dialogue
var npc_pos := Vector2i(12, 9)
var dist := absi(_test_player_pos.x - npc_pos.x) + absi(_test_player_pos.y - npc_pos.y)
if dist <= 2 and _test_has_los(_test_player_pos, npc_pos):
_test_in_dialogue = true
continue
var delta := _action_to_delta(action_name)
var new_pos := _test_player_pos + delta
if _test_is_walkable(new_pos):
_test_player_pos = new_pos
if delta != Vector2i.ZERO:
_test_facing = _delta_to_facing(delta)
# Walk-away dismisses dialogue (D-064)
if _test_in_dialogue:
_test_in_dialogue = false
_test_input_queue.clear()
var px := _test_player_pos.x
@@ -320,6 +332,38 @@ func _test_snapshot() -> Dictionary:
"duration_seconds": 5.0,
}
# v7: mock dialogue (#434, D-061) — triggered by Interact near NPC
# One-shot: dialogue data sent once on the trigger tick, not every frame
var dialogue: Variant = null
if _test_in_dialogue:
dialogue = {
"npc_name": "Kael",
"speech": "Haven't seen you around the transit hub before. You new to Sova, or just passing through?",
"options": [
"Just arrived. Still getting my bearings.",
"Passing through. Know where I can find work?",
"I'm looking for someone.",
],
}
_test_in_dialogue = false
# v7: mock pending_recognitions (#431, D-059/D-060) — cognitive delay fog entity
# Entity at (13, 12) in fog: starts as grey blob, transitions to recognized over 6 ticks.
# Cycles every 12 ticks: 6 ticks recognizing, 6 ticks off (simulates repeat encounters).
var pending_recs: Array = []
var cycle_pos := _test_tick % 12
if cycle_pos < 6:
var total_delay := 6
var remaining := total_delay - cycle_pos
pending_recs.append({
"entity_id": 100,
"x": 13.5,
"y": 12.5,
"z": 0,
"remaining_ticks": remaining,
"total_delay_ticks": total_delay,
})
return {
"tick": _test_tick,
"version": Protocol.PROTOCOL_VERSION,
@@ -338,6 +382,8 @@ func _test_snapshot() -> Dictionary:
"visible_positions": _test_visible_positions(),
"nearby_interactions": nearby,
"current_monologue": monologue,
"current_dialogue": dialogue,
"pending_recognitions": pending_recs,
}
# Generate a small test room: 8x6 room with walls, a door, and floor