added basic stairs
This commit is contained in:
@@ -826,6 +826,54 @@ pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
|
|||||||
settings.colorblind_mode,
|
settings.colorblind_mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Draw stairs and elevators.
|
||||||
|
for stair in &layout.stairs {
|
||||||
|
let x = g.left() + stair.cell.0 as f32 * g.cell;
|
||||||
|
let y = g.top() + stair.cell.1 as f32 * g.cell;
|
||||||
|
let w = stair.size as f32 * g.cell;
|
||||||
|
let h = stair.size as f32 * g.cell;
|
||||||
|
|
||||||
|
if stair.is_elevator {
|
||||||
|
let fill = if settings.colorblind_mode {
|
||||||
|
"rgb(180,180,180)"
|
||||||
|
} else {
|
||||||
|
"rgb(100,150,200)"
|
||||||
|
};
|
||||||
|
let _ = writeln!(
|
||||||
|
s,
|
||||||
|
"<rect x='{x}' y='{y}' width='{w}' height='{h}' fill='{fill}' fill-opacity='0.35' stroke='rgb(100,150,200)' stroke-width='2'/>"
|
||||||
|
);
|
||||||
|
let _ = writeln!(
|
||||||
|
s,
|
||||||
|
"<text x='{cx}' y='{cy}' text-anchor='middle' dominant-baseline='middle' fill='white' font-size='{fs}'>E</text>",
|
||||||
|
cx = x + w / 2.0,
|
||||||
|
cy = y + h / 2.0,
|
||||||
|
fs = (g.cell * stair.size as f32 * 0.4).clamp(14.0, 48.0)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let fill = if settings.colorblind_mode {
|
||||||
|
"rgb(180,180,180)"
|
||||||
|
} else {
|
||||||
|
"rgb(200,150,50)"
|
||||||
|
};
|
||||||
|
let _ = writeln!(
|
||||||
|
s,
|
||||||
|
"<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 step_h = h / steps as f32;
|
||||||
|
for i in 0..steps {
|
||||||
|
let sy = y + (i as f32 * step_h) + step_h * 0.5;
|
||||||
|
let _ = writeln!(
|
||||||
|
s,
|
||||||
|
"<line x1='{x1}' y1='{sy}' x2='{x2}' y2='{sy}' stroke='rgb(150,100,30)' stroke-width='1.5'/>",
|
||||||
|
x1 = x + 2.0,
|
||||||
|
x2 = x + w - 2.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.push_str("</svg>\n");
|
s.push_str("</svg>\n");
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-1
@@ -61,6 +61,13 @@ pub struct AreaMarker {
|
|||||||
pub size: usize,
|
pub size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Staircase {
|
||||||
|
pub cell: (usize, usize),
|
||||||
|
pub size: usize,
|
||||||
|
pub is_elevator: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum WindowSide {
|
pub enum WindowSide {
|
||||||
Left,
|
Left,
|
||||||
@@ -100,6 +107,7 @@ pub struct DungeonLayout {
|
|||||||
pub trap_markers: Vec<AreaMarker>,
|
pub trap_markers: Vec<AreaMarker>,
|
||||||
pub monster_markers: Vec<AreaMarker>,
|
pub monster_markers: Vec<AreaMarker>,
|
||||||
pub packed_rooms: bool,
|
pub packed_rooms: bool,
|
||||||
|
pub stairs: Vec<Staircase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DungeonLayout {
|
impl Default for DungeonLayout {
|
||||||
@@ -115,6 +123,7 @@ impl Default for DungeonLayout {
|
|||||||
trap_markers: Vec::new(),
|
trap_markers: Vec::new(),
|
||||||
monster_markers: Vec::new(),
|
monster_markers: Vec::new(),
|
||||||
packed_rooms: false,
|
packed_rooms: false,
|
||||||
|
stairs: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,6 +195,7 @@ pub fn generate_layout(
|
|||||||
trap_markers: Vec::new(),
|
trap_markers: Vec::new(),
|
||||||
monster_markers: Vec::new(),
|
monster_markers: Vec::new(),
|
||||||
packed_rooms: pack_rooms_without_corridors,
|
packed_rooms: pack_rooms_without_corridors,
|
||||||
|
stairs: Vec::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,6 +223,7 @@ pub fn generate_layout(
|
|||||||
trap_markers: Vec::new(),
|
trap_markers: Vec::new(),
|
||||||
monster_markers: Vec::new(),
|
monster_markers: Vec::new(),
|
||||||
packed_rooms: pack_rooms_without_corridors,
|
packed_rooms: pack_rooms_without_corridors,
|
||||||
|
stairs: Vec::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +246,7 @@ pub fn generate_layout(
|
|||||||
if pack_rooms_without_corridors {
|
if pack_rooms_without_corridors {
|
||||||
rooms = place_packed_rooms(&room_sizes, &room_edges, cols, rows, &mut room_rng);
|
rooms = place_packed_rooms(&room_sizes, &room_edges, cols, rows, &mut room_rng);
|
||||||
let mut layout = DungeonLayout {
|
let mut layout = DungeonLayout {
|
||||||
|
stairs: Vec::new(),
|
||||||
rooms,
|
rooms,
|
||||||
corridors,
|
corridors,
|
||||||
doors: Vec::new(),
|
doors: Vec::new(),
|
||||||
@@ -306,6 +318,7 @@ pub fn generate_layout(
|
|||||||
trap_markers: Vec::new(),
|
trap_markers: Vec::new(),
|
||||||
monster_markers: Vec::new(),
|
monster_markers: Vec::new(),
|
||||||
packed_rooms: false,
|
packed_rooms: false,
|
||||||
|
stairs: Vec::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,6 +367,7 @@ pub fn generate_layout(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut layout = DungeonLayout {
|
let mut layout = DungeonLayout {
|
||||||
|
stairs: Vec::new(),
|
||||||
rooms,
|
rooms,
|
||||||
corridors,
|
corridors,
|
||||||
doors: Vec::new(),
|
doors: Vec::new(),
|
||||||
@@ -983,7 +997,7 @@ fn shared_boundary_edges(a: &Room, b: &Room) -> Vec<((usize, usize), (usize, usi
|
|||||||
edges
|
edges
|
||||||
}
|
}
|
||||||
|
|
||||||
fn room_index_at_cell(rooms: &[Room], cell: (usize, usize)) -> Option<usize> {
|
pub fn room_index_at_cell(rooms: &[Room], cell: (usize, usize)) -> Option<usize> {
|
||||||
rooms.iter().position(|room| {
|
rooms.iter().position(|room| {
|
||||||
cell.0 >= room.x
|
cell.0 >= room.x
|
||||||
&& cell.0 < room.x + room.width
|
&& cell.0 < room.x + room.width
|
||||||
|
|||||||
+84
-1
@@ -18,7 +18,7 @@ use layout::{
|
|||||||
};
|
};
|
||||||
use startend::{
|
use startend::{
|
||||||
manual_marker_size, manual_monster_marker_size, manual_trap_marker_size,
|
manual_marker_size, manual_monster_marker_size, manual_trap_marker_size,
|
||||||
populate_random_markers,
|
populate_random_markers, populate_stairs,
|
||||||
};
|
};
|
||||||
use ui::{AddTool, UiSettings, draw_side_panel};
|
use ui::{AddTool, UiSettings, draw_side_panel};
|
||||||
|
|
||||||
@@ -379,6 +379,7 @@ fn generate_all_levels(settings: &UiSettings) -> Vec<DungeonLayout> {
|
|||||||
let layout = populate_random_markers(layout, settings);
|
let layout = populate_random_markers(layout, settings);
|
||||||
levels.push(layout);
|
levels.push(layout);
|
||||||
}
|
}
|
||||||
|
let levels = populate_stairs(levels, settings);
|
||||||
levels
|
levels
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2302,6 +2303,88 @@ fn draw_layout(
|
|||||||
hover_marker,
|
hover_marker,
|
||||||
MarkerKind::Monster,
|
MarkerKind::Monster,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Draw stairs and elevators.
|
||||||
|
for stair in &layout.stairs {
|
||||||
|
draw_staircase(painter, geometry, stair, colorblind_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw a staircase or elevator within a room.
|
||||||
|
fn draw_staircase(
|
||||||
|
painter: &egui::Painter,
|
||||||
|
geometry: &GridGeometry,
|
||||||
|
stair: &layout::Staircase,
|
||||||
|
colorblind_mode: bool,
|
||||||
|
) {
|
||||||
|
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.size as f32 * geometry.cell_size;
|
||||||
|
let bottom = top + stair.size as f32 * geometry.cell_size;
|
||||||
|
let rect = egui::Rect::from_min_max(egui::pos2(left, top), egui::pos2(right, bottom));
|
||||||
|
|
||||||
|
// Fill with a distinctive color for stairs/elevators.
|
||||||
|
let fill = if colorblind_mode {
|
||||||
|
Color32::from_rgb(180, 180, 180).gamma_multiply(0.4)
|
||||||
|
} else if stair.is_elevator {
|
||||||
|
Color32::from_rgb(100, 150, 200).gamma_multiply(0.35)
|
||||||
|
} else {
|
||||||
|
Color32::from_rgb(200, 150, 50).gamma_multiply(0.35)
|
||||||
|
};
|
||||||
|
painter.rect_filled(rect, 4.0, fill);
|
||||||
|
|
||||||
|
// Draw border.
|
||||||
|
let stroke_color = if colorblind_mode {
|
||||||
|
Color32::BLACK
|
||||||
|
} else if stair.is_elevator {
|
||||||
|
Color32::from_rgb(100, 150, 200)
|
||||||
|
} else {
|
||||||
|
Color32::from_rgb(200, 150, 50)
|
||||||
|
};
|
||||||
|
painter.rect_stroke(
|
||||||
|
rect,
|
||||||
|
4.0,
|
||||||
|
Stroke::new(2.0, stroke_color),
|
||||||
|
egui::StrokeKind::Middle,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw stair lines or elevator symbol.
|
||||||
|
if stair.is_elevator {
|
||||||
|
// Draw elevator symbol: a rectangle with arrows.
|
||||||
|
let center = rect.center();
|
||||||
|
let text = "E";
|
||||||
|
let font_size = (geometry.cell_size * stair.size as f32 * 0.4).clamp(14.0, 48.0);
|
||||||
|
painter.text(
|
||||||
|
center,
|
||||||
|
egui::Align2::CENTER_CENTER,
|
||||||
|
text,
|
||||||
|
egui::FontId::proportional(font_size),
|
||||||
|
if colorblind_mode {
|
||||||
|
Color32::BLACK
|
||||||
|
} else {
|
||||||
|
Color32::WHITE
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Draw stair lines (horizontal steps).
|
||||||
|
let steps = (stair.size as f32 * 2.0).round() as usize;
|
||||||
|
let step_height = (rect.height() / (steps as f32)).max(1.0);
|
||||||
|
let line_color = if colorblind_mode {
|
||||||
|
Color32::BLACK
|
||||||
|
} else {
|
||||||
|
Color32::from_rgb(150, 100, 30)
|
||||||
|
};
|
||||||
|
for i in 0..steps {
|
||||||
|
let y = rect.top() + (i as f32 * step_height) + step_height * 0.5;
|
||||||
|
painter.line_segment(
|
||||||
|
[
|
||||||
|
egui::pos2(rect.left() + 2.0, y),
|
||||||
|
egui::pos2(rect.right() - 2.0, y),
|
||||||
|
],
|
||||||
|
Stroke::new(1.5, line_color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the rectangle for a given grid cell.
|
// Compute the rectangle for a given grid cell.
|
||||||
|
|||||||
+201
@@ -1,6 +1,10 @@
|
|||||||
use crate::layout::{self, DungeonLayout};
|
use crate::layout::{self, DungeonLayout};
|
||||||
use crate::seed;
|
use crate::seed;
|
||||||
use crate::ui::UiSettings;
|
use crate::ui::UiSettings;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
// Import room_index_at_cell from layout module.
|
||||||
|
use crate::layout::room_index_at_cell;
|
||||||
|
|
||||||
const START_COUNT_STREAM: u64 = 10_001;
|
const START_COUNT_STREAM: u64 = 10_001;
|
||||||
const END_COUNT_STREAM: u64 = 10_002;
|
const END_COUNT_STREAM: u64 = 10_002;
|
||||||
@@ -345,3 +349,200 @@ fn pick_random_corridor_cell(
|
|||||||
let idx = (seed_value as usize) % corridor.path.len();
|
let idx = (seed_value as usize) % corridor.path.len();
|
||||||
Some(corridor.path[idx])
|
Some(corridor.path[idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constant for stair marker stream base
|
||||||
|
const STAIR_MARKER_STREAM_BASE: u64 = 21_000;
|
||||||
|
|
||||||
|
// Populate stairs (and optionally elevators) across all levels after markers are placed.
|
||||||
|
pub fn populate_stairs(
|
||||||
|
mut layouts: Vec<DungeonLayout>,
|
||||||
|
settings: &UiSettings,
|
||||||
|
) -> Vec<DungeonLayout> {
|
||||||
|
if settings.min_stairs_per_level == 0 && settings.max_stairs_per_level == 0 {
|
||||||
|
return layouts;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stair_count = if settings.min_stairs_per_level == settings.max_stairs_per_level {
|
||||||
|
settings.min_stairs_per_level
|
||||||
|
} else {
|
||||||
|
let range_seed = seed::derive_seed(settings.seed, 0x2A_3B_4_u64);
|
||||||
|
(range_seed as usize % (settings.max_stairs_per_level - settings.min_stairs_per_level + 1))
|
||||||
|
+ settings.min_stairs_per_level
|
||||||
|
};
|
||||||
|
|
||||||
|
if stair_count == 0 {
|
||||||
|
return layouts;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_elevator = settings.windows_enabled;
|
||||||
|
|
||||||
|
// If syncing stairs, pick the same stair positions for all levels.
|
||||||
|
let mut sync_stair_cells: Vec<layout::Staircase> = Vec::new();
|
||||||
|
if settings.sync_stairs_across_levels && !layouts.is_empty() {
|
||||||
|
let base_settings = settings.clone();
|
||||||
|
let base_layout = layouts[0].clone();
|
||||||
|
sync_stair_cells =
|
||||||
|
pick_stair_positions(&base_layout, &base_settings, stair_count, is_elevator);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (level_idx, layout) in layouts.iter_mut().enumerate() {
|
||||||
|
if settings.sync_stairs_across_levels && !sync_stair_cells.is_empty() {
|
||||||
|
// Use the synced stair positions.
|
||||||
|
layout.stairs = sync_stair_cells
|
||||||
|
.iter()
|
||||||
|
.map(|stair| layout::Staircase {
|
||||||
|
cell: stair.cell,
|
||||||
|
size: random_stair_size(
|
||||||
|
settings,
|
||||||
|
seed::derive_seed(
|
||||||
|
settings.seed,
|
||||||
|
STAIR_MARKER_STREAM_BASE + level_idx as u64,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
is_elevator,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
} else {
|
||||||
|
// Generate independent stair positions for this level.
|
||||||
|
layout.stairs = pick_stair_positions(layout, settings, stair_count, is_elevator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layouts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick random cells within rooms that are valid for stair placement and return Staircase objects.
|
||||||
|
fn pick_stair_positions(
|
||||||
|
layout: &DungeonLayout,
|
||||||
|
settings: &UiSettings,
|
||||||
|
count: usize,
|
||||||
|
is_elevator: bool,
|
||||||
|
) -> Vec<layout::Staircase> {
|
||||||
|
if layout.rooms.is_empty() || count == 0 {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect valid room cells (rooms without start/end markers on bottom row).
|
||||||
|
let mut valid_cells: Vec<(usize, usize)> = Vec::new();
|
||||||
|
for room in &layout.rooms {
|
||||||
|
let has_start_end_bottom =
|
||||||
|
has_start_or_end_on_bottom_row(room, &layout.start_markers, &layout.end_markers);
|
||||||
|
if !has_start_end_bottom {
|
||||||
|
for x in room.x..(room.x + room.width) {
|
||||||
|
for y in room.y..(room.y + room.height) {
|
||||||
|
valid_cells.push((x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if valid_cells.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle valid cells deterministically based on seed.
|
||||||
|
let mut shuffled = valid_cells.clone();
|
||||||
|
shuffle_with_seed(
|
||||||
|
&mut shuffled,
|
||||||
|
settings.seed.wrapping_add(STAIR_MARKER_STREAM_BASE),
|
||||||
|
);
|
||||||
|
|
||||||
|
let max_size = settings.max_stair_width.max(settings.max_stair_height);
|
||||||
|
let mut stair_cells: Vec<(usize, usize)> = Vec::new();
|
||||||
|
let mut used_cells: HashSet<(usize, usize)> = HashSet::new();
|
||||||
|
|
||||||
|
for cell in shuffled {
|
||||||
|
if stair_cells.len() >= count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if used_cells.contains(&cell) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Check if this cell can accommodate a stair of at least min size.
|
||||||
|
let min_w = settings.min_stair_width;
|
||||||
|
let min_h = settings.min_stair_height;
|
||||||
|
let room_idx = room_index_at_cell(&layout.rooms, cell);
|
||||||
|
if let Some(ri) = room_idx {
|
||||||
|
let room = &layout.rooms[ri];
|
||||||
|
let available_w = room.x + room.width - cell.0;
|
||||||
|
let available_h = room.y + room.height - cell.1;
|
||||||
|
if available_w >= min_w && available_h >= min_h {
|
||||||
|
stair_cells.push(cell);
|
||||||
|
// Mark occupied cells to avoid overlap.
|
||||||
|
for dx in 0..max_size {
|
||||||
|
for dy in 0..max_size {
|
||||||
|
used_cells.insert((cell.0 + dx, cell.1 + dy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert cells to Staircase objects with random sizes.
|
||||||
|
stair_cells
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, cell)| layout::Staircase {
|
||||||
|
cell,
|
||||||
|
size: random_stair_size(
|
||||||
|
settings,
|
||||||
|
seed::derive_seed(settings.seed, STAIR_MARKER_STREAM_BASE + i as u64),
|
||||||
|
),
|
||||||
|
is_elevator,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a room has a start or end marker on its bottom row.
|
||||||
|
fn has_start_or_end_on_bottom_row(
|
||||||
|
room: &layout::Room,
|
||||||
|
start_markers: &[layout::AreaMarker],
|
||||||
|
end_markers: &[layout::AreaMarker],
|
||||||
|
) -> bool {
|
||||||
|
let bottom_row = room.y + room.height - 1;
|
||||||
|
for marker in start_markers.iter().chain(end_markers.iter()) {
|
||||||
|
let marker_bottom = marker.cell.1 + marker.size;
|
||||||
|
if marker.cell.1 <= bottom_row && marker_bottom > bottom_row {
|
||||||
|
// Marker overlaps with bottom row.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a random stair size within settings range.
|
||||||
|
fn random_stair_size(settings: &UiSettings, seed_value: u64) -> usize {
|
||||||
|
let min_size = settings.min_stair_width.min(settings.min_stair_height);
|
||||||
|
let max_size = settings.max_stair_width.max(settings.max_stair_height);
|
||||||
|
let span = max_size - min_size + 1;
|
||||||
|
min_size + (seed_value as usize % span)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle a vector deterministically using a seed.
|
||||||
|
fn shuffle_with_seed<T: Clone>(vec: &mut [T], seed: u64) {
|
||||||
|
let mut rng = SimpleRng::new(seed);
|
||||||
|
for i in (1..vec.len()).rev() {
|
||||||
|
let j = (rng.next_u32() as usize) % (i + 1);
|
||||||
|
vec.swap(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple RNG for deterministic shuffling.
|
||||||
|
struct SimpleRng {
|
||||||
|
state: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleRng {
|
||||||
|
fn new(seed: u64) -> Self {
|
||||||
|
Self { state: seed }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_u32(&mut self) -> u32 {
|
||||||
|
// Simple LCG for deterministic shuffling.
|
||||||
|
self.state = self
|
||||||
|
.state
|
||||||
|
.wrapping_mul(6364136223846793005)
|
||||||
|
.wrapping_add(1442695040888963407);
|
||||||
|
(self.state >> 32) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -151,6 +151,13 @@ pub struct UiSettings {
|
|||||||
pub monster_frequency_percent: usize,
|
pub monster_frequency_percent: usize,
|
||||||
pub min_monsters_per_area: usize,
|
pub min_monsters_per_area: usize,
|
||||||
pub max_monsters_per_area: usize,
|
pub max_monsters_per_area: usize,
|
||||||
|
pub min_stair_width: usize,
|
||||||
|
pub max_stair_width: usize,
|
||||||
|
pub min_stair_height: usize,
|
||||||
|
pub max_stair_height: usize,
|
||||||
|
pub min_stairs_per_level: usize,
|
||||||
|
pub max_stairs_per_level: usize,
|
||||||
|
pub sync_stairs_across_levels: bool,
|
||||||
active_tab: Tab,
|
active_tab: Tab,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +215,13 @@ impl Default for UiSettings {
|
|||||||
monster_frequency_percent: 15,
|
monster_frequency_percent: 15,
|
||||||
min_monsters_per_area: 1,
|
min_monsters_per_area: 1,
|
||||||
max_monsters_per_area: 2,
|
max_monsters_per_area: 2,
|
||||||
|
min_stair_width: 1,
|
||||||
|
max_stair_width: 2,
|
||||||
|
min_stair_height: 2,
|
||||||
|
max_stair_height: 3,
|
||||||
|
min_stairs_per_level: 1,
|
||||||
|
max_stairs_per_level: 1,
|
||||||
|
sync_stairs_across_levels: false,
|
||||||
active_tab: Tab::Generate,
|
active_tab: Tab::Generate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -810,6 +824,114 @@ fn draw_layout_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut Si
|
|||||||
)
|
)
|
||||||
.changed();
|
.changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.add_space(12.0);
|
||||||
|
ui.separator();
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label(RichText::new("Staircase Settings").strong());
|
||||||
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.checkbox(
|
||||||
|
&mut settings.sync_stairs_across_levels,
|
||||||
|
"Sync Stairs Across All Levels",
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label("Min Stair Width");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(egui::Slider::new(&mut settings.min_stair_width, 1..=5).show_value(false))
|
||||||
|
.changed();
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(
|
||||||
|
egui::DragValue::new(&mut settings.min_stair_width)
|
||||||
|
.speed(1.0)
|
||||||
|
.range(1..=10),
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label("Max Stair Width");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(egui::Slider::new(&mut settings.max_stair_width, 1..=5).show_value(false))
|
||||||
|
.changed();
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(
|
||||||
|
egui::DragValue::new(&mut settings.max_stair_width)
|
||||||
|
.speed(1.0)
|
||||||
|
.range(1..=10),
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label("Min Stair Height");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(egui::Slider::new(&mut settings.min_stair_height, 1..=5).show_value(false))
|
||||||
|
.changed();
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(
|
||||||
|
egui::DragValue::new(&mut settings.min_stair_height)
|
||||||
|
.speed(1.0)
|
||||||
|
.range(1..=10),
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label("Max Stair Height");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(egui::Slider::new(&mut settings.max_stair_height, 1..=5).show_value(false))
|
||||||
|
.changed();
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(
|
||||||
|
egui::DragValue::new(&mut settings.max_stair_height)
|
||||||
|
.speed(1.0)
|
||||||
|
.range(1..=10),
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label("Min Stairs Per Level");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(egui::Slider::new(&mut settings.min_stairs_per_level, 0..=10).show_value(false))
|
||||||
|
.changed();
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(
|
||||||
|
egui::DragValue::new(&mut settings.min_stairs_per_level)
|
||||||
|
.speed(1.0)
|
||||||
|
.range(0..=20),
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.label("Max Stairs Per Level");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(egui::Slider::new(&mut settings.max_stairs_per_level, 0..=10).show_value(false))
|
||||||
|
.changed();
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.add(
|
||||||
|
egui::DragValue::new(&mut settings.max_stairs_per_level)
|
||||||
|
.speed(1.0)
|
||||||
|
.range(0..=20),
|
||||||
|
)
|
||||||
|
.changed();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(8.0);
|
||||||
|
result.settings_changed |= ui
|
||||||
|
.checkbox(&mut settings.windows_enabled, "Enable Elevators")
|
||||||
|
.changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the Add tab controls.
|
// Render the Add tab controls.
|
||||||
|
|||||||
Reference in New Issue
Block a user