Enable Terrain3D collision_mode=1 so the sculpted landscape provides real physical ground for player and NPCs. Disable GreyboxGround collision (layer/mask = 0) -- it is no longer the active physical floor.
NpcVisual no longer flattens nav waypoint Y to its current elevation. It now follows 3D path points and uses gravity (30 m/s^2) like the player, settling to terrain surface via is_on_floor() + Terrain3D collision. The flat NavigationMesh_greybox still provides correct lateral guidance; physics handles vertical grounding on slopes.
This fixes the player-walking-through-slopes bug and makes NPC movement follow actual terrain elevation for the village area (gentle slopes, ~2.4m noise amplitude). All 10 scenarios pass.
Mouse wheel up/down smoothly zooms the camera in and out. Zoom range: close-in overview (5 high, 4 back) to wide survey (16 high, 12 back). Default sits at midpoint (10 high, 8 back) matching the previous fixed offset. Configurable via min_zoom_offset, max_zoom_offset, and zoom_step exports. Zoom interpolates with the same follow_speed as position tracking for consistent feel.
The file contained only 'extends Node' with no implementation, was referenced nowhere in any scene or script, and the uid sidecar was orphaned. The next interaction surface should be planned and implemented fresh when needed.
Auto-format all GDScript files using gdformat from gdtoolkit.
This is a baseline formatting pass to ensure consistent style:
- Normalizes indentation and spacing
- Wraps long lines to 100 characters
- Removes trailing whitespace
- Standardizes blank lines between functions
68 files reformatted, 8 files left unchanged (3rd-party addon files
with parse errors excluded).
- NpcVisual emits position_changed signal during _physics_process
- SimulationManager.synchronize_npc_position() writes active position
into SimNPC state on movement, arrival, and navigation failure
- WorldViewManager forwards position_changed and syncs before
despawn/reload
- SimNPC stores travel_target_position and has_travel_target as
versioned state; NPCStateRecord v2 persists them with v1 migration
- WorldViewManager.spawn_npc_visual() resumes travel for traveling NPCs
via SimulationManager.request_current_travel()
- WorldViewManager.despawn_npc_visual() syncs position before removal
- WorldViewManager.reload_npc_visual() spawns a visual from persisted
state without rerolling target selection
- tests/npc_visual_lifecycle_test.gd proves unload/reload preserves
action, target, reservation, position, RNG state, and checksum
- simulation_state_serialization_test adds legacy v1 NPC migration test
that does not invent an active travel target
NpcVisual:
- Add navigation_failed(sim_id) signal distinct from arrived_at_target
so the simulation layer can distinguish genuine arrival from failure
- Replace integer stuck_counter with delta-based stuck_time and
exported stuck_timeout (1.5s) for frame-rate-independent detection
- Add path_request_id / path_pending mechanism to reject stale
async path results when a new target is set or NPC dies
- On empty path or stuck movement, emit navigation_failed instead
of arriving; only emit arrived_at_target when truly close to target
- Reduce arrival_distance back to 0.6 now that navigation_failure
is properly signalled (no longer needed as a workaround)
- clamp arrival_distance reference in apply_dead_visual_state
SimNPC:
- Remove retry_count (replaced by navigation_failed signal pathway)
- Move last_task assignment from set_task() to task completion in
SimulationManager so it reflects the task that actually finished
SimulationManager:
- Add notify_npc_navigation_failed(): release reservation, record
last_task, send NPC to wander for 2s, emit task_changed signal
- Add notify_npc_target_unavailable() as public alias of above
- Guard extraction with node.reserved_by == npc.id to prevent
stale/illegitimate extraction when reservation is lost
- Guard fallback zone task completion (apply_npc_task) so it only
fires for non-resource tasks (gather_food/wood with no target
silently skip instead of applying zone values)
- Remove retry_count safety net (fully replaced by signal pipeline)
WorldViewManager:
- Connect navigation_failed signal in spawn_npc_visual()
- Skip target assignment for empty/idle/dead task states
- When no resource node available for gather_food/wood, call
notify_npc_target_unavailable() so NPC wanders instead of idling
- Add _on_npc_visual_navigation_failed handler delegating to
SimulationManager.notify_npc_navigation_failed()
- Remove retry_count references