extends Node signal game_id_changed(new_id: String) # Updated each frame from ObserverSnapshot data (Protocol format: {tick, entities, tiles}). # Entities use Protocol decoded format: {entity_id, x, y, z, kind: {variant, data}}. # Tiles use format: [{x, y, z, type}]. var current_snapshot: Dictionary = {} # D-085 (#258): Active game session identifier. Format: -- # Set by SessionManager.new_game() or SessionManager.resume_game(). # Empty string when no session is active (main menu state). var current_game_id: String = "": set(v): current_game_id = v game_id_changed.emit(v) var current_tick: int = 0 var player_position: Vector2 = Vector2.ZERO var visible_entities: Array = [] var visible_tiles: Array = [] var visible_positions: Dictionary = {} # Vector2i -> true, for fast fog lookups (normal LOS tiles) var boundary_positions: Dictionary = {} # Vector2i -> true, BoundaryWall margin tiles (#585) # — visible in fog but not explored # v2 fields (D-015, D-031) var game_time: Dictionary = {} # {day, time_of_day, day_phase, tick_rate} or empty var player_facing: String = "North" # 8-directional facing direction var visibility_sectors: Dictionary = {} # Vector2i -> "Forward"/"Peripheral" # Player entity ID — the first entity is assumed to be the player (will be # refined when the server assigns explicit player entity IDs). var player_entity_id: int = 1 # #241: Follow target — entity_id of the NPC the player is following, -1 when not following. # Stub for server ticket #241 (Follow verb). Client reads this for camera/UI behavior. var follow_target_id: int = -1 # v4 fields (#404/#405) var nearby_interactions: Array = [] # [{entity_id, entity_type, distance, verbs: [{kind, label, priority, available}]}] # v5 fields (#414) var current_monologue: Variant = null # {id, text, duration_seconds, priority, is_urgent} or null # v6 fields (#449, D-053, D-065) var player_stance: String = "Walk" # Sprint/Walk/Careful/Crouch var player_inventory: Array = [] # [{item_id, name, slot}] # v7 fields (#435, D-061/D-062) var current_dialogue: Variant = null # {npc_name, npc_entity_id, speech, options: [{text, response_id, priority}]} # or null # D-064: true while dialogue box is visible or fading out (300ms). # InputMapper suppresses movement when this is true. var dialogue_active: bool = false # v8 fields (#496): Gauntlet mode — room timer + personal bests var room_id: Variant = null # String room_id from snapshot, null in non-gauntlet mode var gauntlet_mode: bool = false # true when snapshot includes gauntlet_mode flag # OQ-07 (#522): Insert active state — false suppresses verb labels (z-layer 6). # Cursor shape changes still fire when false (D-056 option a). # v0.1 assumption: always true — both playable characters (detective and smuggler) # have neural inserts. Future characters without inserts would receive false from # the server's "insert_active" snapshot field, disabling all z-layer-6 UI. var insert_active: bool = true # #175: World seed for deterministic simulation (D-010, D-029). # Set by SessionManager.new_game(), sent to server via StartupMessage in SimBridge. # Same seed → same EntanglementConfig → same NPC population across playthroughs. # Persists for the session lifetime; not overwritten by apply_snapshot(). var world_seed: int = 0 # #507: RNG seed for replay determinism — populated from snapshot "rng_seed" field. # Null in v0.1 (server does not yet send this field; protocol change required). var rng_seed: Variant = null # v15 fields (#554, D-085): save/load result from server. # {success: bool, kind: "save"|"load", error: Variant} or null. # One-shot: consumed by main.gd after display, then set back to null. var save_result: Variant = null # v18 fields (#580): debug console response from server. # {command: String, text: String, success: bool} or null. # One-shot: consumed by main.gd and forwarded to DebugConsole, then set to null. var debug_response: Variant = null # #257: Pending load path — set by main menu "Load Game" selection. # main.gd sends LOAD_GAME on startup if non-empty, then clears this field. # Format: user://saves//.sav or "" if no pending load. var pending_load_path: String = "" # #898: Free camera mode — camera decoupled from player, WASD pans camera directly. var free_camera_mode: bool = false # #705: Character visual descriptor — set by character_creation.gd on confirmation. # Passed to EntityRenderer for the player entity's CharacterVisual on game start. # Null when no custom appearance has been selected (fallback: default descriptor). # Type is CharacterVisualDescriptor — untyped to avoid autoload parse-order issue. var character_visual_descriptor = null # #646: AI-Enhanced Dialogue enabled state (D-138). # Runtime toggle — true means the LLM re-voicing pipeline should run (server-side). # Default: true (opt-out model per D-138 §8). Hardware detector may disable at startup # if RAM is insufficient. Persisted to server SQLite via ChangeSettings IPC. var ai_enhanced_dialogue_enabled: bool = true # v20 fields (#627, D-138): settings response from server. # One-shot: {kind: "full", settings: [{key, value}]} or {kind: "ack", success, key, error} or null. # "full" response hydrates ai_enhanced_dialogue_enabled (server is authoritative for persisted state). var settings_response: Variant = null # v21 fields (#824, D-181): Economy snapshot from server. # Dictionary keyed by system_id → { price_current, price_trend, trade_flow_volume, # corporate_presence, stockpile_weeks, production_vs_baseline, official_coverage_ratio } # Null when no economy data in the current snapshot. var economy_snapshot: Variant = null # v23 fields (#614): Bookmark catalog from server. # One-shot response to RequestBookmarkCatalog. Array of bookmark Dictionaries: # [{id, title, subtitle, flavor, default_location, allowed_locations, # allowed_locations_cultures, career, starting_capital_tractus}] # Empty array when no catalog has been received yet. var bookmark_catalog: Array = [] # v7 fields (#431, D-059/D-060) var pending_recognitions: Array = [] # [{entity_id, x, y, z, remaining_ticks, total_delay_ticks}] # v8 fields (#305, D-028): NPC follow-up after player dialogue choice var dialogue_response: Variant = null # {line_id, text, speaker_entity_id} # 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}] # 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 = [] # #125, D-018: Close-range sound events for positional 2D audio. # Format: [{x, y, event_type, range_category}] — consumed once per tick in main.gd. var close_sound_events: Array = [] # D-071 (#530): Consecutive ticks without player position change. # D-020: Server-authoritative — read from snapshot "stationary_ticks" field. # Fallback: client-side accumulation (deprecated, remove when server populates field). # ListeningFocus boost activates at 30+ ticks (main.gd manages the dip). var stationary_ticks: int = 0 # DEPRECATED: _prev_player_position moved to SnapshotHandler (client-side accumulation fallback). # D-073 (#529): Server-authoritative zone_id from the player's current tile. # D-020: Read directly from snapshot "zone_id" field. # Fallback: client-side tile lookup (deprecated, remove when server populates field). # Empty string when zone_id field absent. var current_zone_id: String = "" func apply_snapshot(snapshot: Dictionary) -> void: # Autoload parse-order: class_name types are not registered when autoloads compile. # load() returns the cached resource after the first call — essentially free per-tick. var SH := load("res://scripts/snapshot_handler.gd") SH.apply(snapshot)