When an NPC dies from starvation, their highest-familiarity companion enters mourning for 100 ticks (~half a day). Mourning NPCs skip work: they rest if energy is low, otherwise wander near home. Mourning_ticks counts down each tick through ActionExecutionSystem and is serialized for save/load continuity with backward-compatible default of 0. This gives the familiarity graph its first behavioral consequence.
NPCs whose home positions are within 4m of each other gain mutual familiarity at baseline 0.5. Stored per NPC as a Dictionary keyed by other npc_id, serialized through NPCStateRecord with backward-compatible fallback for old saves. Household pairs form naturally from the home_positions configuration in main.tscn. Includes save/load round-trip test verifying familiarity persists.
- Reduce hunger rate to 0.125/tick (~25/day) for ~6-7 day death timeline
- Slow energy drain: idle 0.125, travel 0.5, work 0.5, complete 0.0625/tick
- Use exact binary fractions for all rates to preserve float precision in save/load
- Starving/critically-hungry NPCs always override sleep to seek food first
- Extend starvation death threshold from 20 to 600 ticks (~3 days of starvation)
- Add calligraphy-style wind stroke particles (WindStrokes scene + shader)
- Two wind stroke emitters in valley and ridge for Ghibli atmosphere
- Rework terrain macro-variation colors for vibrant warm-green palette
- Enhance environment: stronger glow/bloom, higher saturation, warmer sky/fog/ambient
Add ACTION_SLEEP with home-position targeting and energy restoration. Embed schedule periods (SLEEP/MEAL/WORK/DISCRETIONARY) in ActionSelectionSystem with TimeOfDay from SimulationClock. NPCs sleep at night when energy is low, eat during meal periods when hungry, and work/discretionary the rest of the cycle. Home position stored per NPC and serialized through NPCStateRecord with backward-compatible fallback. Cycle duration persisted through save/restore. Includes headless test for sleep period, work period, meal hunger, energy restoration, serialization round-trip, and home targeting without world adapter.
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
Replace implicit randomness with seeded per-NPC RandomNumberGenerator
streams so the same seed always produces the same scenario outcome.
- Add SimulationClock (RefCounted): explicit fixed-step clock that
converts frame delta into simulation ticks while preserving sub-tick
remainder; replaces raw _process tick_timer accumulation
- Add simulation_seed export (int=1337) to SimulationManager; drive
per-NPC RNG streams via seed + npc_id + stream_id derivation
(stream 0=init, stream 1=wander)
- Add get_wander_offset(npc_id) to SimulationManager so wander targets
are deterministic per seed; WorldViewManager calls it instead of
local randf_range()
- Add get_state_snapshot() and get_state_checksum() to SimulationManager
for headless verification and future save/load
- Convert SimNPC/SimVillage const DEBUG_LOGS to instance var debug_logs
so headless tests can silence output without recompilation
- Pass RandomNumberGenerator through SimNPC._init() instead of using
global randf_range() for hunger/energy/position/scoring noise
- Remove obsolete farm_zone and forest_zone exports from
WorldViewManager and their marker/tree scene children from main.tscn;
NPC gather tasks use ResourceNode exclusively
- Rename TaskZone container to ActivityMarkers in main.tscn and update
all scene paths (player, WVM, tests) to match
- Add headless deterministic_simulation_test.gd: verifies clock
accuracy, same-seed equality, different-seed divergence, and
24-tick scenario checksum without loading main.tscn
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
Phase 1-2 of the ResourceNode migration plan:
- Create ResourceNode class (world/resource_nodes/) with extraction,
reservation, nearest-available selection, and debug label
- Place 4 resource instances in main.tscn (2 berry bushes, 2 trees)
- Add target_id to SimNPC for tracking which node an NPC is using
- Add set_npc_target_id and release_npc_reservation to SimulationManager
with cleanup on task change, completion, and death
- Update WorldViewManager to query available ResourceNodes first,
falling back to zone markers with a logged warning