172 lines
8.0 KiB
Markdown
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?** |