Files
dorf/NEXT_SESSION_PROMPT.md
T

172 lines
8.0 KiB
Markdown

# 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.
```rust
// 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()`.
```rust
} 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:
```rust
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
```bash
# 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?**