feat(pathfinding): implement Phase 3 reactive pathing

- 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
This commit is contained in:
2026-03-18 21:33:38 +00:00
parent a6f6e7cb9d
commit 49f91de0c9
5 changed files with 40 additions and 0 deletions
+1
View File
@@ -27,6 +27,7 @@ impl Pig {
current_path: None, current_path: None,
path_index: 0, path_index: 0,
step_recovery: 0, step_recovery: 0,
validation_cooldown: 0,
}, },
sprite: Sprite { sprite: Sprite {
image: asset_server.load("pig.png"), image: asset_server.load("pig.png"),
+1
View File
@@ -24,6 +24,7 @@ impl Rabbit {
current_path: None, current_path: None,
path_index: 0, path_index: 0,
step_recovery: 0, step_recovery: 0,
validation_cooldown: 0,
}, },
sprite: Sprite { sprite: Sprite {
image: asset_server.load("rabbit.png"), image: asset_server.load("rabbit.png"),
+1
View File
@@ -24,6 +24,7 @@ impl Dorf {
current_path: None, current_path: None,
path_index: 0, path_index: 0,
step_recovery: 0, step_recovery: 0,
validation_cooldown: 0,
}, },
sprite: Sprite { sprite: Sprite {
image: asset_server.load("dorf.png"), image: asset_server.load("dorf.png"),
@@ -9,6 +9,21 @@ pub struct Ambulatory {
pub path_index: usize, pub path_index: usize,
pub target: Option<Vec3>, pub target: Option<Vec3>,
pub step_recovery: u32, pub step_recovery: u32,
pub validation_cooldown: u8,
}
impl Default for Ambulatory {
fn default() -> Self {
Self {
walk_speed: 1.0,
run_speed: 2.0,
current_path: None,
path_index: 0,
target: None,
step_recovery: 0,
validation_cooldown: 0,
}
}
} }
#[derive(Component)] #[derive(Component)]
@@ -502,6 +502,17 @@ pub fn movement(mut query: Query<(&mut Ambulatory, &mut Transform)>, tilemap: Re
return; return;
} }
if ambulatory.validation_cooldown > 0 {
ambulatory.validation_cooldown -= 1;
} else if let Some(path) = &ambulatory.current_path {
if !validate_next_steps(&tilemap, path, ambulatory.path_index, 3) {
ambulatory.current_path = None;
ambulatory.validation_cooldown = 10;
return;
}
ambulatory.validation_cooldown = 10;
}
if ambulatory.walk_speed > 0. { if ambulatory.walk_speed > 0. {
if ambulatory.step_recovery <= ambulatory.walk_speed as u32 { if ambulatory.step_recovery <= ambulatory.walk_speed as u32 {
ambulatory.step_recovery += 1; ambulatory.step_recovery += 1;
@@ -534,6 +545,17 @@ pub fn movement(mut query: Query<(&mut Ambulatory, &mut Transform)>, tilemap: Re
}); });
} }
fn validate_next_steps(tilemap: &TileMap, path: &[Vec3], start_index: usize, steps: usize) -> bool {
let end = (start_index + steps).min(path.len());
for i in start_index..end {
let pos = path[i].as_ivec3();
if !is_standable_tile(tilemap, pos) {
return false;
}
}
true
}
fn is_standable_tile(tilemap: &TileMap, pos: IVec3) -> bool { fn is_standable_tile(tilemap: &TileMap, pos: IVec3) -> bool {
let can_stand_in_tile = tilemap let can_stand_in_tile = tilemap
.floor_tiles .floor_tiles