added removeable and placable doors

This commit is contained in:
grimsace
2026-03-12 11:36:36 -05:00
parent c588f7d1ae
commit 4bc890ff27
4 changed files with 260 additions and 5 deletions
+14 -3
View File
@@ -587,7 +587,9 @@ fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
("rgb(220,70,70)", None)
};
if let Some((x1, y1, x2, y2)) = door_line_points(&g, door.from, door.to, door.width) {
if let Some((x1, y1, x2, y2)) =
door_line_points(&g, door.from, door.to, door_render_width(door))
{
if let Some(pattern) = dash {
let _ = writeln!(
s,
@@ -938,7 +940,8 @@ fn draw_door(
color: (u8, u8, u8, u8),
style: DoorStyle,
) {
let Some((x1, y1, x2, y2)) = door_line_points(g, door.from, door.to, door.width) else {
let Some((x1, y1, x2, y2)) = door_line_points(g, door.from, door.to, door_render_width(door))
else {
return;
};
draw_line(pixmap, x1, y1, x2, y2, width, color, style);
@@ -1097,7 +1100,7 @@ fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<((usize, usize),
return edges;
}
let width = door.width.max(1) as isize;
let width = door_render_width(door).max(1) as isize;
let min_offset = -((width - 1) / 2);
let max_offset = width / 2;
@@ -1127,3 +1130,11 @@ fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<((usize, usize),
edges
}
fn door_render_width(door: &Door) -> usize {
if door.span_width {
door.width.max(1)
} else {
1
}
}
+5
View File
@@ -31,6 +31,7 @@ pub struct Door {
pub from: (usize, usize),
pub to: (usize, usize),
pub width: usize,
pub span_width: bool,
pub locked: bool,
pub archway: bool,
}
@@ -245,6 +246,7 @@ pub fn apply_doors(
from: edge.0,
to: edge.1,
width: corridor.width.max(1),
span_width: true,
locked: place_door && rng.next_f32() <= locked,
archway: !place_door,
});
@@ -258,6 +260,7 @@ pub fn apply_doors(
from: edge.0,
to: edge.1,
width: corridor.width.max(1),
span_width: true,
locked: place_door && rng.next_f32() <= locked,
archway: !place_door,
});
@@ -276,6 +279,7 @@ pub fn apply_doors(
from: edge.0,
to: edge.1,
width: 1,
span_width: false,
locked: rng.next_f32() <= locked,
archway: false,
});
@@ -288,6 +292,7 @@ pub fn apply_doors(
from: edge.0,
to: edge.1,
width: 1,
span_width: false,
locked: rng.next_f32() <= locked,
archway: false,
});
+231 -2
View File
@@ -33,6 +33,7 @@ struct DungeonApp {
resize_state: Option<RoomResizeState>,
hover_room_idx: Option<usize>,
hover_corridor_idx: Option<usize>,
hover_door_idx: Option<usize>,
}
impl Default for DungeonApp {
@@ -62,6 +63,7 @@ impl Default for DungeonApp {
resize_state: None,
hover_room_idx: None,
hover_corridor_idx: None,
hover_door_idx: None,
}
}
}
@@ -193,6 +195,7 @@ impl eframe::App for DungeonApp {
self.draw_add_overlay(&painter, &geometry);
self.draw_resize_overlay(&painter, &geometry);
self.draw_corridor_hover_overlay(&painter, &geometry);
self.draw_door_hover_overlay(&painter, &geometry);
});
}
}
@@ -445,6 +448,16 @@ impl DungeonApp {
self.settings.add_tool = AddTool::None;
}
}
AddTool::Door | AddTool::LockedDoor => {
self.add_corridor_drag = None;
if response.clicked()
&& let Some(pointer_pos) = response.interact_pointer_pos()
{
let locked = self.settings.add_tool == AddTool::LockedDoor;
self.add_door_at_pointer(pointer_pos, geometry, locked);
self.settings.add_tool = AddTool::None;
}
}
AddTool::None => {
self.add_corridor_drag = None;
}
@@ -552,17 +565,35 @@ impl DungeonApp {
let Some(pointer_pos) = ctx.pointer_hover_pos() else {
self.hover_room_idx = None;
self.hover_corridor_idx = None;
self.hover_door_idx = None;
return;
};
if let Some(edge) = self.door_edge_at_pointer(pointer_pos, geometry) {
let target = normalized_edge(edge.0, edge.1);
self.hover_door_idx = self.layout.doors.iter().position(|d| {
door_edges_for(d, self.settings.cols, self.settings.rows)
.iter()
.any(|e| normalized_edge(e.0, e.1) == target)
});
if self.hover_door_idx.is_some() {
self.hover_room_idx = None;
self.hover_corridor_idx = None;
return;
}
}
if let Some((room_idx, _, _)) = self.room_at_pointer(pointer_pos, geometry) {
self.hover_room_idx = Some(room_idx);
self.hover_corridor_idx = None;
self.hover_door_idx = None;
} else {
self.hover_room_idx = None;
if let Some(corridor_drag) = self.corridor_drag_at_pointer(pointer_pos, geometry) {
self.hover_corridor_idx = Some(corridor_drag.corridor_index);
self.hover_door_idx = None;
} else {
self.hover_corridor_idx = None;
self.hover_door_idx = None;
}
}
}
@@ -777,7 +808,71 @@ impl DungeonApp {
}
}
fn draw_door_hover_overlay(&self, painter: &egui::Painter, geometry: &GridGeometry) {
let Some(idx) = self.hover_door_idx else {
return;
};
let Some(door) = self.layout.doors.get(idx) else {
return;
};
let color = Color32::from_gray(200);
let stroke = Stroke::new(2.0, color);
draw_door_line(
painter,
geometry,
door.from,
door.to,
door_render_width(door),
stroke,
DoorLineStyle::LongDash,
);
}
fn delete_at_pointer(&mut self, pointer_pos: egui::Pos2, geometry: &GridGeometry) {
if let Some(edge) = self.door_edge_at_pointer(pointer_pos, geometry) {
let target = normalized_edge(edge.0, edge.1);
if let Some(idx) = self.layout.doors.iter().position(|d| {
door_edges_for(d, self.settings.cols, self.settings.rows)
.iter()
.any(|e| normalized_edge(e.0, e.1) == target)
}) {
let edges = door_edges_for(
&self.layout.doors[idx],
self.settings.cols,
self.settings.rows,
);
let mut spawned = Vec::new();
for edge in edges {
if normalized_edge(edge.0, edge.1) == target {
continue;
}
if self
.layout
.doors
.iter()
.any(|d| normalized_edge(d.from, d.to) == normalized_edge(edge.0, edge.1))
{
continue;
}
spawned.push(layout::Door {
from: edge.0,
to: edge.1,
width: 1,
span_width: false,
locked: false,
archway: true,
});
}
let door = &mut self.layout.doors[idx];
door.locked = false;
door.archway = true;
door.width = 1;
door.span_width = false;
self.layout.doors.extend(spawned);
return;
}
}
if let Some((room_idx, _, _)) = self.room_at_pointer(pointer_pos, geometry) {
self.delete_room(room_idx);
return;
@@ -811,6 +906,132 @@ impl DungeonApp {
self.drag_state = None;
self.refresh_doors();
}
fn add_door_at_pointer(
&mut self,
pointer_pos: egui::Pos2,
geometry: &GridGeometry,
locked: bool,
) {
let Some(edge) = self.door_edge_at_pointer(pointer_pos, geometry) else {
return;
};
let target = normalized_edge(edge.0, edge.1);
if let Some(idx) = self.layout.doors.iter().position(|d| {
door_edges_for(d, self.settings.cols, self.settings.rows)
.iter()
.any(|e| normalized_edge(e.0, e.1) == target)
}) {
let edges = door_edges_for(
&self.layout.doors[idx],
self.settings.cols,
self.settings.rows,
);
let mut spawned = Vec::new();
for edge in edges {
if normalized_edge(edge.0, edge.1) == target {
continue;
}
if self
.layout
.doors
.iter()
.any(|d| normalized_edge(d.from, d.to) == normalized_edge(edge.0, edge.1))
{
continue;
}
spawned.push(layout::Door {
from: edge.0,
to: edge.1,
width: 1,
span_width: false,
locked: false,
archway: true,
});
}
let door = &mut self.layout.doors[idx];
door.locked = locked;
door.archway = false;
door.width = 1;
door.span_width = false;
self.layout.doors.extend(spawned);
return;
}
let corridor_cells = corridor_cells(&self.layout, self.settings.cols, self.settings.rows);
let a_in_room = room_index_at_cell(&self.layout.rooms, edge.0).is_some();
let b_in_room = room_index_at_cell(&self.layout.rooms, edge.1).is_some();
let a_in_corridor = corridor_cells.contains(&edge.0);
let b_in_corridor = corridor_cells.contains(&edge.1);
if !(a_in_room ^ b_in_room) {
return;
}
if !(a_in_corridor ^ b_in_corridor) {
return;
}
self.layout.doors.push(layout::Door {
from: edge.0,
to: edge.1,
width: 1,
span_width: false,
locked,
archway: false,
});
}
fn door_edge_at_pointer(
&self,
pointer_pos: egui::Pos2,
geometry: &GridGeometry,
) -> Option<((usize, usize), (usize, usize))> {
let (grid_x, grid_y) = pointer_to_grid(pointer_pos, geometry)?;
let cell_x = grid_x.floor() as isize;
let cell_y = grid_y.floor() as isize;
if cell_x < 0
|| cell_y < 0
|| cell_x >= self.settings.cols as isize
|| cell_y >= self.settings.rows as isize
{
return None;
}
let fx = grid_x - grid_x.floor();
let fy = grid_y - grid_y.floor();
let left = fx;
let right = 1.0 - fx;
let top = fy;
let bottom = 1.0 - fy;
let mut candidates = [
(left, (cell_x - 1, cell_y), (cell_x, cell_y)),
(right, (cell_x + 1, cell_y), (cell_x, cell_y)),
(top, (cell_x, cell_y - 1), (cell_x, cell_y)),
(bottom, (cell_x, cell_y + 1), (cell_x, cell_y)),
];
candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
let (dist, a, b) = candidates[0];
if dist > 0.2 {
return None;
}
let (ax, ay) = a;
let (bx, by) = b;
if ax < 0
|| ay < 0
|| bx < 0
|| by < 0
|| ax >= self.settings.cols as isize
|| bx >= self.settings.cols as isize
|| ay >= self.settings.rows as isize
|| by >= self.settings.rows as isize
{
return None;
}
Some(((ax as usize, ay as usize), (bx as usize, by as usize)))
}
}
#[derive(Debug, Clone)]
@@ -1117,7 +1338,7 @@ fn draw_layout(
geometry,
door.from,
door.to,
door.width,
door_render_width(door),
Stroke::new(door_width_px, color),
style,
);
@@ -1287,7 +1508,7 @@ fn door_edges_for(
return edges;
}
let width = door.width.max(1) as isize;
let width = door_render_width(door).max(1) as isize;
let min_offset = -((width - 1) / 2);
let max_offset = width / 2;
@@ -1318,6 +1539,14 @@ fn door_edges_for(
edges
}
fn door_render_width(door: &layout::Door) -> usize {
if door.span_width {
door.width.max(1)
} else {
1
}
}
fn pointer_to_grid(pointer_pos: egui::Pos2, geometry: &GridGeometry) -> Option<(f32, f32)> {
if !geometry.rect.contains(pointer_pos) {
return None;
+10
View File
@@ -153,6 +153,8 @@ pub enum AddTool {
None,
Room,
Corridor,
Door,
LockedDoor,
}
#[derive(Debug, Clone, Copy, Default)]
@@ -557,12 +559,20 @@ fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings) {
ui.horizontal(|ui| {
ui.selectable_value(&mut settings.add_tool, AddTool::Room, "Add Room");
ui.selectable_value(&mut settings.add_tool, AddTool::Corridor, "Add Corridor");
ui.selectable_value(&mut settings.add_tool, AddTool::Door, "Add Door");
ui.selectable_value(
&mut settings.add_tool,
AddTool::LockedDoor,
"Add Locked Door",
);
});
ui.add_space(6.0);
match settings.add_tool {
AddTool::Room => ui.label("Click to place a room at the grid cell."),
AddTool::Corridor => ui.label("Click-drag to place a corridor between two cells."),
AddTool::Door => ui.label("Click a room/corridor edge to place a door."),
AddTool::LockedDoor => ui.label("Click a room/corridor edge to place a locked door."),
AddTool::None => ui.label("Select a tool to add rooms or corridors."),
};
}