fix: four interlocking pathfinding bugs causing panic, failure, and yo-yo teleportation

1. CSV Overflow Panic: Use saturating_sub to prevent underflow when total_failed_paths > n
2. Heuristic Scale Mismatch: octile_distance_3d now divides by ITILE_SIZE to match g-score units
3. Goal Z Offset: Correct target.as_ivec3() - ivec3(0,0,1) not ITILE_SIZE (target already has +1.0)
4. Gravity Yo-Yo: Clear current_path and target when entity falls to prevent teleportation loop
This commit is contained in:
2026-03-18 17:49:07 +00:00
parent 00cbd92491
commit e9ef5ebbcd
2 changed files with 1908 additions and 6 deletions
+1900
View File
File diff suppressed because it is too large Load Diff
+8 -6
View File
@@ -183,8 +183,8 @@ pub fn prepare_paths(
continue; continue;
}; };
let start = transform.translation.as_ivec3(); let start = transform.translation.as_ivec3();
let goal = target.as_ivec3() - ivec3(0, 0, ITILE_SIZE); let goal = target.as_ivec3() - ivec3(0, 0, 1);
let distance = octile_distance_3d(start, goal) / ITILE_SIZE; let distance = octile_distance_3d(start, goal);
if distance <= PATHFINDER_SHORT_PATH_MAX_TILES { if distance <= PATHFINDER_SHORT_PATH_MAX_TILES {
let path = calculate_path_benchmarked(&tilemap, start, goal); let path = calculate_path_benchmarked(&tilemap, start, goal);
@@ -352,6 +352,8 @@ pub fn movement(mut query: Query<(&mut Ambulatory, &mut Transform)>, tilemap: Re
let current_pos = transform.translation; let current_pos = transform.translation;
if !is_standable_tile(&tilemap, current_pos.as_ivec3()) { if !is_standable_tile(&tilemap, current_pos.as_ivec3()) {
transform.translation.z -= TILE_SIZE; transform.translation.z -= TILE_SIZE;
ambulatory.current_path = None;
ambulatory.target = None;
return; return;
} }
@@ -433,9 +435,9 @@ fn calculate_movement_cost(move_dir: IVec3) -> i32 {
} }
fn octile_distance_3d(a: IVec3, b: IVec3) -> i32 { fn octile_distance_3d(a: IVec3, b: IVec3) -> i32 {
let dx = (a.x - b.x).abs(); let dx = (a.x - b.x).abs() / ITILE_SIZE;
let dy = (a.y - b.y).abs(); let dy = (a.y - b.y).abs() / ITILE_SIZE;
let dz = (a.z - b.z).abs(); let dz = (a.z - b.z).abs() / ITILE_SIZE;
let (dmax, dmid, dmin) = sorted_desc(dx, dy, dz); let (dmax, dmid, dmin) = sorted_desc(dx, dy, dz);
10 * dmax + 4 * dmid + dmin 10 * dmax + 4 * dmid + dmin
} }
@@ -767,7 +769,7 @@ fn write_benchmark_csv(bench: &PathfindingBenchmark, filename: &str) -> std::io:
let duration = bench.path_calc_times_us.get(i).copied().unwrap_or(0); let duration = bench.path_calc_times_us.get(i).copied().unwrap_or(0);
let length = bench.path_lengths.get(i).copied().unwrap_or(0); let length = bench.path_lengths.get(i).copied().unwrap_or(0);
let nodes = bench.nodes_expanded.get(i).copied().unwrap_or(0); let nodes = bench.nodes_expanded.get(i).copied().unwrap_or(0);
let success = i < (n - bench.total_failed_paths as usize); let success = i < n.saturating_sub(bench.total_failed_paths as usize);
writeln!(file, "{},{},{},{},{}", i, duration, length, nodes, success)?; writeln!(file, "{},{},{},{},{}", i, duration, length, nodes, success)?;
} }