diff --git a/src/interact/drag.rs b/src/interact/drag.rs index 0dc18b2..f757a31 100644 --- a/src/interact/drag.rs +++ b/src/interact/drag.rs @@ -10,7 +10,10 @@ use super::{ stair_drag_at_pointer, text_at_pointer, }; use crate::app::DungeonApp; -use crate::layout::{blocked_room_cells, shortest_path_cells}; +use crate::layout::{ + blocked_room_cells, is_cell_inside, is_marker_inside, is_room_inside, is_stair_inside, + shortest_path_cells, +}; use crate::ui::AddTool; use eframe::egui; use egui::{Color32, Stroke}; @@ -57,10 +60,70 @@ pub fn handle_drag( && let Some((room_idx, offset_x, offset_y)) = room_at_pointer(app, pointer_pos, geometry) { app.push_undo_snapshot(); + let active_idx = app.settings.active_level_index; + let layout = &app.levels[active_idx]; + let outer_room = &layout.rooms[room_idx]; + + let mut nested_rooms = Vec::new(); + for (i, room) in layout.rooms.iter().enumerate() { + if i != room_idx && is_room_inside(room, outer_room) { + nested_rooms.push(i); + } + } + + let mut nested_start_markers = Vec::new(); + for (i, marker) in layout.start_markers.iter().enumerate() { + if is_marker_inside(marker, outer_room) { + nested_start_markers.push(i); + } + } + + let mut nested_end_markers = Vec::new(); + for (i, marker) in layout.end_markers.iter().enumerate() { + if is_marker_inside(marker, outer_room) { + nested_end_markers.push(i); + } + } + + let mut nested_trap_markers = Vec::new(); + for (i, marker) in layout.trap_markers.iter().enumerate() { + if is_marker_inside(marker, outer_room) { + nested_trap_markers.push(i); + } + } + + let mut nested_monster_markers = Vec::new(); + for (i, marker) in layout.monster_markers.iter().enumerate() { + if is_marker_inside(marker, outer_room) { + nested_monster_markers.push(i); + } + } + + let mut nested_text_labels = Vec::new(); + for (i, label) in layout.text_labels.iter().enumerate() { + if is_cell_inside(label.cell, outer_room) { + nested_text_labels.push(i); + } + } + + let mut nested_stairs = Vec::new(); + for (i, stair) in layout.stairs.iter().enumerate() { + if is_stair_inside(stair, outer_room) { + nested_stairs.push(i); + } + } + app.drag_state = Some(DragState::Room(RoomDragState { room_idx, offset_x, offset_y, + nested_rooms, + nested_start_markers, + nested_end_markers, + nested_trap_markers, + nested_monster_markers, + nested_text_labels, + nested_stairs, })); } @@ -118,9 +181,72 @@ pub fn drag_room_to_pointer( return; } - app.levels[app.settings.active_level_index].rooms[drag.room_idx].x = new_x; - app.levels[app.settings.active_level_index].rooms[drag.room_idx].y = new_y; + let delta_x = new_x as isize - old_x as isize; + let delta_y = new_y as isize - old_y as isize; + + { + let level = &mut app.levels[app.settings.active_level_index]; + level.rooms[drag.room_idx].x = new_x; + level.rooms[drag.room_idx].y = new_y; + + for &idx in &drag.nested_rooms { + if idx < level.rooms.len() { + level.rooms[idx].x = (level.rooms[idx].x as isize + delta_x) as usize; + level.rooms[idx].y = (level.rooms[idx].y as isize + delta_y) as usize; + } + } + for &idx in &drag.nested_start_markers { + if idx < level.start_markers.len() { + level.start_markers[idx].cell.0 = + (level.start_markers[idx].cell.0 as isize + delta_x) as usize; + level.start_markers[idx].cell.1 = + (level.start_markers[idx].cell.1 as isize + delta_y) as usize; + } + } + for &idx in &drag.nested_end_markers { + if idx < level.end_markers.len() { + level.end_markers[idx].cell.0 = + (level.end_markers[idx].cell.0 as isize + delta_x) as usize; + level.end_markers[idx].cell.1 = + (level.end_markers[idx].cell.1 as isize + delta_y) as usize; + } + } + for &idx in &drag.nested_trap_markers { + if idx < level.trap_markers.len() { + level.trap_markers[idx].cell.0 = + (level.trap_markers[idx].cell.0 as isize + delta_x) as usize; + level.trap_markers[idx].cell.1 = + (level.trap_markers[idx].cell.1 as isize + delta_y) as usize; + } + } + for &idx in &drag.nested_monster_markers { + if idx < level.monster_markers.len() { + level.monster_markers[idx].cell.0 = + (level.monster_markers[idx].cell.0 as isize + delta_x) as usize; + level.monster_markers[idx].cell.1 = + (level.monster_markers[idx].cell.1 as isize + delta_y) as usize; + } + } + for &idx in &drag.nested_text_labels { + if idx < level.text_labels.len() { + level.text_labels[idx].cell.0 = + (level.text_labels[idx].cell.0 as isize + delta_x) as usize; + level.text_labels[idx].cell.1 = + (level.text_labels[idx].cell.1 as isize + delta_y) as usize; + } + } + for &idx in &drag.nested_stairs { + if idx < level.stairs.len() { + level.stairs[idx].cell.0 = (level.stairs[idx].cell.0 as isize + delta_x) as usize; + level.stairs[idx].cell.1 = (level.stairs[idx].cell.1 as isize + delta_y) as usize; + } + } + } + reroute_corridors_for_room(app, drag.room_idx); + for &idx in &drag.nested_rooms { + reroute_corridors_for_room(app, idx); + } app.refresh_doors(); } diff --git a/src/interact/types.rs b/src/interact/types.rs index 8801e58..906f518 100644 --- a/src/interact/types.rs +++ b/src/interact/types.rs @@ -11,6 +11,13 @@ pub struct RoomDragState { pub room_idx: usize, pub offset_x: f32, pub offset_y: f32, + pub nested_rooms: Vec, + pub nested_start_markers: Vec, + pub nested_end_markers: Vec, + pub nested_trap_markers: Vec, + pub nested_monster_markers: Vec, + pub nested_text_labels: Vec, + pub nested_stairs: Vec, } #[derive(Debug, Clone)] diff --git a/src/layout/utils.rs b/src/layout/utils.rs index b34c92c..f2ef51a 100644 --- a/src/layout/utils.rs +++ b/src/layout/utils.rs @@ -128,6 +128,38 @@ pub fn overlaps_with_padding(a: &Room, b: &Room, padding: usize) -> bool { a_left < b_right && a_right > b_left && a_top < b_bottom && a_bottom > b_top } +// Test whether one room is completely inside another. +pub fn is_room_inside(inner: &Room, outer: &Room) -> bool { + inner.x >= outer.x + && inner.x + inner.width <= outer.x + outer.width + && inner.y >= outer.y + && inner.y + inner.height <= outer.y + outer.height +} + +// Test whether a cell is inside a room. +pub fn is_cell_inside(cell: (usize, usize), room: &Room) -> bool { + cell.0 >= room.x + && cell.0 < room.x + room.width + && cell.1 >= room.y + && cell.1 < room.y + room.height +} + +// Test whether an area marker is completely inside a room. +pub fn is_marker_inside(marker: &crate::layout::AreaMarker, room: &Room) -> bool { + marker.cell.0 >= room.x + && marker.cell.0 + marker.size <= room.x + room.width + && marker.cell.1 >= room.y + && marker.cell.1 + marker.size <= room.y + room.height +} + +// Test whether a staircase is completely inside a room. +pub fn is_stair_inside(stair: &crate::layout::Staircase, room: &Room) -> bool { + stair.cell.0 >= room.x + && stair.cell.0 + stair.width <= room.x + room.width + && stair.cell.1 >= room.y + && stair.cell.1 + stair.height <= room.y + room.height +} + // Checks whether rects overlap. #[allow(clippy::too_many_arguments)] pub fn rects_overlap(