Files
settled-reach/client/scripts/autoloads/game_state.gd
T
jpmschweitzerandClaude Opus 4.6 949d721eac feat(ui): economics monitor insert panel with placeholder data (#824)
New implant panel at implant/economics: system selector, 6-commodity
price table with trend indicators, GDP strip. Composed from D-169
component library. Ring buffer caches last 20 ticks per system.
Snapshot routing wired through snapshot_handler → GameState →
snapshot_consumers → economics_panel. Placeholder prices shown
until server ships EconomySnapshot (#822).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 13:22:20 +02:00

177 lines
8.7 KiB
GDScript

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: <YYYYMMDD>-<HHMMSS>-<hex6>
# 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
# #122 (D-032): Character lattice profile — selects monologue text colour palette.
# "lattice_augmented" = detective, "lattice_baseline" = smuggler.
# Server sends this field as part of the player's capability snapshot.
var lattice_profile: String = "lattice_baseline"
# 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/<game-id>/<filename>.sav or "" if no pending load.
var pending_load_path: String = ""
# #588: Character archetype chosen at character select screen.
# "detective" or "smuggler". Set before game scene loads; sent in StartupMessage.
# Default: "detective" — fallback for legacy saves without character.txt.
var character_archetype: String = "detective"
# #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
# 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)