Files
dorf/NEXT_SESSION_PROMPT.md

8.0 KiB

Session Continuation Prompt

A) Starting Position

The project had a working task system where dorfs would:

  1. Get assigned jobs (FellTree, HaulCargo)
  2. Walk to job locations
  3. Chop trees
  4. Haul logs

Last Known Good Commit: The user started from commit 3c74a68 (Refactor job queue: add JobState enum, improve pathfinding fallback). However, the system was NOT fully working - dorfs would freeze because:

  • The old provisional pathfinding used a node limit (4096) that would fail on distant jobs
  • Job assignment was using provisional pathfinding to check "reachability" which is incorrect
  • Jobs would stay Unclaimed forever because dorfs couldn't be assigned

B) Changes Made This Session and WHY

1. New Traversal Distance Function (src/entities/shared_systems/pathfinding.rs)

WHAT: Added calculate_traversal_distance() - uses full A* with 15,000 node limit (not provisional) to check if a path actually exists.

WHY: The old approach used calculate_provisional_path() which:

  • Returns partial paths that hit node limits
  • Was NOT designed for reachability checking
  • Would return "no path" on distant jobs even though paths exist

2. JobState Calculating (src/entities/tasks/job_queue.rs)

WHAT: Added Calculating(Vec<Entity>) state to lock dorfs during pathfinding.

WHY: Prevents race conditions where two jobs try to assign the same dorf.

3. Candidate Filter Fix (src/entities/tasks/job_pathfinding.rs)

WHAT: Changed filter from !queue.is_empty() && !matches!(state, Active) to allow Idle tasks.

// BEFORE: Only dorfs with empty queues
if !queue.is_empty() { return false; }

// AFTER: Dorfs that are idle (empty queue OR Idle task)
let is_idle = queue.is_empty()
    || current_task.map(|t| matches!(t, Task::Idle { .. })).unwrap_or(false);
let is_busy = matches!(*state, TaskState::Active) && !is_idle;
if is_busy { return false; }

WHY: The job_assignment_system gives Idle tasks to dorfs, then task_executor_system promotes them to Active. All dorfs had Active+Idle state, so they were ALL filtered out.

4. Target Clearing Fix (src/entities/tasks/job_pathfinding.rs)

WHAT: Removed ambulatory.target = None from job assignment.

WHY: The job_pathfinding_system was clearing ambulatory.target on assignment, but then the executor would set it, and the next frame job_pathfinding would clear it again. This oscillation prevented movement.

5. HaulCargo Target Fix (src/entities/tasks/executor.rs)

WHAT: Added branch to set ambulatory.target when approach.is_some() and target.is_none().

} else if ambulatory.target.is_none() {
    if let Some(approach_tile) = *approach {
        ambulatory.target = Some(Vec3::new(...));
        ambulatory.current_path = None;
    }
}

WHY: When job_pathfinding_system pre-computed the approach tile, the executor would skip the "find approach" block and never set the target. Dorfs had approach position but no path to walk there.

C) Current Plan for Improvement

The architecture is correct:

  1. job_pathfinding_system (FixedUpdate) - Finds idle dorfs, calculates traversal distances, assigns jobs with approach targets
  2. job_assignment_system (FixedUpdate) - Gives Idle tasks to dorfs with empty queues
  3. task_executor_system (FixedUpdate) - Executes tasks, handles movement via pathfinding

Planned improvements (NOT YET IMPLEMENTED):

  • Throttling: Only process N jobs per tick to prevent CPU spikes
  • Expanding scope: Start with 5 nearest dorfs, expand to 20, then all if all fail
  • Async pathfinding: Move expensive pathfinding to background threads

D) Current Issues and State

Last Test Result (output5.log):

  • Jobs ARE being assigned - [PATHFIND] SUCCESS messages appeared
  • ChopTree works - Trees are being chopped (120 tick countdown completes)
  • HaulCargo jobs created - 8 HaulCargo jobs created after tree fell
  • BUG: target=None - HaulCargo tasks showed target=None meaning no movement path was set

