Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
171 lines
8.4 KiB
GDScript
171 lines
8.4 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
|
|
|
|
# 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)
|