added removeable and placable doors
This commit is contained in:
+14
-3
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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."),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user