added options to add windows
This commit is contained in:
@@ -81,6 +81,10 @@ pub fn handle_add_tool(app: &mut DungeonApp, response: &egui::Response, geometry
|
|||||||
app.add_corridor_drag = None;
|
app.add_corridor_drag = None;
|
||||||
handle_add_door_tool(app, response, geometry);
|
handle_add_door_tool(app, response, geometry);
|
||||||
}
|
}
|
||||||
|
AddTool::Window => {
|
||||||
|
app.add_corridor_drag = None;
|
||||||
|
handle_add_window_tool(app, response, geometry);
|
||||||
|
}
|
||||||
AddTool::Text => {
|
AddTool::Text => {
|
||||||
app.add_corridor_drag = None;
|
app.add_corridor_drag = None;
|
||||||
app.add_door_drag = None;
|
app.add_door_drag = None;
|
||||||
@@ -149,6 +153,178 @@ pub fn handle_add_door_tool(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handles click-and-drag painting for manual window tools.
|
||||||
|
pub fn handle_add_window_tool(
|
||||||
|
app: &mut DungeonApp,
|
||||||
|
response: &egui::Response,
|
||||||
|
geometry: &GridGeometry,
|
||||||
|
) {
|
||||||
|
if response.drag_started() {
|
||||||
|
app.add_door_drag = Some(AddDoorDrag {
|
||||||
|
painted_edges: Default::default(),
|
||||||
|
snapshot_taken: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.drag_started() || response.dragged() || response.clicked())
|
||||||
|
&& let Some(pointer_pos) = response.interact_pointer_pos()
|
||||||
|
{
|
||||||
|
paint_window_at_pointer(app, pointer_pos, geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.drag_stopped() {
|
||||||
|
app.add_door_drag = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paints one window edge during a manual window drag.
|
||||||
|
pub fn paint_window_at_pointer(
|
||||||
|
app: &mut DungeonApp,
|
||||||
|
pointer_pos: egui::Pos2,
|
||||||
|
geometry: &GridGeometry,
|
||||||
|
) {
|
||||||
|
let Some(edge) = door_edge_at_pointer(app, pointer_pos, geometry) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let target = normalized_edge(edge.0, edge.1);
|
||||||
|
|
||||||
|
if let Some(drag) = &app.add_door_drag
|
||||||
|
&& drag.painted_edges.contains(&target)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let needs_snapshot = app
|
||||||
|
.add_door_drag
|
||||||
|
.as_ref()
|
||||||
|
.map(|drag| !drag.snapshot_taken)
|
||||||
|
.unwrap_or(true);
|
||||||
|
if needs_snapshot {
|
||||||
|
app.push_undo_snapshot();
|
||||||
|
if let Some(drag) = &mut app.add_door_drag {
|
||||||
|
drag.snapshot_taken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if add_window_at_edge(app, edge)
|
||||||
|
&& let Some(drag) = &mut app.add_door_drag
|
||||||
|
{
|
||||||
|
drag.painted_edges.insert(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds or updates a manual window on a specific cell edge.
|
||||||
|
pub fn add_window_at_edge(app: &mut DungeonApp, edge: ((usize, usize), (usize, usize))) -> bool {
|
||||||
|
if app.settings.active_level_index >= app.levels.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout = &mut app.levels[app.settings.active_level_index];
|
||||||
|
|
||||||
|
let target = normalized_edge(edge.0, edge.1);
|
||||||
|
|
||||||
|
// Check if a door already exists at this edge
|
||||||
|
if layout.doors.iter().any(|d| {
|
||||||
|
door_edges_for(d, app.settings.cols, app.settings.rows)
|
||||||
|
.iter()
|
||||||
|
.any(|e| normalized_edge(e.0, e.1) == target)
|
||||||
|
}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a window already exists at this edge
|
||||||
|
if layout.windows.iter().any(|w| {
|
||||||
|
let win_edge = match w.side {
|
||||||
|
layout::WindowSide::Left => {
|
||||||
|
((w.cell.0, w.cell.1), (w.cell.0.saturating_sub(1), w.cell.1))
|
||||||
|
}
|
||||||
|
layout::WindowSide::Right => ((w.cell.0, w.cell.1), (w.cell.0 + 1, w.cell.1)),
|
||||||
|
layout::WindowSide::Top => {
|
||||||
|
((w.cell.0, w.cell.1), (w.cell.0, w.cell.1.saturating_sub(1)))
|
||||||
|
}
|
||||||
|
layout::WindowSide::Bottom => ((w.cell.0, w.cell.1), (w.cell.0, w.cell.1 + 1)),
|
||||||
|
};
|
||||||
|
normalized_edge(win_edge.0, win_edge.1) == target
|
||||||
|
}) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_boundary = layout::is_room_boundary_edge(&layout.rooms, edge.0, edge.1);
|
||||||
|
|
||||||
|
// Only allow windows on room boundaries (no corridors for windows usually)
|
||||||
|
if !is_boundary {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine cell and side for the window
|
||||||
|
let (cell, side) = if edge.0.0 == edge.1.0 {
|
||||||
|
// Vertical edge
|
||||||
|
let top = edge.0.1.min(edge.1.1);
|
||||||
|
// We need to know which cell is the room cell.
|
||||||
|
let a_in = layout.rooms.iter().any(|r| {
|
||||||
|
edge.0.0 >= r.x
|
||||||
|
&& edge.0.0 < r.x + r.width
|
||||||
|
&& edge.0.1 >= r.y
|
||||||
|
&& edge.0.1 < r.y + r.height
|
||||||
|
});
|
||||||
|
if a_in {
|
||||||
|
(
|
||||||
|
edge.0,
|
||||||
|
if edge.0.1 == top {
|
||||||
|
layout::WindowSide::Bottom
|
||||||
|
} else {
|
||||||
|
layout::WindowSide::Top
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
edge.1,
|
||||||
|
if edge.1.1 == top {
|
||||||
|
layout::WindowSide::Bottom
|
||||||
|
} else {
|
||||||
|
layout::WindowSide::Top
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Horizontal edge
|
||||||
|
let left = edge.0.0.min(edge.1.0);
|
||||||
|
let a_in = layout.rooms.iter().any(|r| {
|
||||||
|
edge.0.0 >= r.x
|
||||||
|
&& edge.0.0 < r.x + r.width
|
||||||
|
&& edge.0.1 >= r.y
|
||||||
|
&& edge.0.1 < r.y + r.height
|
||||||
|
});
|
||||||
|
if a_in {
|
||||||
|
(
|
||||||
|
edge.0,
|
||||||
|
if edge.0.0 == left {
|
||||||
|
layout::WindowSide::Right
|
||||||
|
} else {
|
||||||
|
layout::WindowSide::Left
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
edge.1,
|
||||||
|
if edge.1.0 == left {
|
||||||
|
layout::WindowSide::Right
|
||||||
|
} else {
|
||||||
|
layout::WindowSide::Left
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
layout.windows.push(layout::Window {
|
||||||
|
cell,
|
||||||
|
side,
|
||||||
|
width: 1,
|
||||||
|
span_width: false,
|
||||||
|
});
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
// Converts the active add tool into a manual door kind.
|
// Converts the active add tool into a manual door kind.
|
||||||
pub fn manual_door_kind_from_tool(tool: AddTool) -> Option<ManualDoorKind> {
|
pub fn manual_door_kind_from_tool(tool: AddTool) -> Option<ManualDoorKind> {
|
||||||
match tool {
|
match tool {
|
||||||
@@ -243,6 +419,8 @@ pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry
|
|||||||
if let Some(edge) = door_edge_at_pointer(app, pointer_pos, geometry) {
|
if let Some(edge) = door_edge_at_pointer(app, pointer_pos, geometry) {
|
||||||
let target = normalized_edge(edge.0, edge.1);
|
let target = normalized_edge(edge.0, edge.1);
|
||||||
let active_index = app.settings.active_level_index;
|
let active_index = app.settings.active_level_index;
|
||||||
|
|
||||||
|
// Try deleting a door first
|
||||||
if let Some(idx) = app.levels[active_index].doors.iter().position(|d| {
|
if let Some(idx) = app.levels[active_index].doors.iter().position(|d| {
|
||||||
door_edges_for(d, app.settings.cols, app.settings.rows)
|
door_edges_for(d, app.settings.cols, app.settings.rows)
|
||||||
.iter()
|
.iter()
|
||||||
@@ -262,6 +440,25 @@ pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry
|
|||||||
layout.doors.remove(idx);
|
layout.doors.remove(idx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try deleting a window if no door was found at this edge
|
||||||
|
if let Some(idx) = app.levels[active_index].windows.iter().position(|w| {
|
||||||
|
let win_edge = match w.side {
|
||||||
|
layout::WindowSide::Left => {
|
||||||
|
((w.cell.0, w.cell.1), (w.cell.0.saturating_sub(1), w.cell.1))
|
||||||
|
}
|
||||||
|
layout::WindowSide::Right => ((w.cell.0, w.cell.1), (w.cell.0 + 1, w.cell.1)),
|
||||||
|
layout::WindowSide::Top => {
|
||||||
|
((w.cell.0, w.cell.1), (w.cell.0, w.cell.1.saturating_sub(1)))
|
||||||
|
}
|
||||||
|
layout::WindowSide::Bottom => ((w.cell.0, w.cell.1), (w.cell.0, w.cell.1 + 1)),
|
||||||
|
};
|
||||||
|
normalized_edge(win_edge.0, win_edge.1) == target
|
||||||
|
}) {
|
||||||
|
app.push_undo_snapshot();
|
||||||
|
app.levels[active_index].windows.remove(idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stair_idx) = stair_at_pointer(app, pointer_pos, geometry) {
|
if let Some(stair_idx) = stair_at_pointer(app, pointer_pos, geometry) {
|
||||||
@@ -814,6 +1011,21 @@ pub fn draw_add_tool_ghost(app: &DungeonApp, painter: &egui::Painter, geometry:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AddTool::Window => {
|
||||||
|
if let Some((edge_a, edge_b)) = door_edge_at_pointer(app, pointer_pos, geometry) {
|
||||||
|
let door_width_px = (geometry.cell_size / 5.0).max(1.0);
|
||||||
|
let ghost_color = Color32::from_rgb(70, 130, 220).gamma_multiply(ghost_alpha);
|
||||||
|
crate::rendering::draw_door_line(
|
||||||
|
painter,
|
||||||
|
geometry,
|
||||||
|
edge_a,
|
||||||
|
edge_b,
|
||||||
|
1,
|
||||||
|
Stroke::new(door_width_px, ghost_color),
|
||||||
|
crate::rendering::DoorLineStyle::Solid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
AddTool::None | AddTool::Corridor => {}
|
AddTool::None | AddTool::Corridor => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -689,6 +689,7 @@ pub fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut S
|
|||||||
ui.selectable_value(&mut settings.add_tool, AddTool::Door, "Door");
|
ui.selectable_value(&mut settings.add_tool, AddTool::Door, "Door");
|
||||||
ui.selectable_value(&mut settings.add_tool, AddTool::LockedDoor, "Locked");
|
ui.selectable_value(&mut settings.add_tool, AddTool::LockedDoor, "Locked");
|
||||||
ui.selectable_value(&mut settings.add_tool, AddTool::SecretDoor, "Secret");
|
ui.selectable_value(&mut settings.add_tool, AddTool::SecretDoor, "Secret");
|
||||||
|
ui.selectable_value(&mut settings.add_tool, AddTool::Window, "Window");
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
@@ -769,6 +770,9 @@ pub fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut S
|
|||||||
AddTool::SecretDoor => {
|
AddTool::SecretDoor => {
|
||||||
ui.label("Left-click or drag along room/corridor edges to place secret doors.");
|
ui.label("Left-click or drag along room/corridor edges to place secret doors.");
|
||||||
}
|
}
|
||||||
|
AddTool::Window => {
|
||||||
|
ui.label("Left-click or drag along room edges to place windows.");
|
||||||
|
}
|
||||||
AddTool::Text => {
|
AddTool::Text => {
|
||||||
ui.label("Left-click to place the text label (edit text in options above).");
|
ui.label("Left-click to place the text label (edit text in options above).");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ pub enum AddTool {
|
|||||||
Door,
|
Door,
|
||||||
LockedDoor,
|
LockedDoor,
|
||||||
SecretDoor,
|
SecretDoor,
|
||||||
|
Window,
|
||||||
Text,
|
Text,
|
||||||
StartMarker,
|
StartMarker,
|
||||||
EndMarker,
|
EndMarker,
|
||||||
|
|||||||
Reference in New Issue
Block a user