diff --git a/client/scripts/autoloads/game_state.gd b/client/scripts/autoloads/game_state.gd index 979008f4a..881be4bd3 100644 --- a/client/scripts/autoloads/game_state.gd +++ b/client/scripts/autoloads/game_state.gd @@ -70,6 +70,21 @@ var dialogue_response: Variant = null # {line_id, text, speaker_entity_id} var conversation_events: Array = [] # [{speaker_id, target_id, speaker_name, target_name, occluded_line}] var conversation_ended: Array = [] # [{speaker_id, target_id}] +# v10 fields (#151, D-013): Discovered POIs from server (#148/#149). +# Format: [{poi_id, name, x, y, z, category}]. Persists between snapshots unless +# server explicitly sends an empty array (cleared locations are not typical in v0.1). +# Populated from snapshot "poi_list" field — only updated when field present. +var discovered_pois: Array = [] + +# v14 fields (#174, #242): Character-filtered examine result. +# {entity_id, text, confidence} or null. Auto-dismisses on client after 4-6 seconds. +var current_examine_result: Variant = null + +# v14 fields (#264, D-041): Player knowledge graph dump for journal panel. +# {entities: [{entity_id, name, confidence, source, state, relationship, last_observed_tick}], +# facts: [{fact_id, confidence, source, state, acquired_tick}]} +var player_knowledge: Variant = null + # #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 = [] @@ -240,6 +255,25 @@ func apply_snapshot(snapshot: Dictionary) -> void: medium_sound_events = [] close_sound_events = [] + # v10: discovered_pois (#151, D-013) — server sends POIs discovered by the player. + # Accepts "discovered_pois" or "poi_list" key — both map to the same client field. + # Only update if the field is present — absence means "no change since last tick". + if snapshot.has("discovered_pois") and snapshot.discovered_pois is Array: + discovered_pois = snapshot.discovered_pois + elif snapshot.has("poi_list") and snapshot.poi_list is Array: + discovered_pois = snapshot.poi_list + + # v14: examine_result (#174, #242) — character-filtered observation from Examine verb. + if snapshot.has("examine_result") and snapshot.examine_result is Dictionary: + current_examine_result = snapshot.examine_result + else: + current_examine_result = null + + # v14: player_knowledge (#264, D-041) — partial KG dump for journal panel. + # Only update when field is present (null means no change, server sends when KG changes). + if snapshot.has("player_knowledge") and snapshot.player_knowledge is Dictionary: + player_knowledge = snapshot.player_knowledge + # D-073 (#529): O(1) zone_id lookup. Build coord→tile dict from member visible_tiles # (populated above from either "tiles" test-mode key or "visible_tiles" live key). # Must use the member var, not snapshot.visible_tiles, so test mode is covered. diff --git a/client/scripts/protocol/protocol.gd b/client/scripts/protocol/protocol.gd index 492dc219d..c18b3c274 100644 --- a/client/scripts/protocol/protocol.gd +++ b/client/scripts/protocol/protocol.gd @@ -11,7 +11,7 @@ class_name Protocol ## Protocol version — must match server PROTOCOL_VERSION in bridge/types.rs. ## Reject snapshots where version != this value. -const PROTOCOL_VERSION: int = 13 +const PROTOCOL_VERSION: int = 14 # -- Decode: bytes from server → GDScript types -------------------------------- @@ -206,6 +206,70 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant: "target_id": int(raw_end.get("target_id", 0)), }) + # v14: poi_list (#151) — discovered POIs for minimap rendering. + # Each entry: {poi_id, name, x, y, z, category}. Positions in sim tile coords. + var poi_list: Array = [] + var raw_pois: Variant = raw.get("poi_list") + if raw_pois is Array: + for raw_poi in raw_pois: + if raw_poi is Dictionary and raw_poi.has("poi_id") and raw_poi.has("x") and raw_poi.has("y"): + poi_list.append({ + "poi_id": str(raw_poi["poi_id"]), + "name": str(raw_poi.get("name", "")), + "x": int(raw_poi["x"]), + "y": int(raw_poi["y"]), + "z": int(raw_poi.get("z", 0)), + "category": str(raw_poi.get("category", "Location")), + }) + + # v14: examine_result (#174, #242) — character-filtered observation text. + # {entity_id, text, confidence} or null. Auto-dismisses on client after 4-6 seconds. + var examine_result: Variant = null + var raw_examine: Variant = raw.get("examine_result") + if raw_examine is Dictionary and raw_examine.has("text"): + examine_result = { + "entity_id": int(raw_examine.get("entity_id", 0)), + "text": str(raw_examine["text"]), + "confidence": str(raw_examine.get("confidence", "KnowsOf")), + } + + # v14: player_knowledge (#264, D-041) — partial KG dump for journal panel. + # {entities: [{entity_id, name, confidence, source, state, relationship, last_observed_tick}], + # facts: [{fact_id, confidence, source, state, acquired_tick}]} + var player_knowledge: Variant = null + var raw_pk: Variant = raw.get("player_knowledge") + if raw_pk is Dictionary: + var kg_entities: Array = [] + var raw_kg_entities: Variant = raw_pk.get("entities") + if raw_kg_entities is Array: + for raw_ke in raw_kg_entities: + if raw_ke is Dictionary and raw_ke.has("entity_id"): + kg_entities.append({ + "entity_id": int(raw_ke["entity_id"]), + "name": str(raw_ke.get("name", "Unknown")), + "confidence": str(raw_ke.get("confidence", "Suspects")), + "source": str(raw_ke.get("source", "")), + "state": str(raw_ke.get("state", "Active")), + "relationship": str(raw_ke.get("relationship", "Unknown")), + "last_observed_tick": int(raw_ke.get("last_observed_tick", 0)), + }) + var kg_facts: Array = [] + var raw_kg_facts: Variant = raw_pk.get("facts") + if raw_kg_facts is Array: + for raw_kf in raw_kg_facts: + if raw_kf is Dictionary and raw_kf.has("fact_id"): + kg_facts.append({ + "fact_id": str(raw_kf["fact_id"]), + "confidence": str(raw_kf.get("confidence", "Suspects")), + "source": str(raw_kf.get("source", "")), + "state": str(raw_kf.get("state", "Active")), + "acquired_tick": int(raw_kf.get("acquired_tick", 0)), + }) + player_knowledge = { + "entities": kg_entities, + "facts": kg_facts, + } + return { "tick": tick, "entities": entities, @@ -223,6 +287,9 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant: "pending_recognitions": pending_recognitions, "conversation_events": conversation_events, "conversation_ended": conversation_ended, + "poi_list": poi_list, + "examine_result": examine_result, + "player_knowledge": player_knowledge, } diff --git a/client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack b/client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack index 0a0876c58..56a60bcd1 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack and b/client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack b/client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack index 6aafb8c2c..e0784d8f3 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack and b/client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack b/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack index 7d6d91200..b9228db57 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack and b/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack b/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack index cd39ea41f..a286d7833 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack and b/client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack b/client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack index a195fdf6b..84ef21619 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack and b/client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_empty.msgpack b/client/tests/fixtures/msgpack/snapshot_empty.msgpack index 0a0876c58..56a60bcd1 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_empty.msgpack and b/client/tests/fixtures/msgpack/snapshot_empty.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack b/client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack index a332c6c50..3ebbb6094 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack and b/client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_one_npc.msgpack b/client/tests/fixtures/msgpack/snapshot_one_npc.msgpack index 0093a3ac1..17c284558 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_one_npc.msgpack and b/client/tests/fixtures/msgpack/snapshot_one_npc.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_player.msgpack b/client/tests/fixtures/msgpack/snapshot_player.msgpack index e919024ce..9908361a7 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_player.msgpack and b/client/tests/fixtures/msgpack/snapshot_player.msgpack differ diff --git a/client/tests/fixtures/msgpack/snapshot_v2_full.msgpack b/client/tests/fixtures/msgpack/snapshot_v2_full.msgpack index b7d86d7de..c2faadd09 100644 Binary files a/client/tests/fixtures/msgpack/snapshot_v2_full.msgpack and b/client/tests/fixtures/msgpack/snapshot_v2_full.msgpack differ