diff --git a/src/layout.rs b/src/layout.rs index 934e19d..27a152e 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -52,6 +52,26 @@ pub struct DungeonLayout { pub doors: Vec, } +// Collect all room cells except those belonging to excluded room ids. +pub fn blocked_room_cells(rooms: &[Room], excluded_room_ids: &[usize]) -> HashSet<(usize, usize)> { + let excluded: HashSet = excluded_room_ids.iter().copied().collect(); + let mut blocked = HashSet::new(); + + for (room_idx, room) in rooms.iter().enumerate() { + if excluded.contains(&room_idx) { + continue; + } + + for x in room.x..(room.x + room.width) { + for y in room.y..(room.y + room.height) { + blocked.insert((x, y)); + } + } + } + + blocked +} + // Build a layout based on settings and a derived deterministic seed. pub fn generate_layout( cols: usize, @@ -178,9 +198,10 @@ pub fn generate_layout( for (start_room_id, end_room_id) in room_edges { let start = centers[start_room_id]; let end = centers[end_room_id]; + let blocked = blocked_room_cells(&rooms, &[start_room_id, end_room_id]); let path = if randomness <= 0.001 { - shortest_path_cells(start, end, cols, rows, &HashSet::new()) + shortest_path_cells(start, end, cols, rows, &blocked) } else { Some(noisy_path( start, @@ -188,10 +209,11 @@ pub fn generate_layout( cols, rows, randomness, + &blocked, &mut path_rng, )) } - .or_else(|| shortest_path_cells(start, end, cols, rows, &HashSet::new())); + .or_else(|| shortest_path_cells(start, end, cols, rows, &blocked)); if let Some(path) = path && path.len() >= 2 @@ -656,6 +678,7 @@ fn noisy_path( cols: usize, rows: usize, randomness: f32, + blocked: &HashSet<(usize, usize)>, rng: &mut SimpleRng, ) -> Vec<(usize, usize)> { if start == end { @@ -698,6 +721,10 @@ fn noisy_path( let mut best_score = f32::INFINITY; for &candidate in &neighbors { + if blocked.contains(&candidate) && candidate != end { + continue; + } + let step_dir = ( candidate.0 as isize - current.0 as isize, candidate.1 as isize - current.1 as isize, @@ -734,7 +761,7 @@ fn noisy_path( } if current != end - && let Some(tail) = shortest_path_cells(current, end, cols, rows, &HashSet::new()) + && let Some(tail) = shortest_path_cells(current, end, cols, rows, blocked) { for &cell in tail.iter().skip(1) { path.push(cell); diff --git a/src/main.rs b/src/main.rs index 06b6d19..7ba61d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,9 @@ use std::thread; use eframe::egui; use egui::{Color32, Stroke}; -use layout::{DoorSettings, DungeonLayout, corridor_cells, shortest_path_cells}; +use layout::{ + DoorSettings, DungeonLayout, blocked_room_cells, corridor_cells, shortest_path_cells, +}; use ui::{AddTool, UiSettings, draw_side_panel}; // Boot the native app and start the egui event loop. @@ -333,7 +335,6 @@ impl DungeonApp { // Recalculate corridor paths connected to a moved room. fn reroute_corridors_for_room(&mut self, room_idx: usize) { - let empty = HashSet::new(); for corridor in &mut self.layout.corridors { if corridor.start_room_id != room_idx && corridor.end_room_id != room_idx { continue; @@ -341,9 +342,15 @@ impl DungeonApp { let start = self.layout.rooms[corridor.start_room_id].center_cell(); let end = self.layout.rooms[corridor.end_room_id].center_cell(); - corridor.path = - shortest_path_cells(start, end, self.settings.cols, self.settings.rows, &empty) - .unwrap_or_else(|| vec![start, end]); + let blocked = blocked_room_cells( + &self.layout.rooms, + &[corridor.start_room_id, corridor.end_room_id], + ); + if let Some(path) = + shortest_path_cells(start, end, self.settings.cols, self.settings.rows, &blocked) + { + corridor.path = path; + } } } @@ -383,6 +390,7 @@ impl DungeonApp { self.drag_state = None; return; } + let corridor = &self.layout.corridors[drag.corridor_index]; let target = ( (grid_x.floor().max(0.0) as usize).min(self.settings.cols.saturating_sub(1)), @@ -395,6 +403,10 @@ impl DungeonApp { target, self.settings.cols, self.settings.rows, + &blocked_room_cells( + &self.layout.rooms, + &[corridor.start_room_id, corridor.end_room_id], + ), ) else { return; }; @@ -540,16 +552,7 @@ impl DungeonApp { return; }; - let mut blocked = HashSet::new(); - for room in &self.layout.rooms { - for x in room.x..(room.x + room.width) { - for y in room.y..(room.y + room.height) { - blocked.insert((x, y)); - } - } - } - blocked.remove(&start); - blocked.remove(&end); + let blocked = blocked_room_cells(&self.layout.rooms, &[start_room, end_room]); let Some(path) = shortest_path_cells(start, end, self.settings.cols, self.settings.rows, &blocked) @@ -1604,13 +1607,14 @@ fn reroute_path_through_cell( target_cell: (usize, usize), cols: usize, rows: usize, + blocked_room_cells_set: &HashSet<(usize, usize)>, ) -> Option> { let (&start, &end) = (original_path.first()?, original_path.last()?); if start == end { return None; } - let mut blocked = HashSet::new(); + let mut blocked = blocked_room_cells_set.clone(); blocked.insert(original_cell); blocked.remove(&start); blocked.remove(&end);