Last Fix Applied:

The HaulCargo target fix (commit 8e3e8b8) should resolve the target=None issue. This fix was NOT tested yet.

Known Remaining Issues:

  1. Approach tile z-level mismatch - The BFS finds standable tiles at cargo's z-level, but the dorf might be on a different floor. The z coordinate in approach might not match the dorf's current z.

  2. Cargo position staleness - If cargo moves after job is created, the job's cargo_pos is stale.

  3. No pathfinding failure recovery - If pathfinding fails, there's no retry or fallback.

E) Plan to Get to Working State

DO NOT MAKE MORE FIXES WITHOUT TESTING FIRST.

  1. Run the game with output logging - Capture output6.log after the HaulCargo target fix.

  2. Verify the fix works - Look for:

    • [HAUL] {:?} setting target to approach {:?} messages
    • target=Some(...) instead of target=None
    • Dorfs moving toward cargo (transform positions changing)
    • [HAUL] {:?} arrived at approach {:?}, picking up cargo messages
    • [HAUL] {:?} picked up {:?} → hauling to {:?} messages
  3. If still broken - Use targeted debug logging:

    info!("[DEBUG] approach={:?} target={:?} transform={:?}", approach, ambulatory.target, transform.translation);
    

    Add this to MovingToCargo step in executor.rs to trace the exact state.

  4. Check z-level issues - If dorfs aren't moving, check if the approach tile is reachable. The approach z might not match the dorf's z:

    • Dorf at (x, y, z=0) needs to reach cargo at (x, y, z=16)
    • Approach tile should be adjacent to cargo AND reachable from dorf's z-level
  5. Only after full working state - Consider implementing:

    • Throttling (MAX_JOBS_PER_TICK)
    • Expanding scope for island scenarios
    • Async pathfinding

Key Files to Focus On

src/entities/tasks/job_pathfinding.rs - Job assignment, pathfinding trigger
src/entities/tasks/executor.rs - Task execution, movement triggers
src/entities/shared_systems/pathfinding.rs - calculate_traversal_distance()
src/entities/tasks/job_queue.rs - JobState, job management

Debug Commands

# Check compilation
cargo check 2>&1 | tail -20

# Run with logging
cargo run 2>&1 | tee output.log

# Search for specific log patterns
grep -E "\[PATHFIND\] SUCCESS" output.log
grep -E "\[HAUL\]" output.log | tail -50
grep -E "target=None" output.log | head -10
grep -E "setting target" output.log

Critical Context: The Z-Level System

IMPORTANT: READ src/world/generation/terrain.rs AND src/entities/shared_systems/pathfinding.rs FIRST.

The world is NOT flat. The terrain is generated using Perlin noise:

  • The surface height varies - z is NOT "underground" or "surface", it's HEIGHT
  • A tile at z=0 might be surface at one location and underground/void at another
  • Trees, rocks, items can spawn at various z-levels based on terrain
  • Dorfs walk on the surface at whatever z-level that happens to be

The existing entity pathfinding system (in src/entities/shared_systems/pathfinding.rs) WORKS. It correctly handles z-levels. Study it to understand:

  1. How is_standable() works across z-levels
  2. How ALLOWED_MOVES includes diagonal 3D movement
  3. How entities navigate from z=0 to z=16 or vice versa

The HaulCargo approach finding BFS (in executor.rs) only searches at cargo's z-level. This may be correct OR may need to search 3D-adjacent tiles like the entity pathfinding does.

If dorfs freeze after the current fix:

  1. Compare find_all_standable_adjacent() in executor.rs with the working pathfinding
  2. Check if approach tiles found are actually reachable from the dorf's position
  3. Look at job_pathfinding.rs: find_all_standable_adjacent() - does it search 3D?
  4. The job_pathfinding system calls calculate_traversal_distance() which uses full A* - if this returns Err(()), the job stays Unclaimed

Key question: The traversal distance calculation in src/entities/shared_systems/pathfinding.rs uses the same pathfinding as entity movement. If that works for movement, why wouldn't it work for job assignment?**