feat: Prevent player from being trapped by planting

This commit is contained in:
demenik
2025-12-10 17:38:25 +01:00
parent 4942c28f52
commit ccab27b933
2 changed files with 83 additions and 2 deletions

View File

@@ -184,7 +184,7 @@ fn handle_interact(
} }
} }
fn perform_interaction( pub fn perform_interaction(
mut pom_query: Query<(&GridPosition, &mut InteractionTarget, &PathQueue)>, mut pom_query: Query<(&GridPosition, &mut InteractionTarget, &PathQueue)>,
grid: Res<Grid>, grid: Res<Grid>,
mut tile_query: Query<&mut TileState>, mut tile_query: Query<&mut TileState>,
@@ -193,6 +193,7 @@ fn perform_interaction(
mut commands: Commands, mut commands: Commands,
config: Res<GameConfig>, config: Res<GameConfig>,
mut session_tracker: ResMut<crate::features::phase::components::SessionTracker>, mut session_tracker: ResMut<crate::features::phase::components::SessionTracker>,
mut notifications: ResMut<Notifications>,
) { ) {
for (pos, mut target_component, path_queue) in pom_query.iter_mut() { for (pos, mut target_component, path_queue) in pom_query.iter_mut() {
if let Some(target) = target_component.target { if let Some(target) = target_component.target {
@@ -202,6 +203,17 @@ fn perform_interaction(
} }
if manhattan_distance(pos.x, pos.y, target.0, target.1) == 1 { if manhattan_distance(pos.x, pos.y, target.0, target.1) == 1 {
if let Some(actions::InteractionAction::Plant(_)) = &target_component.action {
if utils::is_trapped((pos.x, pos.y), target, &grid, |e| {
tile_query.get(e).ok().cloned()
}) {
notifications.error(None::<String>, "That would trap you!");
target_component.target = None;
target_component.action = None;
continue;
}
}
println!( println!(
"Performing interaction on tile ({}, {})", "Performing interaction on tile ({}, {})",
target.0, target.1 target.0, target.1

View File

@@ -1,6 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap, VecDeque}; use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq)]
pub struct Node { pub struct Node {
@@ -29,6 +29,75 @@ pub fn manhattan_distance(x1: u32, y1: u32, x2: u32, y2: u32) -> u32 {
x1.abs_diff(x2) + y1.abs_diff(y2) x1.abs_diff(x2) + y1.abs_diff(y2)
} }
/// Checks if Pom would be trapped in a small, isolated area if a specific tile is blocked
pub fn is_trapped<F>(
start: (u32, u32),
blocked_pos: (u32, u32),
grid: &Grid,
get_tile_state: F,
) -> bool
where
F: Fn(Entity) -> Option<TileState>,
{
let mut open_set = VecDeque::new();
let mut visited = HashSet::new();
open_set.push_back(start);
visited.insert(start);
if start == blocked_pos {
return true;
}
let max_search = 50;
let min_safe_area = 5;
while let Some(current) = open_set.pop_front() {
if visited.len() >= max_search {
return false;
}
let neighbors = [
(current.0 as i32 + 1, current.1 as i32),
(current.0 as i32 - 1, current.1 as i32),
(current.0 as i32, current.1 as i32 + 1),
(current.0 as i32, current.1 as i32 - 1),
];
for (nx, ny) in neighbors {
if nx < 0 || ny < 0 {
continue;
}
let next_pos = (nx as u32, ny as u32);
if next_pos == blocked_pos {
continue;
}
if visited.contains(&next_pos) {
continue;
}
let tile_entity = match grid.get_tile(next_pos) {
Ok(e) => e,
Err(_) => continue,
};
if let Some(state) = get_tile_state(tile_entity) {
if let TileState::Unclaimed = state {
return false;
}
if !state.is_blocking() {
visited.insert(next_pos);
open_set.push_back(next_pos);
}
}
}
}
visited.len() < min_safe_area
}
pub fn find_path( pub fn find_path(
start: (u32, u32), start: (u32, u32),
end: (u32, u32), end: (u32, u32),