diff --git a/src/features/pom/mod.rs b/src/features/pom/mod.rs index c8dffb4..ed55560 100644 --- a/src/features/pom/mod.rs +++ b/src/features/pom/mod.rs @@ -184,7 +184,7 @@ fn handle_interact( } } -fn perform_interaction( +pub fn perform_interaction( mut pom_query: Query<(&GridPosition, &mut InteractionTarget, &PathQueue)>, grid: Res, mut tile_query: Query<&mut TileState>, @@ -193,6 +193,7 @@ fn perform_interaction( mut commands: Commands, config: Res, mut session_tracker: ResMut, + mut notifications: ResMut, ) { for (pos, mut target_component, path_queue) in pom_query.iter_mut() { 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 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::, "That would trap you!"); + target_component.target = None; + target_component.action = None; + continue; + } + } + println!( "Performing interaction on tile ({}, {})", target.0, target.1 diff --git a/src/features/pom/utils.rs b/src/features/pom/utils.rs index 73ad8fb..0658684 100644 --- a/src/features/pom/utils.rs +++ b/src/features/pom/utils.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use std::cmp::Ordering; -use std::collections::{BinaryHeap, HashMap, VecDeque}; +use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; #[derive(Copy, Clone, Eq, PartialEq)] 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) } +/// Checks if Pom would be trapped in a small, isolated area if a specific tile is blocked +pub fn is_trapped( + start: (u32, u32), + blocked_pos: (u32, u32), + grid: &Grid, + get_tile_state: F, +) -> bool +where + F: Fn(Entity) -> Option, +{ + 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( start: (u32, u32), end: (u32, u32),