diff --git a/src/exporter.rs b/src/exporter.rs index 470f742..f7201f9 100644 --- a/src/exporter.rs +++ b/src/exporter.rs @@ -22,6 +22,8 @@ const WINDOW: (u8, u8, u8, u8) = (70, 130, 220, 255); const WHITE: (u8, u8, u8, u8) = (255, 255, 255, 255); const START_MARKER: (u8, u8, u8, u8) = (60, 220, 200, 255); const END_MARKER: (u8, u8, u8, u8) = (240, 90, 90, 255); +const TRAP_MARKER: (u8, u8, u8, u8) = (150, 80, 230, 255); +const MONSTER_MARKER: (u8, u8, u8, u8) = (200, 50, 50, 255); pub enum ExportTarget { File(PathBuf), @@ -292,6 +294,22 @@ fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result String { END_MARKER, settings.colorblind_mode, ); + append_svg_area_marker_group( + &mut s, + &g, + &layout.trap_markers, + "T", + TRAP_MARKER, + settings.colorblind_mode, + ); + append_svg_area_marker_group( + &mut s, + &g, + &layout.monster_markers, + "M", + MONSTER_MARKER, + settings.colorblind_mode, + ); s.push_str("\n"); s @@ -818,7 +858,12 @@ fn draw_area_marker( color: (u8, u8, u8, u8), colorblind_mode: bool, ) { - let overlay = (color.0, color.1, color.2, if colorblind_mode { 56 } else { 90 }); + let overlay = ( + color.0, + color.1, + color.2, + if colorblind_mode { 56 } else { 90 }, + ); fill_rect_marker(pixmap, g, marker, overlay); let x = g.left() + marker.cell.0 as f32 * g.cell; @@ -852,7 +897,7 @@ fn draw_area_marker( symbol, DoorStyle::Solid, ); - } else { + } else if label == "E" { draw_line( pixmap, x + w * 0.25, @@ -873,6 +918,70 @@ fn draw_area_marker( symbol, DoorStyle::Solid, ); + } else if label == "T" { + // Horizontal bar + draw_line( + pixmap, + x + w * 0.2, + y + h * 0.25, + x + w * 0.8, + y + h * 0.25, + 4.0, + symbol, + DoorStyle::Solid, + ); + // Vertical bar + draw_line( + pixmap, + x + w * 0.5, + y + h * 0.25, + x + w * 0.5, + y + h * 0.75, + 4.0, + symbol, + DoorStyle::Solid, + ); + } else if label == "M" { + draw_line( + pixmap, + x + w * 0.2, + y + h * 0.75, + x + w * 0.2, + y + h * 0.25, + 4.0, + symbol, + DoorStyle::Solid, + ); + draw_line( + pixmap, + x + w * 0.2, + y + h * 0.25, + x + w * 0.5, + y + h * 0.5, + 4.0, + symbol, + DoorStyle::Solid, + ); + draw_line( + pixmap, + x + w * 0.5, + y + h * 0.5, + x + w * 0.8, + y + h * 0.25, + 4.0, + symbol, + DoorStyle::Solid, + ); + draw_line( + pixmap, + x + w * 0.8, + y + h * 0.25, + x + w * 0.8, + y + h * 0.75, + 4.0, + symbol, + DoorStyle::Solid, + ); } } @@ -1172,30 +1281,70 @@ fn append_svg_area_marker( if label == "S" { let _ = writeln!( out, - "", + "", x + w * 0.25, y + h * 0.75, x + w * 0.5, y + h * 0.25, x + w * 0.75, - y + h * 0.75 + y + h * 0.75, + symbol ); - } else { + } else if label == "E" { let _ = writeln!( out, - "", + "", x + w * 0.25, y + h * 0.25, x + w * 0.75, - y + h * 0.75 + y + h * 0.75, + symbol ); let _ = writeln!( out, - "", + "", x + w * 0.75, y + h * 0.25, x + w * 0.25, - y + h * 0.75 + y + h * 0.75, + symbol + ); + } else if label == "T" { + // Horizontal bar + let _ = writeln!( + out, + "", + x + w * 0.2, + y + h * 0.25, + x + w * 0.8, + y + h * 0.25, + symbol + ); + // Vertical bar + let _ = writeln!( + out, + "", + x + w * 0.5, + y + h * 0.25, + x + w * 0.5, + y + h * 0.75, + symbol + ); + } else if label == "M" { + let _ = writeln!( + out, + "", + x + w * 0.2, + y + h * 0.75, + x + w * 0.2, + y + h * 0.25, + x + w * 0.5, + y + h * 0.5, + x + w * 0.8, + y + h * 0.25, + x + w * 0.8, + y + h * 0.75, + symbol ); } } diff --git a/src/layout.rs b/src/layout.rs index 6224e9f..7e4c216 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -96,6 +96,8 @@ pub struct DungeonLayout { pub text_labels: Vec, pub start_markers: Vec, pub end_markers: Vec, + pub trap_markers: Vec, + pub monster_markers: Vec, pub packed_rooms: bool, } @@ -109,6 +111,8 @@ impl Default for DungeonLayout { text_labels: Vec::new(), start_markers: Vec::new(), end_markers: Vec::new(), + trap_markers: Vec::new(), + monster_markers: Vec::new(), packed_rooms: false, } } @@ -178,6 +182,8 @@ pub fn generate_layout( text_labels: Vec::new(), start_markers: Vec::new(), end_markers: Vec::new(), + trap_markers: Vec::new(), + monster_markers: Vec::new(), packed_rooms: pack_rooms_without_corridors, }; } @@ -203,6 +209,8 @@ pub fn generate_layout( text_labels: Vec::new(), start_markers: Vec::new(), end_markers: Vec::new(), + trap_markers: Vec::new(), + monster_markers: Vec::new(), packed_rooms: pack_rooms_without_corridors, }; } @@ -233,6 +241,8 @@ pub fn generate_layout( text_labels: Vec::new(), start_markers: Vec::new(), end_markers: Vec::new(), + trap_markers: Vec::new(), + monster_markers: Vec::new(), packed_rooms: true, }; apply_doors(&mut layout, seed, door_settings, cols, rows); @@ -292,6 +302,8 @@ pub fn generate_layout( text_labels: Vec::new(), start_markers: Vec::new(), end_markers: Vec::new(), + trap_markers: Vec::new(), + monster_markers: Vec::new(), packed_rooms: false, }; } @@ -348,6 +360,8 @@ pub fn generate_layout( text_labels: Vec::new(), start_markers: Vec::new(), end_markers: Vec::new(), + trap_markers: Vec::new(), + monster_markers: Vec::new(), packed_rooms: false, }; apply_doors(&mut layout, seed, door_settings, cols, rows); diff --git a/src/main.rs b/src/main.rs index f73cab4..f61f6e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,10 @@ use layout::{ DoorSettings, DungeonLayout, WindowSettings, blocked_room_cells, corridor_cells, shortest_path_cells, }; -use startend::{manual_marker_size, populate_random_markers}; +use startend::{ + manual_marker_size, manual_monster_marker_size, manual_trap_marker_size, + populate_random_markers, +}; use ui::{AddTool, UiSettings, draw_side_panel}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -199,6 +202,8 @@ impl eframe::App for DungeonApp { draw_legend_entry_colorblind(ui, "Windows", LegendStyle::DashDotLine); draw_legend_entry_colorblind(ui, "Start Marker", LegendStyle::SolidLine); draw_legend_entry_colorblind(ui, "End Marker", LegendStyle::SolidLine); + draw_legend_entry_colorblind(ui, "Trap", LegendStyle::SolidLine); + draw_legend_entry_colorblind(ui, "Monster", LegendStyle::SolidLine); } else { draw_legend_entry(ui, Color32::from_rgb(70, 120, 160), "Rooms"); draw_legend_entry(ui, Color32::from_rgb(210, 190, 120), "Corridors"); @@ -210,13 +215,17 @@ impl eframe::App for DungeonApp { draw_legend_entry(ui, Color32::from_rgb(70, 130, 220), "Windows"); draw_legend_entry(ui, Color32::from_rgb(60, 220, 200), "Start Marker"); draw_legend_entry(ui, Color32::from_rgb(240, 90, 90), "End Marker"); + draw_legend_entry(ui, Color32::from_rgb(150, 80, 230), "Trap"); + draw_legend_entry(ui, Color32::from_rgb(200, 50, 50), "Monster"); } ui.add_space(10.0); ui.separator(); ui.add_space(8.0); ui.heading("Keybinds"); ui.add_space(6.0); - ui.label("• Delete / Backspace: Remove hovered room, corridor, door, text, or marker"); + ui.label( + "• Delete / Backspace: Remove hovered room, corridor, door, text, or marker", + ); ui.label("• Ctrl+Z: Undo"); ui.label("• Ctrl+Y: Redo"); ui.label("• Left click + drag: Move hovered room"); @@ -282,6 +291,12 @@ fn clamp_dependent_settings(settings: &mut UiSettings) { if settings.min_end_marker_count > settings.max_end_marker_count { settings.max_end_marker_count = settings.min_end_marker_count; } + if settings.min_traps_per_area > settings.max_traps_per_area { + settings.max_traps_per_area = settings.min_traps_per_area; + } + if settings.min_monsters_per_area > settings.max_monsters_per_area { + settings.max_monsters_per_area = settings.min_monsters_per_area; + } } impl DungeonApp { @@ -706,7 +721,7 @@ impl DungeonApp { self.settings.add_tool = AddTool::None; } } - AddTool::StartMarker | AddTool::EndMarker => { + AddTool::StartMarker | AddTool::EndMarker | AddTool::TrapMarker => { self.add_corridor_drag = None; if response.clicked() && let Some(pointer_pos) = response.interact_pointer_pos() @@ -716,6 +731,7 @@ impl DungeonApp { let kind = match self.settings.add_tool { AddTool::StartMarker => MarkerKind::Start, AddTool::EndMarker => MarkerKind::End, + AddTool::TrapMarker => MarkerKind::Trap, _ => unreachable!(), }; self.add_marker_at_cell(cell, kind); @@ -854,7 +870,12 @@ impl DungeonApp { return; } - let size = manual_marker_size(&self.settings, matches!(kind, MarkerKind::Start)); + let size = match kind { + MarkerKind::Start => manual_marker_size(&self.settings, true), + MarkerKind::End => manual_marker_size(&self.settings, false), + MarkerKind::Trap => manual_trap_marker_size(&self.settings), + MarkerKind::Monster => manual_monster_marker_size(&self.settings), + }; let origin = centered_marker_origin(cell, size, self.settings.cols, self.settings.rows); let marker = layout::AreaMarker { cell: origin, size }; @@ -862,6 +883,8 @@ impl DungeonApp { match kind { MarkerKind::Start => self.layout.start_markers.push(marker), MarkerKind::End => self.layout.end_markers.push(marker), + MarkerKind::Trap => self.layout.trap_markers.push(marker), + MarkerKind::Monster => self.layout.monster_markers.push(marker), } } @@ -953,6 +976,22 @@ impl DungeonApp { .find(|(_, marker)| marker_contains_cell(Some(marker), clicked)) .map(|(idx, _)| HoverMarker::End(idx)) }) + .or_else(|| { + self.layout + .trap_markers + .iter() + .enumerate() + .find(|(_, marker)| marker_contains_cell(Some(marker), clicked)) + .map(|(idx, _)| HoverMarker::Trap(idx)) + }) + .or_else(|| { + self.layout + .monster_markers + .iter() + .enumerate() + .find(|(_, marker)| marker_contains_cell(Some(marker), clicked)) + .map(|(idx, _)| HoverMarker::Monster(idx)) + }) } fn marker_drag_at_pointer( @@ -980,6 +1019,24 @@ impl DungeonApp { offset_y: grid_y - marker.cell.1 as f32, }) } + HoverMarker::Trap(marker_idx) => { + let marker = self.layout.trap_markers.get(marker_idx)?; + Some(MarkerDragState { + kind: MarkerKind::Trap, + marker_idx, + offset_x: grid_x - marker.cell.0 as f32, + offset_y: grid_y - marker.cell.1 as f32, + }) + } + HoverMarker::Monster(marker_idx) => { + let marker = self.layout.monster_markers.get(marker_idx)?; + Some(MarkerDragState { + kind: MarkerKind::Monster, + marker_idx, + offset_x: grid_x - marker.cell.0 as f32, + offset_y: grid_y - marker.cell.1 as f32, + }) + } } } @@ -996,6 +1053,8 @@ impl DungeonApp { let markers = match drag.kind { MarkerKind::Start => &mut self.layout.start_markers, MarkerKind::End => &mut self.layout.end_markers, + MarkerKind::Trap => &mut self.layout.trap_markers, + MarkerKind::Monster => &mut self.layout.monster_markers, }; let Some(marker) = markers.get_mut(drag.marker_idx) else { self.drag_state = None; @@ -1336,6 +1395,16 @@ impl DungeonApp { self.layout.end_markers.remove(idx); } } + HoverMarker::Trap(idx) => { + if idx < self.layout.trap_markers.len() { + self.layout.trap_markers.remove(idx); + } + } + HoverMarker::Monster(idx) => { + if idx < self.layout.monster_markers.len() { + self.layout.monster_markers.remove(idx); + } + } } return; } @@ -1584,12 +1653,16 @@ enum ResizeCorner { enum MarkerKind { Start, End, + Trap, + Monster, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum HoverMarker { Start(usize), End(usize), + Trap(usize), + Monster(usize), } #[derive(Debug, Clone)] @@ -1957,6 +2030,26 @@ fn draw_layout( hover_marker, MarkerKind::End, ); + draw_area_marker_group( + painter, + geometry, + &layout.trap_markers, + "T", + Color32::from_rgb(150, 80, 230), // Purple + colorblind_mode, + hover_marker, + MarkerKind::Trap, + ); + draw_area_marker_group( + painter, + geometry, + &layout.monster_markers, + "M", + Color32::from_rgb(200, 50, 50), // Red + colorblind_mode, + hover_marker, + MarkerKind::Monster, + ); } // Compute the rectangle for a given grid cell. @@ -2001,7 +2094,10 @@ fn centered_marker_origin( let half = size / 2; let max_x = cols.saturating_sub(size); let max_y = rows.saturating_sub(size); - (cell.0.saturating_sub(half).min(max_x), cell.1.saturating_sub(half).min(max_y)) + ( + cell.0.saturating_sub(half).min(max_x), + cell.1.saturating_sub(half).min(max_y), + ) } fn draw_area_marker( @@ -2059,6 +2155,8 @@ fn draw_area_marker_group( let hovered = match (kind, hover_marker) { (MarkerKind::Start, Some(HoverMarker::Start(hover_idx))) => hover_idx == idx, (MarkerKind::End, Some(HoverMarker::End(hover_idx))) => hover_idx == idx, + (MarkerKind::Trap, Some(HoverMarker::Trap(hover_idx))) => hover_idx == idx, + (MarkerKind::Monster, Some(HoverMarker::Monster(hover_idx))) => hover_idx == idx, _ => false, }; draw_area_marker( diff --git a/src/startend.rs b/src/startend.rs index b0dea6e..44d3664 100644 --- a/src/startend.rs +++ b/src/startend.rs @@ -10,14 +10,17 @@ const EXTRA_START_ROOM_STREAM_BASE: u64 = 13_000; const EXTRA_START_MARKER_STREAM_BASE: u64 = 14_000; const EXTRA_END_ROOM_STREAM_BASE: u64 = 15_000; const EXTRA_END_MARKER_STREAM_BASE: u64 = 16_000; +const TRAP_ROOM_STREAM_BASE: u64 = 17_000; +const TRAP_CORRIDOR_STREAM_BASE: u64 = 18_000; +const MONSTER_ROOM_STREAM_BASE: u64 = 19_000; +const MONSTER_CORRIDOR_STREAM_BASE: u64 = 20_000; // Populate generated start/end markers after the core dungeon layout exists. -pub fn populate_random_markers( - mut layout: DungeonLayout, - settings: &UiSettings, -) -> DungeonLayout { +pub fn populate_random_markers(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout { layout.start_markers.clear(); layout.end_markers.clear(); + layout.trap_markers.clear(); + layout.monster_markers.clear(); if layout.rooms.is_empty() { return layout; @@ -84,6 +87,9 @@ pub fn populate_random_markers( EXTRA_END_MARKER_STREAM_BASE, ); + layout = populate_random_traps(layout, settings); + layout = populate_random_monsters(layout, settings); + layout } @@ -94,11 +100,21 @@ pub fn manual_marker_size(settings: &UiSettings, is_start: bool) -> usize { .min_start_marker_size .min(settings.max_start_marker_size) } else { - settings.min_end_marker_size.min(settings.max_end_marker_size) + settings + .min_end_marker_size + .min(settings.max_end_marker_size) } .clamp(1, 10) } +pub fn manual_trap_marker_size(_settings: &UiSettings) -> usize { + 1 +} + +pub fn manual_monster_marker_size(_settings: &UiSettings) -> usize { + 1 +} + // Pick a deterministic inclusive random value from a seed-derived stream. fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize { let min = min.max(1); @@ -220,3 +236,112 @@ fn pick_room_index(available_rooms: &[usize], room_count: usize, seed_value: u64 seed_value as usize % room_count.max(1) } } + +pub fn populate_random_traps(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout { + if settings.trap_frequency_percent == 0 { + return layout; + } + + let mut trap_markers = Vec::new(); + + // Rooms + for (idx, room) in layout.rooms.iter().enumerate() { + let seed = seed::derive_seed(settings.seed, TRAP_ROOM_STREAM_BASE + idx as u64); + if (seed % 100) < settings.trap_frequency_percent as u64 { + let count = random_range_inclusive( + settings.min_traps_per_area, + settings.max_traps_per_area, + seed.rotate_left(13), + ); + for i in 0..count { + trap_markers.push(marker_in_room( + room, + 1, + 1, + seed.wrapping_add(i as u64).rotate_right(7), + )); + } + } + } + + // Corridors + for (idx, corridor) in layout.corridors.iter().enumerate() { + let seed = seed::derive_seed(settings.seed, TRAP_CORRIDOR_STREAM_BASE + idx as u64); + if (seed % 100) < settings.trap_frequency_percent as u64 { + let count = random_range_inclusive( + settings.min_traps_per_area, + settings.max_traps_per_area, + seed.rotate_left(13), + ); + for i in 0..count { + if let Some(cell) = pick_random_corridor_cell(corridor, seed.wrapping_add(i as u64)) + { + trap_markers.push(layout::AreaMarker { cell, size: 1 }); + } + } + } + } + + layout.trap_markers = trap_markers; + layout +} + +pub fn populate_random_monsters(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout { + if settings.monster_frequency_percent == 0 { + return layout; + } + + let mut monster_markers = Vec::new(); + + // Rooms + for (idx, room) in layout.rooms.iter().enumerate() { + let seed = seed::derive_seed(settings.seed, MONSTER_ROOM_STREAM_BASE + idx as u64); + if (seed % 100) < settings.monster_frequency_percent as u64 { + let count = random_range_inclusive( + settings.min_monsters_per_area, + settings.max_monsters_per_area, + seed.rotate_left(13), + ); + for i in 0..count { + monster_markers.push(marker_in_room( + room, + 1, + 1, + seed.wrapping_add(i as u64).rotate_right(7), + )); + } + } + } + + // Corridors + for (idx, corridor) in layout.corridors.iter().enumerate() { + let seed = seed::derive_seed(settings.seed, MONSTER_CORRIDOR_STREAM_BASE + idx as u64); + if (seed % 100) < settings.monster_frequency_percent as u64 { + let count = random_range_inclusive( + settings.min_monsters_per_area, + settings.max_monsters_per_area, + seed.rotate_left(13), + ); + for i in 0..count { + if let Some(cell) = pick_random_corridor_cell(corridor, seed.wrapping_add(i as u64)) + { + monster_markers.push(layout::AreaMarker { cell, size: 1 }); + } + } + } + } + + layout.monster_markers = monster_markers; + layout +} + +fn pick_random_corridor_cell( + corridor: &layout::Corridor, + seed_value: u64, +) -> Option<(usize, usize)> { + if corridor.path.is_empty() { + return None; + } + let idx = (seed_value as usize) % corridor.path.len(); + Some(corridor.path[idx]) +} diff --git a/src/ui.rs b/src/ui.rs index fc94744..256d744 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -8,6 +8,7 @@ enum Tab { Generate, Layout, StartAndEnd, + MonstersAndTraps, Add, } @@ -140,6 +141,12 @@ pub struct UiSettings { pub max_start_marker_count: usize, pub min_end_marker_count: usize, pub max_end_marker_count: usize, + pub trap_frequency_percent: usize, + pub min_traps_per_area: usize, + pub max_traps_per_area: usize, + pub monster_frequency_percent: usize, + pub min_monsters_per_area: usize, + pub max_monsters_per_area: usize, active_tab: Tab, } @@ -187,6 +194,12 @@ impl Default for UiSettings { max_start_marker_count: 1, min_end_marker_count: 1, max_end_marker_count: 1, + trap_frequency_percent: 20, + min_traps_per_area: 1, + max_traps_per_area: 3, + monster_frequency_percent: 15, + min_monsters_per_area: 1, + max_monsters_per_area: 2, active_tab: Tab::Generate, } } @@ -204,6 +217,7 @@ pub enum AddTool { Text, StartMarker, EndMarker, + TrapMarker, } #[derive(Debug, Clone, Copy, Default)] @@ -231,8 +245,6 @@ pub fn draw_side_panel( ui.horizontal(|ui| { let is_generate = settings.active_tab == Tab::Generate; let is_layout = settings.active_tab == Tab::Layout; - let is_start_and_end = settings.active_tab == Tab::StartAndEnd; - let is_add = settings.active_tab == Tab::Add; if ui.selectable_label(is_generate, "Generate").clicked() { settings.active_tab = Tab::Generate; @@ -240,10 +252,25 @@ pub fn draw_side_panel( if ui.selectable_label(is_layout, "Layout").clicked() { settings.active_tab = Tab::Layout; } - if ui.selectable_label(is_start_and_end, "Start & End").clicked() { + if ui + .selectable_label(settings.active_tab == Tab::StartAndEnd, "Start & End") + .clicked() + { settings.active_tab = Tab::StartAndEnd; } - if ui.selectable_label(is_add, "Add").clicked() { + if ui + .selectable_label( + settings.active_tab == Tab::MonstersAndTraps, + "Monsters/Traps", + ) + .clicked() + { + settings.active_tab = Tab::MonstersAndTraps; + } + if ui + .selectable_label(settings.active_tab == Tab::Add, "Add") + .clicked() + { settings.active_tab = Tab::Add; } }); @@ -253,6 +280,7 @@ pub fn draw_side_panel( Tab::Generate => draw_generate_tab(ui, settings, &mut result, export_in_progress), Tab::Layout => draw_layout_tab(ui, settings, &mut result), Tab::StartAndEnd => draw_start_and_end_tab(ui, settings, &mut result), + Tab::MonstersAndTraps => draw_monsters_and_traps_tab(ui, settings, &mut result), Tab::Add => draw_add_tab(ui, settings), } }); @@ -751,6 +779,7 @@ fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings) { ui.selectable_value(&mut settings.add_tool, AddTool::Text, "Add Text"); ui.selectable_value(&mut settings.add_tool, AddTool::StartMarker, "Place Start"); ui.selectable_value(&mut settings.add_tool, AddTool::EndMarker, "Place End"); + ui.selectable_value(&mut settings.add_tool, AddTool::TrapMarker, "Place Trap"); }); ui.add_space(6.0); @@ -772,6 +801,7 @@ fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings) { } AddTool::StartMarker => ui.label("Click the grid to place the start marker."), AddTool::EndMarker => ui.label("Click the grid to place the end marker."), + AddTool::TrapMarker => ui.label("Click the grid to place a trap marker."), AddTool::None => ui.label("Select a tool to add rooms or corridors."), }; } @@ -858,6 +888,69 @@ fn draw_start_and_end_tab( ui.label("Markers are generated inside rooms. Matching start/end indices are paired as far apart as possible."); } +fn draw_monsters_and_traps_tab( + ui: &mut egui::Ui, + settings: &mut UiSettings, + result: &mut SidePanelResult, +) { + ui.label(RichText::new("Trap Settings").strong()); + ui.add_space(8.0); + draw_usize_slider_row( + ui, + result, + "Trap Frequency (%)", + &mut settings.trap_frequency_percent, + 0..=100, + 0..=100, + ); + draw_usize_slider_row( + ui, + result, + "Min Traps Per Room/Corridor", + &mut settings.min_traps_per_area, + 0..=20, + 0..=100, + ); + draw_usize_slider_row( + ui, + result, + "Max Traps Per Room/Corridor", + &mut settings.max_traps_per_area, + 0..=20, + 0..=100, + ); + + ui.add_space(12.0); + ui.separator(); + ui.add_space(8.0); + ui.label(RichText::new("Monster Settings").strong()); + ui.add_space(8.0); + draw_usize_slider_row( + ui, + result, + "Monster Frequency (%)", + &mut settings.monster_frequency_percent, + 0..=100, + 0..=100, + ); + draw_usize_slider_row( + ui, + result, + "Min Monsters Per Room/Corridor", + &mut settings.min_monsters_per_area, + 0..=20, + 0..=100, + ); + draw_usize_slider_row( + ui, + result, + "Max Monsters Per Room/Corridor", + &mut settings.max_monsters_per_area, + 0..=20, + 0..=100, + ); +} + fn draw_usize_slider_row( ui: &mut egui::Ui, result: &mut SidePanelResult,