added stair resizing
This commit is contained in:
+8
-8
@@ -889,8 +889,8 @@ pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
|
|||||||
for stair in &layout.stairs {
|
for stair in &layout.stairs {
|
||||||
let x = g.left() + stair.cell.0 as f32 * g.cell;
|
let x = g.left() + stair.cell.0 as f32 * g.cell;
|
||||||
let y = g.top() + stair.cell.1 as f32 * g.cell;
|
let y = g.top() + stair.cell.1 as f32 * g.cell;
|
||||||
let w = stair.size as f32 * g.cell;
|
let w = stair.width as f32 * g.cell;
|
||||||
let h = stair.size as f32 * g.cell;
|
let h = stair.height as f32 * g.cell;
|
||||||
|
|
||||||
let fill = if settings.colorblind_mode {
|
let fill = if settings.colorblind_mode {
|
||||||
"rgb(180,180,180)"
|
"rgb(180,180,180)"
|
||||||
@@ -901,7 +901,7 @@ pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
|
|||||||
s,
|
s,
|
||||||
"<rect x='{x}' y='{y}' width='{w}' height='{h}' fill='{fill}' fill-opacity='0.35' stroke='rgb(150,100,30)' stroke-width='2'/>"
|
"<rect x='{x}' y='{y}' width='{w}' height='{h}' fill='{fill}' fill-opacity='0.35' stroke='rgb(150,100,30)' stroke-width='2'/>"
|
||||||
);
|
);
|
||||||
let steps = (stair.size as f32 * 2.0).round() as usize;
|
let steps = (stair.height as f32 * 2.0).round() as usize;
|
||||||
let step_h = h / steps as f32;
|
let step_h = h / steps as f32;
|
||||||
for i in 0..steps {
|
for i in 0..steps {
|
||||||
let sy = y + (i as f32 * step_h) + step_h * 0.5;
|
let sy = y + (i as f32 * step_h) + step_h * 0.5;
|
||||||
@@ -1155,8 +1155,8 @@ fn fill_rect_staircase(
|
|||||||
let Some(rect) = Rect::from_xywh(
|
let Some(rect) = Rect::from_xywh(
|
||||||
g.left() + staircase.cell.0 as f32 * g.cell,
|
g.left() + staircase.cell.0 as f32 * g.cell,
|
||||||
g.top() + staircase.cell.1 as f32 * g.cell,
|
g.top() + staircase.cell.1 as f32 * g.cell,
|
||||||
staircase.size as f32 * g.cell,
|
staircase.width as f32 * g.cell,
|
||||||
staircase.size as f32 * g.cell,
|
staircase.height as f32 * g.cell,
|
||||||
) else {
|
) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -1181,8 +1181,8 @@ fn draw_staircase(
|
|||||||
let Some(rect) = Rect::from_xywh(
|
let Some(rect) = Rect::from_xywh(
|
||||||
g.left() + staircase.cell.0 as f32 * g.cell,
|
g.left() + staircase.cell.0 as f32 * g.cell,
|
||||||
g.top() + staircase.cell.1 as f32 * g.cell,
|
g.top() + staircase.cell.1 as f32 * g.cell,
|
||||||
staircase.size as f32 * g.cell,
|
staircase.width as f32 * g.cell,
|
||||||
staircase.size as f32 * g.cell,
|
staircase.height as f32 * g.cell,
|
||||||
) else {
|
) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -1206,7 +1206,7 @@ fn draw_staircase(
|
|||||||
draw_wall_segment(pixmap, x, y, x, y + h, 2.0, stroke_color);
|
draw_wall_segment(pixmap, x, y, x, y + h, 2.0, stroke_color);
|
||||||
draw_wall_segment(pixmap, x + w, y, x + w, y + h, 2.0, stroke_color);
|
draw_wall_segment(pixmap, x + w, y, x + w, y + h, 2.0, stroke_color);
|
||||||
|
|
||||||
let steps = (staircase.size as f32 * 2.0).round() as usize;
|
let steps = (staircase.height as f32 * 2.0).round() as usize;
|
||||||
let step_h = h / steps as f32;
|
let step_h = h / steps as f32;
|
||||||
for i in 0..steps {
|
for i in 0..steps {
|
||||||
let sy = (y + (i as f32 * step_h) + step_h * 0.5).round();
|
let sy = (y + (i as f32 * step_h) + step_h * 0.5).round();
|
||||||
|
|||||||
+21
-7
@@ -64,7 +64,8 @@ pub struct AreaMarker {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Staircase {
|
pub struct Staircase {
|
||||||
pub cell: (usize, usize),
|
pub cell: (usize, usize),
|
||||||
pub size: usize,
|
pub width: usize,
|
||||||
|
pub height: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@@ -1384,13 +1385,26 @@ fn shuffle_rooms(rooms: &mut [Room], rng: &mut SimpleRng) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rooms_overlap(a: &Room, b: &Room) -> bool {
|
fn rects_overlap(
|
||||||
let a_right = a.x + a.width;
|
ax: usize,
|
||||||
let a_bottom = a.y + a.height;
|
ay: usize,
|
||||||
let b_right = b.x + b.width;
|
aw: usize,
|
||||||
let b_bottom = b.y + b.height;
|
ah: usize,
|
||||||
|
bx: usize,
|
||||||
|
by: usize,
|
||||||
|
bw: usize,
|
||||||
|
bh: usize,
|
||||||
|
) -> bool {
|
||||||
|
let a_right = ax + aw;
|
||||||
|
let a_bottom = ay + ah;
|
||||||
|
let b_right = bx + bw;
|
||||||
|
let b_bottom = by + bh;
|
||||||
|
|
||||||
a.x < b_right && a_right > b.x && a.y < b_bottom && a_bottom > b.y
|
ax < b_right && a_right > bx && ay < b_bottom && a_bottom > by
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rooms_overlap(a: &Room, b: &Room) -> bool {
|
||||||
|
rects_overlap(a.x, a.y, a.width, a.height, b.x, b.y, b.width, b.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rooms_touch(a: &Room, b: &Room) -> bool {
|
fn rooms_touch(a: &Room, b: &Room) -> bool {
|
||||||
|
|||||||
+261
-53
@@ -1194,7 +1194,8 @@ impl DungeonApp {
|
|||||||
|
|
||||||
let stair = layout::Staircase {
|
let stair = layout::Staircase {
|
||||||
cell: (origin_x, origin_y),
|
cell: (origin_x, origin_y),
|
||||||
size,
|
width: size,
|
||||||
|
height: size,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.settings.sync_stairs_across_levels {
|
if self.settings.sync_stairs_across_levels {
|
||||||
@@ -1414,9 +1415,9 @@ impl DungeonApp {
|
|||||||
|
|
||||||
layout.stairs.iter().position(|stair| {
|
layout.stairs.iter().position(|stair| {
|
||||||
clicked.0 >= stair.cell.0
|
clicked.0 >= stair.cell.0
|
||||||
&& clicked.0 < stair.cell.0 + stair.size
|
&& clicked.0 < stair.cell.0 + stair.width
|
||||||
&& clicked.1 >= stair.cell.1
|
&& clicked.1 >= stair.cell.1
|
||||||
&& clicked.1 < stair.cell.1 + stair.size
|
&& clicked.1 < stair.cell.1 + stair.height
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1488,16 +1489,16 @@ impl DungeonApp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let stair_size = {
|
let (stair_width, stair_height) = {
|
||||||
let layout = &self.levels[self.settings.active_level_index];
|
let layout = &self.levels[self.settings.active_level_index];
|
||||||
let Some(stair) = layout.stairs.get(drag.stair_idx) else {
|
let Some(stair) = layout.stairs.get(drag.stair_idx) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
stair.size
|
(stair.width, stair.height)
|
||||||
};
|
};
|
||||||
|
|
||||||
let max_x = self.settings.cols.saturating_sub(stair_size);
|
let max_x = self.settings.cols.saturating_sub(stair_width);
|
||||||
let max_y = self.settings.rows.saturating_sub(stair_size);
|
let max_y = self.settings.rows.saturating_sub(stair_height);
|
||||||
let target_x = (grid_x - drag.offset_x).floor().max(0.0) as usize;
|
let target_x = (grid_x - drag.offset_x).floor().max(0.0) as usize;
|
||||||
let target_y = (grid_y - drag.offset_y).floor().max(0.0) as usize;
|
let target_y = (grid_y - drag.offset_y).floor().max(0.0) as usize;
|
||||||
let new_cell = (target_x.min(max_x), target_y.min(max_y));
|
let new_cell = (target_x.min(max_x), target_y.min(max_y));
|
||||||
@@ -1565,6 +1566,14 @@ impl DungeonApp {
|
|||||||
self.resize_state = Some(ResizeState::Room(state));
|
self.resize_state = Some(ResizeState::Room(state));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else if let Some(stair_idx) = self.stair_at_pointer(pointer_pos, geometry) {
|
||||||
|
if let Some(state) =
|
||||||
|
self.start_staircase_resize(stair_idx, pointer_pos, geometry)
|
||||||
|
{
|
||||||
|
self.push_undo_snapshot();
|
||||||
|
self.resize_state = Some(ResizeState::Staircase(state));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1579,6 +1588,7 @@ impl DungeonApp {
|
|||||||
match state {
|
match state {
|
||||||
ResizeState::Room(state) => self.resize_room(&state, cell),
|
ResizeState::Room(state) => self.resize_room(&state, cell),
|
||||||
ResizeState::Text(state) => self.resize_text(&state, cell),
|
ResizeState::Text(state) => self.resize_text(&state, cell),
|
||||||
|
ResizeState::Staircase(state) => self.resize_staircase(&state, cell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1671,6 +1681,38 @@ impl DungeonApp {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn start_staircase_resize(
|
||||||
|
&self,
|
||||||
|
stair_idx: usize,
|
||||||
|
pointer_pos: egui::Pos2,
|
||||||
|
geometry: &GridGeometry,
|
||||||
|
) -> Option<StaircaseResizeState> {
|
||||||
|
if self.settings.active_level_index >= self.levels.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let layout = &self.levels[self.settings.active_level_index];
|
||||||
|
|
||||||
|
let original_stair = layout.stairs.get(stair_idx)?.clone();
|
||||||
|
let (grid_x, grid_y) = pointer_to_grid(pointer_pos, geometry)?;
|
||||||
|
let center_x = original_stair.cell.0 as f32 + original_stair.width as f32 * 0.5;
|
||||||
|
let center_y = original_stair.cell.1 as f32 + original_stair.height as f32 * 0.5;
|
||||||
|
let pointer_x = grid_x;
|
||||||
|
let pointer_y = grid_y;
|
||||||
|
|
||||||
|
let resize_corner = match (pointer_x >= center_x, pointer_y >= center_y) {
|
||||||
|
(false, false) => ResizeCorner::TopLeft,
|
||||||
|
(true, false) => ResizeCorner::TopRight,
|
||||||
|
(false, true) => ResizeCorner::BottomLeft,
|
||||||
|
(true, true) => ResizeCorner::BottomRight,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(StaircaseResizeState {
|
||||||
|
stair_idx,
|
||||||
|
original_stair,
|
||||||
|
resize_corner,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Resize only the corner chosen at drag start while keeping the opposite corner fixed.
|
// Resize only the corner chosen at drag start while keeping the opposite corner fixed.
|
||||||
fn resize_room(&mut self, state: &RoomResizeState, target_cell: (usize, usize)) {
|
fn resize_room(&mut self, state: &RoomResizeState, target_cell: (usize, usize)) {
|
||||||
if self.settings.active_level_index >= self.levels.len() {
|
if self.settings.active_level_index >= self.levels.len() {
|
||||||
@@ -1748,6 +1790,112 @@ impl DungeonApp {
|
|||||||
label.font_size = new_size as u16;
|
label.font_size = new_size as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resize_staircase(&mut self, state: &StaircaseResizeState, target_cell: (usize, usize)) {
|
||||||
|
if self.settings.active_level_index >= self.levels.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cols = self.settings.cols.max(1);
|
||||||
|
let rows = self.settings.rows.max(1);
|
||||||
|
|
||||||
|
let tx = target_cell.0.min(cols - 1) as isize;
|
||||||
|
let ty = target_cell.1.min(rows - 1) as isize;
|
||||||
|
let original = &state.original_stair;
|
||||||
|
|
||||||
|
let left = original.cell.0 as isize;
|
||||||
|
let top = original.cell.1 as isize;
|
||||||
|
let right = (original.cell.0 + original.width - 1) as isize;
|
||||||
|
let bottom = (original.cell.1 + original.height - 1) as isize;
|
||||||
|
|
||||||
|
let (x0, x1, y0, y1) = match state.resize_corner {
|
||||||
|
ResizeCorner::TopLeft => (tx.min(right), right, ty.min(bottom), bottom),
|
||||||
|
ResizeCorner::TopRight => (left, tx.max(left), ty.min(bottom), bottom),
|
||||||
|
ResizeCorner::BottomLeft => (tx.min(right), right, top, ty.max(top)),
|
||||||
|
ResizeCorner::BottomRight => (left, tx.max(left), top, ty.max(top)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_stair_cell = (x0 as usize, y0 as usize);
|
||||||
|
let new_stair_width = (x1 - x0 + 1) as usize;
|
||||||
|
let new_stair_height = (y1 - y0 + 1) as usize;
|
||||||
|
|
||||||
|
if new_stair_width == 0 || new_stair_height == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collisions with other stairs or rooms on the current level.
|
||||||
|
let active_layout = &self.levels[self.settings.active_level_index];
|
||||||
|
if active_layout.rooms.iter().any(|room| {
|
||||||
|
rects_overlap(
|
||||||
|
new_stair_cell.0,
|
||||||
|
new_stair_cell.1,
|
||||||
|
new_stair_width,
|
||||||
|
new_stair_height,
|
||||||
|
room.x,
|
||||||
|
room.y,
|
||||||
|
room.width,
|
||||||
|
room.height,
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if active_layout.stairs.iter().enumerate().any(|(idx, stair)| {
|
||||||
|
idx != state.stair_idx
|
||||||
|
&& rects_overlap(
|
||||||
|
new_stair_cell.0,
|
||||||
|
new_stair_cell.1,
|
||||||
|
new_stair_width,
|
||||||
|
new_stair_height,
|
||||||
|
stair.cell.0,
|
||||||
|
stair.cell.1,
|
||||||
|
stair.width,
|
||||||
|
stair.height,
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.settings.sync_stairs_across_levels {
|
||||||
|
for layout in &mut self.levels {
|
||||||
|
for stair in &mut layout.stairs {
|
||||||
|
if stair.cell == state.original_stair.cell {
|
||||||
|
stair.cell = new_stair_cell;
|
||||||
|
stair.width = new_stair_width;
|
||||||
|
stair.height = new_stair_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let current_idx = self.settings.active_level_index;
|
||||||
|
let level_indices = vec![
|
||||||
|
Some(current_idx),
|
||||||
|
current_idx.checked_sub(1),
|
||||||
|
if current_idx + 1 < self.levels.len() {
|
||||||
|
Some(current_idx + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for idx in level_indices.into_iter().flatten() {
|
||||||
|
let layout = &mut self.levels[idx];
|
||||||
|
for stair in &mut layout.stairs {
|
||||||
|
if stair.cell == state.original_stair.cell {
|
||||||
|
stair.cell = new_stair_cell;
|
||||||
|
stair.width = new_stair_width;
|
||||||
|
stair.height = new_stair_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the resize state with the new dimensions to allow continuous resizing.
|
||||||
|
if let Some(ResizeState::Staircase(ref mut s)) = self.resize_state {
|
||||||
|
s.original_stair.cell = new_stair_cell;
|
||||||
|
s.original_stair.width = new_stair_width;
|
||||||
|
s.original_stair.height = new_stair_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw dashed overlays for room resizing interactions.
|
// Draw dashed overlays for room resizing interactions.
|
||||||
fn draw_resize_overlay(&self, painter: &egui::Painter, geometry: &GridGeometry) {
|
fn draw_resize_overlay(&self, painter: &egui::Painter, geometry: &GridGeometry) {
|
||||||
if self.settings.active_level_index >= self.levels.len() {
|
if self.settings.active_level_index >= self.levels.len() {
|
||||||
@@ -1760,7 +1908,7 @@ impl DungeonApp {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| match s {
|
.and_then(|s| match s {
|
||||||
ResizeState::Text(state) => Some(state.text_idx),
|
ResizeState::Text(state) => Some(state.text_idx),
|
||||||
ResizeState::Room(_) => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.or(self.hover_text_idx)
|
.or(self.hover_text_idx)
|
||||||
{
|
{
|
||||||
@@ -1781,50 +1929,88 @@ impl DungeonApp {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| match s {
|
.and_then(|s| match s {
|
||||||
ResizeState::Room(state) => Some(state.room_idx),
|
ResizeState::Room(state) => Some(state.room_idx),
|
||||||
ResizeState::Text(_) => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.or(self.hover_room_idx);
|
.or(self.hover_room_idx);
|
||||||
let Some(room_idx) = room_idx else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(room) = layout.rooms.get(room_idx) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let color = Color32::from_gray(170);
|
|
||||||
let stroke = Stroke::new(1.5, color);
|
|
||||||
|
|
||||||
let left = geometry.rect.left() + room.x as f32 * geometry.cell_size;
|
if let Some(room_idx) = room_idx {
|
||||||
let top = geometry.rect.top() + room.y as f32 * geometry.cell_size;
|
if let Some(room) = layout.rooms.get(room_idx) {
|
||||||
let right = left + room.width as f32 * geometry.cell_size;
|
let color = Color32::from_gray(170);
|
||||||
let bottom = top + room.height as f32 * geometry.cell_size;
|
let stroke = Stroke::new(1.5, color);
|
||||||
|
|
||||||
let tl = egui::pos2(left, top);
|
let left = geometry.rect.left() + room.x as f32 * geometry.cell_size;
|
||||||
let tr = egui::pos2(right, top);
|
let top = geometry.rect.top() + room.y as f32 * geometry.cell_size;
|
||||||
let br = egui::pos2(right, bottom);
|
let right = left + room.width as f32 * geometry.cell_size;
|
||||||
let bl = egui::pos2(left, bottom);
|
let bottom = top + room.height as f32 * geometry.cell_size;
|
||||||
|
|
||||||
draw_dashed_line(painter, tl, tr, stroke, 6.0, 6.0);
|
let tl = egui::pos2(left, top);
|
||||||
draw_dashed_line(painter, tr, br, stroke, 6.0, 6.0);
|
let tr = egui::pos2(right, top);
|
||||||
draw_dashed_line(painter, br, bl, stroke, 6.0, 6.0);
|
let br = egui::pos2(right, bottom);
|
||||||
draw_dashed_line(painter, bl, tl, stroke, 6.0, 6.0);
|
let bl = egui::pos2(left, bottom);
|
||||||
|
|
||||||
let font = egui::FontId::proportional(12.0);
|
draw_dashed_line(painter, tl, tr, stroke, 6.0, 6.0);
|
||||||
let center_top = egui::pos2((left + right) * 0.5, top - 10.0);
|
draw_dashed_line(painter, tr, br, stroke, 6.0, 6.0);
|
||||||
let center_left = egui::pos2(left - 12.0, (top + bottom) * 0.5);
|
draw_dashed_line(painter, br, bl, stroke, 6.0, 6.0);
|
||||||
painter.text(
|
draw_dashed_line(painter, bl, tl, stroke, 6.0, 6.0);
|
||||||
center_top,
|
|
||||||
egui::Align2::CENTER_CENTER,
|
|
||||||
"<->",
|
|
||||||
font.clone(),
|
|
||||||
color,
|
|
||||||
);
|
|
||||||
painter.text(center_left, egui::Align2::CENTER_CENTER, "^v", font, color);
|
|
||||||
|
|
||||||
if matches!(self.resize_state, Some(ResizeState::Room(_))) {
|
let font = egui::FontId::proportional(12.0);
|
||||||
draw_resize_handle(painter, tl, ResizeCorner::TopLeft, color);
|
let center_top = egui::pos2((left + right) * 0.5, top - 10.0);
|
||||||
draw_resize_handle(painter, tr, ResizeCorner::TopRight, color);
|
let center_left = egui::pos2(left - 12.0, (top + bottom) * 0.5);
|
||||||
draw_resize_handle(painter, br, ResizeCorner::BottomRight, color);
|
painter.text(
|
||||||
draw_resize_handle(painter, bl, ResizeCorner::BottomLeft, color);
|
center_top,
|
||||||
|
egui::Align2::CENTER_CENTER,
|
||||||
|
"<->",
|
||||||
|
font.clone(),
|
||||||
|
color,
|
||||||
|
);
|
||||||
|
painter.text(center_left, egui::Align2::CENTER_CENTER, "^v", font, color);
|
||||||
|
|
||||||
|
if matches!(self.resize_state, Some(ResizeState::Room(_))) {
|
||||||
|
draw_resize_handle(painter, tl, ResizeCorner::TopLeft, color);
|
||||||
|
draw_resize_handle(painter, tr, ResizeCorner::TopRight, color);
|
||||||
|
draw_resize_handle(painter, br, ResizeCorner::BottomRight, color);
|
||||||
|
draw_resize_handle(painter, bl, ResizeCorner::BottomLeft, color);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stair_idx = self
|
||||||
|
.resize_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|s| match s {
|
||||||
|
ResizeState::Staircase(state) => Some(state.stair_idx),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.or(self.hover_stair_idx);
|
||||||
|
|
||||||
|
if let Some(stair_idx) = stair_idx {
|
||||||
|
if let Some(stair) = layout.stairs.get(stair_idx) {
|
||||||
|
let color = Color32::from_gray(170);
|
||||||
|
let stroke = Stroke::new(1.5, color);
|
||||||
|
|
||||||
|
let left = geometry.rect.left() + stair.cell.0 as f32 * geometry.cell_size;
|
||||||
|
let top = geometry.rect.top() + stair.cell.1 as f32 * geometry.cell_size;
|
||||||
|
let right = left + stair.width as f32 * geometry.cell_size;
|
||||||
|
let bottom = top + stair.height as f32 * geometry.cell_size;
|
||||||
|
|
||||||
|
let tl = egui::pos2(left, top);
|
||||||
|
let tr = egui::pos2(right, top);
|
||||||
|
let br = egui::pos2(right, bottom);
|
||||||
|
let bl = egui::pos2(left, bottom);
|
||||||
|
|
||||||
|
draw_dashed_line(painter, tl, tr, stroke, 6.0, 6.0);
|
||||||
|
draw_dashed_line(painter, tr, br, stroke, 6.0, 6.0);
|
||||||
|
draw_dashed_line(painter, br, bl, stroke, 6.0, 6.0);
|
||||||
|
draw_dashed_line(painter, bl, tl, stroke, 6.0, 6.0);
|
||||||
|
|
||||||
|
if matches!(self.resize_state, Some(ResizeState::Staircase(_))) {
|
||||||
|
draw_resize_handle(painter, tl, ResizeCorner::TopLeft, color);
|
||||||
|
draw_resize_handle(painter, tr, ResizeCorner::TopRight, color);
|
||||||
|
draw_resize_handle(painter, br, ResizeCorner::BottomRight, color);
|
||||||
|
draw_resize_handle(painter, bl, ResizeCorner::BottomLeft, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2248,10 +2434,18 @@ struct TextResizeState {
|
|||||||
original_font_size: u16,
|
original_font_size: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct StaircaseResizeState {
|
||||||
|
stair_idx: usize,
|
||||||
|
original_stair: layout::Staircase,
|
||||||
|
resize_corner: ResizeCorner,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum ResizeState {
|
enum ResizeState {
|
||||||
Room(RoomResizeState),
|
Room(RoomResizeState),
|
||||||
Text(TextResizeState),
|
Text(TextResizeState),
|
||||||
|
Staircase(StaircaseResizeState),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GridGeometry {
|
struct GridGeometry {
|
||||||
@@ -2635,8 +2829,8 @@ fn draw_staircase(
|
|||||||
) {
|
) {
|
||||||
let left = geometry.rect.left() + stair.cell.0 as f32 * geometry.cell_size;
|
let left = geometry.rect.left() + stair.cell.0 as f32 * geometry.cell_size;
|
||||||
let top = geometry.rect.top() + stair.cell.1 as f32 * geometry.cell_size;
|
let top = geometry.rect.top() + stair.cell.1 as f32 * geometry.cell_size;
|
||||||
let right = left + stair.size as f32 * geometry.cell_size;
|
let right = left + stair.width as f32 * geometry.cell_size;
|
||||||
let bottom = top + stair.size as f32 * geometry.cell_size;
|
let bottom = top + stair.height as f32 * geometry.cell_size;
|
||||||
let rect = egui::Rect::from_min_max(egui::pos2(left, top), egui::pos2(right, bottom));
|
let rect = egui::Rect::from_min_max(egui::pos2(left, top), egui::pos2(right, bottom));
|
||||||
|
|
||||||
// Fill with a distinctive color for stairs.
|
// Fill with a distinctive color for stairs.
|
||||||
@@ -2665,7 +2859,7 @@ fn draw_staircase(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Draw stair lines (horizontal steps).
|
// Draw stair lines (horizontal steps).
|
||||||
let steps = (stair.size as f32 * 2.0).round() as usize;
|
let steps = (stair.height as f32 * 2.0).round() as usize;
|
||||||
let step_height = (rect.height() / (steps as f32)).max(1.0);
|
let step_height = (rect.height() / (steps as f32)).max(1.0);
|
||||||
let line_color = if colorblind_mode {
|
let line_color = if colorblind_mode {
|
||||||
Color32::BLACK
|
Color32::BLACK
|
||||||
@@ -2886,13 +3080,27 @@ fn room_index_at_cell(rooms: &[layout::Room], cell: (usize, usize)) -> Option<us
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether two rectangles overlap by area.
|
||||||
|
fn rects_overlap(
|
||||||
|
ax: usize,
|
||||||
|
ay: usize,
|
||||||
|
aw: usize,
|
||||||
|
ah: usize,
|
||||||
|
bx: usize,
|
||||||
|
by: usize,
|
||||||
|
bw: usize,
|
||||||
|
bh: usize,
|
||||||
|
) -> bool {
|
||||||
|
let a_right = ax + aw;
|
||||||
|
let a_bottom = ay + ah;
|
||||||
|
let b_right = bx + bw;
|
||||||
|
let b_bottom = by + bh;
|
||||||
|
ax < b_right && a_right > bx && ay < b_bottom && a_bottom > by
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether two rooms overlap by area.
|
// Check whether two rooms overlap by area.
|
||||||
fn rooms_overlap(a: &layout::Room, b: &layout::Room) -> bool {
|
fn rooms_overlap(a: &layout::Room, b: &layout::Room) -> bool {
|
||||||
let a_right = a.x + a.width;
|
rects_overlap(a.x, a.y, a.width, a.height, b.x, b.y, b.width, b.height)
|
||||||
let a_bottom = a.y + a.height;
|
|
||||||
let b_right = b.x + b.width;
|
|
||||||
let b_bottom = b.y + b.height;
|
|
||||||
a.x < b_right && a_right > b.x && a.y < b_bottom && a_bottom > b.y
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn manual_door_edge_allowed(
|
fn manual_door_edge_allowed(
|
||||||
|
|||||||
+32
-20
@@ -448,14 +448,15 @@ fn get_stair_count(settings: &UiSettings, gap_idx: usize) -> usize {
|
|||||||
pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &layout::Staircase) -> bool {
|
pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &layout::Staircase) -> bool {
|
||||||
let stair_x = stair.cell.0;
|
let stair_x = stair.cell.0;
|
||||||
let stair_y = stair.cell.1;
|
let stair_y = stair.cell.1;
|
||||||
let stair_size = stair.size;
|
let stair_width = stair.width;
|
||||||
|
let stair_height = stair.height;
|
||||||
|
|
||||||
// Check if any room already contains the staircase.
|
// Check if any room already contains the staircase.
|
||||||
for room in &layout.rooms {
|
for room in &layout.rooms {
|
||||||
if stair_x >= room.x
|
if stair_x >= room.x
|
||||||
&& stair_y >= room.y
|
&& stair_y >= room.y
|
||||||
&& (stair_x + stair_size) <= (room.x + room.width)
|
&& (stair_x + stair_width) <= (room.x + room.width)
|
||||||
&& (stair_y + stair_size) <= (room.y + room.height)
|
&& (stair_y + stair_height) <= (room.y + room.height)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -480,8 +481,8 @@ pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &layout::Staircas
|
|||||||
let room = &mut layout.rooms[idx];
|
let room = &mut layout.rooms[idx];
|
||||||
let new_x = room.x.min(stair_x);
|
let new_x = room.x.min(stair_x);
|
||||||
let new_y = room.y.min(stair_y);
|
let new_y = room.y.min(stair_y);
|
||||||
let new_right = (room.x + room.width).max(stair_x + stair_size);
|
let new_right = (room.x + room.width).max(stair_x + stair_width);
|
||||||
let new_bottom = (room.y + room.height).max(stair_y + stair_size);
|
let new_bottom = (room.y + room.height).max(stair_y + stair_height);
|
||||||
|
|
||||||
room.x = new_x;
|
room.x = new_x;
|
||||||
room.y = new_y;
|
room.y = new_y;
|
||||||
@@ -493,8 +494,8 @@ pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &layout::Staircas
|
|||||||
layout.rooms.push(layout::Room {
|
layout.rooms.push(layout::Room {
|
||||||
x: stair_x,
|
x: stair_x,
|
||||||
y: stair_y,
|
y: stair_y,
|
||||||
width: stair_size,
|
width: stair_width,
|
||||||
height: stair_size,
|
height: stair_height,
|
||||||
});
|
});
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -502,16 +503,16 @@ pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &layout::Staircas
|
|||||||
|
|
||||||
// Compute the minimum Manhattan distance between a room and a staircase.
|
// Compute the minimum Manhattan distance between a room and a staircase.
|
||||||
fn room_to_stair_min_dist(room: &layout::Room, stair: &layout::Staircase) -> usize {
|
fn room_to_stair_min_dist(room: &layout::Room, stair: &layout::Staircase) -> usize {
|
||||||
let dx = if stair.cell.0 + stair.size <= room.x {
|
let dx = if stair.cell.0 + stair.width <= room.x {
|
||||||
room.x - (stair.cell.0 + stair.size)
|
room.x - (stair.cell.0 + stair.width)
|
||||||
} else if stair.cell.0 >= room.x + room.width {
|
} else if stair.cell.0 >= room.x + room.width {
|
||||||
stair.cell.0 - (room.x + room.width)
|
stair.cell.0 - (room.x + room.width)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let dy = if stair.cell.1 + stair.size <= room.y {
|
let dy = if stair.cell.1 + stair.height <= room.y {
|
||||||
room.y - (stair.cell.1 + stair.size)
|
room.y - (stair.cell.1 + stair.height)
|
||||||
} else if stair.cell.1 >= room.y + room.height {
|
} else if stair.cell.1 >= room.y + room.height {
|
||||||
stair.cell.1 - (room.y + room.height)
|
stair.cell.1 - (room.y + room.height)
|
||||||
} else {
|
} else {
|
||||||
@@ -595,15 +596,19 @@ fn pick_stair_positions(
|
|||||||
stair_cells
|
stair_cells
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, cell)| layout::Staircase {
|
.map(|(i, cell)| {
|
||||||
cell,
|
let (width, height) = random_stair_size(
|
||||||
size: random_stair_size(
|
|
||||||
settings,
|
settings,
|
||||||
seed::derive_seed(
|
seed::derive_seed(
|
||||||
settings.seed,
|
settings.seed,
|
||||||
STAIR_MARKER_STREAM_BASE + i as u64 + seed_offset * 1000,
|
STAIR_MARKER_STREAM_BASE + i as u64 + seed_offset * 1000,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
layout::Staircase {
|
||||||
|
cell,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -626,11 +631,18 @@ fn has_start_or_end_on_bottom_row(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pick a random stair size within settings range.
|
// Pick a random stair size within settings range.
|
||||||
fn random_stair_size(settings: &UiSettings, seed_value: u64) -> usize {
|
fn random_stair_size(settings: &UiSettings, seed_value: u64) -> (usize, usize) {
|
||||||
let min_size = settings.min_stair_width.min(settings.min_stair_height);
|
let w_span = settings
|
||||||
let max_size = settings.max_stair_width.max(settings.max_stair_height);
|
.max_stair_width
|
||||||
let span = max_size - min_size + 1;
|
.saturating_sub(settings.min_stair_width)
|
||||||
min_size + (seed_value as usize % span)
|
+ 1;
|
||||||
|
let h_span = settings
|
||||||
|
.max_stair_height
|
||||||
|
.saturating_sub(settings.min_stair_height)
|
||||||
|
+ 1;
|
||||||
|
let width = settings.min_stair_width + (seed_value as usize % w_span);
|
||||||
|
let height = settings.min_stair_height + (seed_value.rotate_left(17) as usize % h_span);
|
||||||
|
(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shuffle a vector deterministically using a seed.
|
// Shuffle a vector deterministically using a seed.
|
||||||
|
|||||||
Reference in New Issue
Block a user