added logic for doors

This commit is contained in:
grimsace
2026-03-06 10:53:46 -06:00
parent dd1be7e6e6
commit 941bdb1ca8
4 changed files with 247 additions and 5 deletions
+104 -4
View File
@@ -25,10 +25,27 @@ pub struct Corridor {
pub path: Vec<(usize, usize)>,
}
#[derive(Debug, Clone)]
pub struct Door {
pub from: (usize, usize),
pub to: (usize, usize),
pub locked: bool,
pub archway: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct DoorSettings {
pub frequency_percent: usize,
pub room_hallway_percent: usize,
pub locked_percent: usize,
pub allow_middle_corridor_doors: bool,
}
#[derive(Debug, Clone, Default)]
pub struct DungeonLayout {
pub rooms: Vec<Room>,
pub corridors: Vec<Corridor>,
pub doors: Vec<Door>,
}
pub fn generate_layout(
@@ -41,6 +58,7 @@ pub fn generate_layout(
square_rooms_only: bool,
corridor_randomness_percent: usize,
dead_end_room_percent: usize,
door_settings: DoorSettings,
) -> DungeonLayout {
let layout_salt = ((cols as u64) << 48)
^ ((rows as u64) << 32)
@@ -58,7 +76,11 @@ pub fn generate_layout(
let mut corridors = Vec::new();
if cols < 2 || rows < 2 || target_room_count == 0 {
return DungeonLayout { rooms, corridors };
return DungeonLayout {
rooms,
corridors,
doors: Vec::new(),
};
}
let mut min_size = min_room_size.max(2);
@@ -74,7 +96,11 @@ pub fn generate_layout(
}
if min_size == 0 || max_size < min_size {
return DungeonLayout { rooms, corridors };
return DungeonLayout {
rooms,
corridors,
doors: Vec::new(),
};
}
let max_attempts = target_room_count.saturating_mul(40).max(50);
@@ -121,7 +147,11 @@ pub fn generate_layout(
}
if rooms.len() < 2 {
return DungeonLayout { rooms, corridors };
return DungeonLayout {
rooms,
corridors,
doors: Vec::new(),
};
}
let randomness = (corridor_randomness_percent.min(100) as f32) / 100.0;
@@ -162,7 +192,64 @@ pub fn generate_layout(
}
}
DungeonLayout { rooms, corridors }
let mut layout = DungeonLayout {
rooms,
corridors,
doors: Vec::new(),
};
apply_doors(&mut layout, seed, door_settings);
layout
}
pub fn apply_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) {
layout.doors.clear();
let mut rng = SimpleRng::new(seed::derive_seed(seed, 0xD005_5EED_u64));
let base = (settings.frequency_percent.min(100) as f32) / 100.0;
let room_hall = (settings.room_hallway_percent.min(100) as f32) / 100.0;
let locked = (settings.locked_percent.min(100) as f32) / 100.0;
let mut seen_edges = HashSet::new();
for corridor in &layout.corridors {
for pair in corridor.path.windows(2) {
if pair[0] == pair[1] {
continue;
}
let edge = normalized_cell_edge(pair[0], pair[1]);
if !seen_edges.insert(edge) {
continue;
}
let a_in_room = room_index_at_cell(&layout.rooms, edge.0).is_some();
let b_in_room = room_index_at_cell(&layout.rooms, edge.1).is_some();
let is_room_hallway = a_in_room ^ b_in_room;
let is_middle = !a_in_room && !b_in_room;
if is_room_hallway {
let door_chance = base * room_hall;
let place_door = door_chance > 0.0 && rng.next_f32() <= door_chance;
layout.doors.push(Door {
from: edge.0,
to: edge.1,
locked: place_door && rng.next_f32() <= locked,
archway: !place_door,
});
continue;
}
if is_middle && settings.allow_middle_corridor_doors {
if base > 0.0 && rng.next_f32() <= base {
layout.doors.push(Door {
from: edge.0,
to: edge.1,
locked: rng.next_f32() <= locked,
archway: false,
});
}
}
}
}
}
fn build_room_connection_edges(
@@ -470,6 +557,19 @@ fn manhattan_distance(a: (usize, usize), b: (usize, usize)) -> usize {
a.0.abs_diff(b.0) + a.1.abs_diff(b.1)
}
fn room_index_at_cell(rooms: &[Room], cell: (usize, usize)) -> Option<usize> {
rooms.iter().position(|room| {
cell.0 >= room.x
&& cell.0 < room.x + room.width
&& cell.1 >= room.y
&& cell.1 < room.y + room.height
})
}
fn normalized_cell_edge(a: (usize, usize), b: (usize, usize)) -> ((usize, usize), (usize, usize)) {
if a <= b { (a, b) } else { (b, a) }
}
struct SimpleRng {
state: u64,
}
+65 -1
View File
@@ -7,7 +7,7 @@ use std::collections::HashSet;
use eframe::egui;
use egui::{Color32, Stroke};
use layout::{DungeonLayout, shortest_path_cells};
use layout::{DoorSettings, DungeonLayout, shortest_path_cells};
use ui::{UiSettings, draw_side_panel};
fn main() -> eframe::Result<()> {
@@ -39,6 +39,7 @@ impl Default for DungeonApp {
settings.square_rooms_only,
settings.corridor_randomness,
settings.dead_end_rooms_percent,
door_settings_from_ui(&settings),
);
Self {
settings,
@@ -92,6 +93,7 @@ impl DungeonApp {
self.settings.square_rooms_only,
self.settings.corridor_randomness,
self.settings.dead_end_rooms_percent,
door_settings_from_ui(&self.settings),
);
}
@@ -185,6 +187,7 @@ impl DungeonApp {
self.layout.rooms[drag.room_idx].x = new_x;
self.layout.rooms[drag.room_idx].y = new_y;
self.reroute_corridors_for_room(drag.room_idx);
self.refresh_doors();
}
fn reroute_corridors_for_room(&mut self, room_idx: usize) {
@@ -253,6 +256,15 @@ impl DungeonApp {
};
self.layout.corridors[drag.corridor_index].path = new_path;
self.refresh_doors();
}
fn refresh_doors(&mut self) {
layout::apply_doors(
&mut self.layout,
self.settings.seed,
door_settings_from_ui(&self.settings),
);
}
}
@@ -405,6 +417,23 @@ fn draw_layout(painter: &egui::Painter, geometry: &GridGeometry, layout: &Dungeo
painter.rect_filled(room_rect, 0.0, room_fill);
painter.rect_stroke(room_rect, 0.0, outline_stroke, egui::StrokeKind::Middle);
}
for door in &layout.doors {
let color = if door.archway {
Color32::from_rgb(70, 130, 220)
} else if door.locked {
Color32::from_rgb(80, 200, 120)
} else {
Color32::from_rgb(220, 70, 70)
};
draw_door_line(
painter,
geometry,
door.from,
door.to,
Stroke::new(3.0, color),
);
}
}
fn cell_rect(geometry: &GridGeometry, col: usize, row: usize) -> egui::Rect {
@@ -495,3 +524,38 @@ fn simplify_path_loops(path: &mut Vec<(usize, usize)>) {
}
*path = out;
}
fn door_settings_from_ui(settings: &UiSettings) -> DoorSettings {
DoorSettings {
frequency_percent: settings.door_frequency_percent,
room_hallway_percent: settings.room_hallway_door_percent,
locked_percent: settings.locked_door_percent,
allow_middle_corridor_doors: settings.allow_middle_corridor_doors,
}
}
fn draw_door_line(
painter: &egui::Painter,
geometry: &GridGeometry,
a: (usize, usize),
b: (usize, usize),
stroke: Stroke,
) {
if a.0.abs_diff(b.0) + a.1.abs_diff(b.1) != 1 {
return;
}
if a.0 != b.0 {
let x = geometry.rect.left() + (a.0.max(b.0) as f32) * geometry.cell_size;
let row = a.1;
let y0 = geometry.rect.top() + row as f32 * geometry.cell_size;
let y1 = y0 + geometry.cell_size;
painter.line_segment([egui::pos2(x, y0), egui::pos2(x, y1)], stroke);
} else {
let y = geometry.rect.top() + (a.1.max(b.1) as f32) * geometry.cell_size;
let col = a.0;
let x0 = geometry.rect.left() + col as f32 * geometry.cell_size;
let x1 = x0 + geometry.cell_size;
painter.line_segment([egui::pos2(x0, y), egui::pos2(x1, y)], stroke);
}
}
+69
View File
@@ -27,6 +27,10 @@ pub struct UiSettings {
pub square_rooms_only: bool,
pub corridor_randomness: usize,
pub dead_end_rooms_percent: usize,
pub door_frequency_percent: usize,
pub room_hallway_door_percent: usize,
pub locked_door_percent: usize,
pub allow_middle_corridor_doors: bool,
active_tab: Tab,
}
@@ -42,6 +46,10 @@ impl Default for UiSettings {
square_rooms_only: false,
corridor_randomness: 0,
dead_end_rooms_percent: 0,
door_frequency_percent: 30,
room_hallway_door_percent: 70,
locked_door_percent: 25,
allow_middle_corridor_doors: false,
active_tab: Tab::Generate,
}
}
@@ -227,4 +235,65 @@ fn draw_layout_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut Si
)
.changed();
});
ui.add_space(12.0);
ui.separator();
ui.add_space(8.0);
ui.label(RichText::new("Door Settings").strong());
ui.add_space(8.0);
ui.label("Door Frequency (%)");
ui.horizontal(|ui| {
result.settings_changed |= ui
.add(egui::Slider::new(&mut settings.door_frequency_percent, 0..=100).show_value(false))
.changed();
result.settings_changed |= ui
.add(
egui::DragValue::new(&mut settings.door_frequency_percent)
.speed(1.0)
.range(0..=100),
)
.changed();
});
ui.add_space(8.0);
ui.label("Room/Hallway Door Chance (%)");
ui.horizontal(|ui| {
result.settings_changed |= ui
.add(
egui::Slider::new(&mut settings.room_hallway_door_percent, 0..=100)
.show_value(false),
)
.changed();
result.settings_changed |= ui
.add(
egui::DragValue::new(&mut settings.room_hallway_door_percent)
.speed(1.0)
.range(0..=100),
)
.changed();
});
ui.add_space(8.0);
ui.label("Locked Door Chance (%)");
ui.horizontal(|ui| {
result.settings_changed |= ui
.add(egui::Slider::new(&mut settings.locked_door_percent, 0..=100).show_value(false))
.changed();
result.settings_changed |= ui
.add(
egui::DragValue::new(&mut settings.locked_door_percent)
.speed(1.0)
.range(0..=100),
)
.changed();
});
ui.add_space(8.0);
result.settings_changed |= ui
.checkbox(
&mut settings.allow_middle_corridor_doors,
"Allow Middle Corridor Doors",
)
.changed();
}