Files
settled-reach/client/scripts/main.gd
T
jpmschweitzerandClaude Opus 4.6 3c106aa2d0 feat(simulation): complete interaction and monologue pipelines (#413, #414, #415)
Three fixes to make the gameplay loop functional end-to-end:

- Add Interactable component to NPC spawn so E-prompt detection works
- Build monologue trigger system (enter_location + time_idle) with
  MonologueBuffer/MonologueState components, wire through ObserverSnapshot
  as current_monologue field, decode on client and display via HUD
- Change PlayerAction::Interact from unit to struct variant carrying
  optional target_entity_id and verb fields

Bumps protocol version from 4 to 5. Regenerates MessagePack fixtures.
All 200 tests pass (170 unit + 30 integration).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 19:23:47 +01:00

52 lines
1.8 KiB
GDScript

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 = $UILayer/InteractionPrompt
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()
# Show monologue if server sent one this tick (#414)
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 # Consume — don't re-show next frame
# 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:
var target_id: int = interaction_prompt.get_interaction_target()
# Always send struct form for Interact (#415) — server expects named fields
if target_id >= 0:
input["action_data"] = {
"target_entity_id": target_id,
"verb": interaction_prompt.get_selected_verb(),
}
else:
input["action_data"] = {
"target_entity_id": null,
"verb": null,
}
SimBridge.send_input(input)