extends Node2D @onready var world_renderer = $World @onready var camera = $Camera2D @onready var hud = $UILayer/HUD @onready var monologue_display = $UILayer/MonologueDisplay @onready var interaction_prompt = $InsertOverlay/InteractionPrompt # v0.1 single-line fallback @onready var interaction_list = $InsertOverlay/InteractionList # D-057: z-layer 6 @onready var world_radial = $InsertOverlay/WorldRadial # D-058: z-layer 6 @onready var inventory_grid = $UILayer/InventoryGrid # D-065: z-layer 7 @onready var stance_indicator = $UILayer/StanceIndicator # D-053: z-layer 7 @onready var cursor_renderer = $UILayer/CursorRenderer # D-056: z-layer 7 func _ready() -> void: print("The Settled Reach — client initialized") # Connect to simulation (will use test mode initially) SimBridge.connect_to_sim() func _process(_delta: float) -> void: # Main game loop: poll snapshot, apply state, flush input var snapshot = SimBridge.poll_snapshot() if snapshot != null: GameState.apply_snapshot(snapshot) # Update renderers with new state if world_renderer and world_renderer.has_method("update_from_state"): world_renderer.update_from_state() # D-057: Update interaction list from game state if interaction_list and interaction_list.has_method("update_from_state"): interaction_list.update_from_state() # D-065: Update inventory grid if inventory_grid and inventory_grid.has_method("update_from_state"): inventory_grid.update_from_state() # D-053: Update stance indicator if stance_indicator and stance_indicator.has_method("update_from_state"): stance_indicator.update_from_state() # Show monologue if server sent one this tick (#414) # Consume-once: set to null after showing to prevent re-display. # Single monologue per snapshot is guaranteed by server. if GameState.current_monologue != null and monologue_display: var mono: Dictionary = GameState.current_monologue monologue_display.show_monologue(mono.get("text", ""), mono.get("duration_seconds", 5.0)) GameState.current_monologue = null # Track camera to player position every frame (D-015: locked, no panning) # Camera2D smoothing handles interpolation — we just set the target camera.global_position = GameState.player_position * Constants.TILE_SIZE # Send queued input to simulation var inputs = InputMapper.flush_queue() for input in inputs: if input.action == InputMapper.Action.INTERACT: # D-057: prefer interaction list (multi-verb), fall back to prompt (v0.1) var target_id: int = -1 var verb: String = "" if interaction_list and interaction_list.has_method("get_interaction_target"): target_id = interaction_list.get_interaction_target() verb = interaction_list.get_selected_verb() if target_id < 0 and interaction_prompt: target_id = interaction_prompt.get_interaction_target() verb = interaction_prompt.get_selected_verb() # Always send struct form for Interact (#415) — server expects named fields if target_id >= 0: input["action_data"] = { "target_entity_id": target_id, "verb": verb, } else: input["action_data"] = { "target_entity_id": null, "verb": null, } SimBridge.send_input(input)