- Add assigned_this_frame HashSet to track dorfs receiving jobs
- Filter out already-assigned dorfs from subsequent job assignments
- Add debug logging for spawn position (z-level)
- Add debug logging for is_standable failures
- Add debug logging for falling/snapping movement behavior
This fixes the critical bug where one dorf could be assigned multiple
jobs in the same frame, causing earlier assignments to be lost due to
queue.clear(). Each dorf now receives at most one job per frame.
- Add JobId (rolling u16 counter) to track jobs through their lifecycle
- Add job_id field to all Task variants (Idle, GoTo, ChopTree, HaulCargo, DropHauled)
- Add unclaim_job() method to requeue failed jobs for reassignment
- Change task failure paths to use unclaim_job() instead of complete_job_by_id()
- This allows failed jobs to be picked up by another dorf instead of being stuck
- Add to_job_kind() and get_job_id() helper methods to Task
- Fix pathfinding is_standable_tile debug logging spam
- Fix chunk bounds check in ChunkData::is_standable (was checking wrong z range)
Fix 1 — insert_fixture now updates ChunkData bitsets:
Log trunks (can_stand_in=false) now block movement in is_standable.
Leaf canopy (can_stand_in=true) remains walkable.
Fix 2 — Diagonal-aware collision avoidance:
Sidestep priority: left+forward, left, right+forward, right.
If all blocked: push through (excuse me), advance path, apply one-step
delay (speed-modulated recovery). Does not stack delays.
Fix 3 — Movement direction tracking:
Ambulatory now has move_direction (Vec2) and step_history ([i16; 4]).
TileOccupancy tracks per-tile direction hints for Stage 2 collision
(convoy skip, E/S yield) via direction_at().
move_direction is smoothed from: 2 historical steps, current confirmed
step, and 2-step path lookahead.
Ignore: add *.patch and *.diff to .gitignore
collision_delay was causing entities to skip step_recovery ticks, freezing
movement whenever the next tile was occupied. Combined with the ordering fix
(collision_delay placed before step_recovery), entities were stuck at
path_index=0 indefinitely in spawn clusters.
Remove collision_delay entirely — step_recovery already provides the
per-tick pacing, and the natural next-tick retry (via left-step or
normal movement) handles occupied tiles without an artificial delay.
Actual thresholds (grass weight=50): dorfs move after 6 ticks, pigs after
26 ticks, rabbits after 2 ticks. Without collision_delay interference,
movement is now governed purely by step_recovery.
- Add TileOccupancy resource and rebuild_tile_occupancy system to track
per-tile entity counts for soft collision avoidance
- Add collision_delay to Ambulatory; entities try relative-left step on
occupied tiles, wait 1 tick, then push through
- Fix leaf canopy standability: leaves are now can_stand_in=true,
can_stand_on=false (walkable, not standable-on)
- Delete FloorTilePrefab / FixtureTilePrefab / FloorTile / FixtureTile /
TileState (all fully dead — TileMap + ChunkData are sole truth)
- Delete tile_spawns from TerrainBlob (populated but never consumed)
- Delete leaf ghost entity spawn in forestry (orphaned invisible ECS entity)
- Replace log prefab spawn with inline commands.spawn(Transform, Visibility)
- Add TileMap::remove_fixture for future digging/explosion use
- Skip collision avoidance when current tile has >2 entities (handles spawn
cluster deadlock)
- Add validation_cooldown field to Ambulatory component
- Implement validate_next_steps to check walkability of next 3 path nodes
- Integrate validation into movement system (every 10 frames)
- Trigger re-path when validation fails (path blocked or invalid)
- Entities now detect and recover from invalid paths automatically