massive commenting pass to explain random functions and their purpose

This commit is contained in:
grimsace
2026-05-18 10:30:59 -05:00
parent ab713a5a4f
commit c71cc73406
21 changed files with 229 additions and 0 deletions
+22
View File
@@ -56,6 +56,7 @@ pub struct DungeonApp {
}
impl Default for DungeonApp {
// Provides default settings for this type.
fn default() -> Self {
let settings = settings::load_settings().unwrap_or_default();
let mut app = Self {
@@ -89,6 +90,7 @@ impl Default for DungeonApp {
}
impl Drop for DungeonApp {
// Cleans up background resources when the app is dropped.
fn drop(&mut self) {
if let Err(err) = settings::save_settings(&self.settings) {
eprintln!("Failed to save settings: {err}");
@@ -97,6 +99,7 @@ impl Drop for DungeonApp {
}
impl eframe::App for DungeonApp {
// Runs one UI frame and handles app interaction.
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let panel_result = draw_side_panel(
ctx,
@@ -299,6 +302,7 @@ impl eframe::App for DungeonApp {
}
}
// Generates all levels.
pub fn generate_all_levels(settings: &UiSettings) -> Vec<DungeonLayout> {
let level_count = if settings.min_levels == settings.max_levels {
settings.min_levels
@@ -315,6 +319,7 @@ pub fn generate_all_levels(settings: &UiSettings) -> Vec<DungeonLayout> {
levels
}
// Generates a single dungeon level from the UI settings.
pub fn generate_level(settings: &UiSettings, level_index: usize) -> DungeonLayout {
let level_seed = crate::seed::derive_seed(settings.seed, level_index as u64);
let layout = layout::generate_layout(
@@ -337,6 +342,7 @@ pub fn generate_level(settings: &UiSettings, level_index: usize) -> DungeonLayou
populate_random_markers(layout, settings)
}
// Clamps dependent settings.
pub fn clamp_dependent_settings(settings: &mut UiSettings) {
if settings.min_room_size > settings.max_room_size {
settings.max_room_size = settings.min_room_size;
@@ -377,6 +383,7 @@ pub fn clamp_dependent_settings(settings: &mut UiSettings) {
}
impl DungeonApp {
// Clears hover targets.
pub fn clear_hover_targets(&mut self) {
self.hover_room_idx = None;
self.hover_corridor_idx = None;
@@ -387,6 +394,7 @@ impl DungeonApp {
self.hover_stair_idx = None;
}
// Resets transient state.
pub fn reset_transient_state(&mut self) {
self.drag_state = None;
self.add_corridor_drag = None;
@@ -395,6 +403,7 @@ impl DungeonApp {
self.pending_delete = false;
}
// Captures the current app state for undo history.
pub fn snapshot(&self) -> AppSnapshot {
AppSnapshot {
settings: self.settings.clone(),
@@ -403,6 +412,7 @@ impl DungeonApp {
}
}
// Pushes undo snapshot.
pub fn push_undo_snapshot(&mut self) {
let snapshot = self.snapshot();
if self.undo_stack.last() == Some(&snapshot) {
@@ -415,6 +425,7 @@ impl DungeonApp {
self.redo_stack.clear();
}
// Restores a saved app state snapshot.
pub fn restore_snapshot(&mut self, snapshot: AppSnapshot) {
self.settings = snapshot.settings;
self.levels = snapshot.levels;
@@ -422,6 +433,7 @@ impl DungeonApp {
self.reset_transient_state();
}
// Restores the previous undo snapshot. undo.
pub fn undo(&mut self) {
let Some(snapshot) = self.undo_stack.pop() else {
return;
@@ -430,6 +442,7 @@ impl DungeonApp {
self.restore_snapshot(snapshot);
}
// Restores the next redo snapshot. redo.
pub fn redo(&mut self) {
let Some(snapshot) = self.redo_stack.pop() else {
return;
@@ -438,6 +451,7 @@ impl DungeonApp {
self.restore_snapshot(snapshot);
}
// Clears all generated layout content and resets editing state.
pub fn clear_layout(&mut self) {
if self.settings.active_level_index >= self.levels.len() {
return;
@@ -448,6 +462,7 @@ impl DungeonApp {
self.reset_transient_state();
}
// Switches to composition layout.
pub fn enter_composition_layout(&mut self) {
self.levels = vec![DungeonLayout::empty(
self.settings.pack_rooms_without_corridors,
@@ -458,6 +473,7 @@ impl DungeonApp {
self.reset_transient_state();
}
// Regenerates every level from the current settings.
pub fn regenerate_layout(&mut self) {
self.drag_state = None;
self.suppressed_auto_door_edges.clear();
@@ -468,6 +484,7 @@ impl DungeonApp {
}
}
// Deletes a level and keeps the active level index valid.
pub fn delete_level(&mut self, level_idx: usize) {
if self.levels.len() <= 1 {
return;
@@ -483,6 +500,7 @@ impl DungeonApp {
self.reset_transient_state();
}
// Regenerates one level while preserving manual annotations where possible.
pub fn refresh_level(&mut self, level_idx: usize) {
if level_idx >= self.levels.len() {
return;
@@ -534,6 +552,7 @@ impl DungeonApp {
);
}
// Recomputes staircase links across the current levels.
pub fn refresh_stairs(&mut self) {
let results = populate_stairs(self.levels.clone(), &self.settings);
self.levels = results.iter().map(|(layout, _)| layout.clone()).collect();
@@ -545,6 +564,7 @@ impl DungeonApp {
}
}
// Recomputes generated doors while keeping manual doors.
pub fn refresh_doors(&mut self) {
if self.settings.active_level_index >= self.levels.len() {
return;
@@ -573,6 +593,7 @@ impl DungeonApp {
}
}
// Computes settings from ui.
pub fn door_settings_from_ui(settings: &UiSettings) -> DoorSettings {
DoorSettings {
frequency_percent: settings.door_frequency_percent,
@@ -583,6 +604,7 @@ pub fn door_settings_from_ui(settings: &UiSettings) -> DoorSettings {
}
}
// Computes settings from ui.
pub fn window_settings_from_ui(settings: &UiSettings) -> WindowSettings {
WindowSettings {
enabled: settings.windows_enabled,
+14
View File
@@ -24,6 +24,7 @@ pub use types::*;
type ProgressSender = mpsc::Sender<ExportEvent>;
// Selects export target.
pub fn select_export_target(settings: &UiSettings) -> Result<ExportTarget, String> {
if settings.export_format == ExportFormat::Folder || settings.export_level_index == 999 {
let title = if settings.export_level_index == 999 {
@@ -65,6 +66,7 @@ pub fn select_export_target(settings: &UiSettings) -> Result<ExportTarget, Strin
Ok(ExportTarget::File(path))
}
// Exports to target.
pub fn export_to_target(
layouts: &[DungeonLayout],
settings: &UiSettings,
@@ -112,6 +114,7 @@ pub fn export_to_target(
}
}
// Exports image count.
fn export_image_count(settings: &UiSettings, target: &ExportTarget, level_count: usize) -> usize {
match target {
ExportTarget::File(_) => 1,
@@ -122,6 +125,7 @@ fn export_image_count(settings: &UiSettings, target: &ExportTarget, level_count:
}
}
// Counts images per level.
fn composite_images_per_level(settings: &UiSettings) -> usize {
let base_masks = 12;
let grid_mask = usize::from(settings.export_show_grid);
@@ -129,10 +133,12 @@ fn composite_images_per_level(settings: &UiSettings) -> usize {
base_masks + grid_mask + composite
}
// Sends an export progress update to the UI channel.
fn report_progress(progress_tx: &ProgressSender, completed: usize, total: usize) {
let _ = progress_tx.send(ExportEvent::Progress(ExportProgress { completed, total }));
}
// Exports level images to folder.
fn export_level_images_to_folder(
layouts: &[DungeonLayout],
settings: &UiSettings,
@@ -155,6 +161,7 @@ fn export_level_images_to_folder(
})
}
// Saves layout to file.
fn save_layout_to_file(
layout: &DungeonLayout,
settings: &UiSettings,
@@ -182,6 +189,7 @@ fn save_layout_to_file(
Ok(())
}
// Exports composite masks for levels.
fn export_composite_masks_for_levels(
layouts: &[DungeonLayout],
settings: &UiSettings,
@@ -265,6 +273,7 @@ fn export_composite_masks_for_levels(
Ok(())
}
// Saves composite image.
fn save_composite_image(
context: &CompositeExportContext<'_>,
settings: &UiSettings,
@@ -285,6 +294,7 @@ fn save_composite_image(
Ok(())
}
// Renders mask pixmap.
fn render_mask_pixmap(
context: &CompositeExportContext<'_>,
task: MaskTask,
@@ -441,6 +451,7 @@ fn render_mask_pixmap(
Ok(result)
}
// Creates an empty pixmap for a named export mask.
fn blank_mask(g: &ExportGeometry, name: &str) -> Result<Pixmap, String> {
let mut pixmap = Pixmap::new(g.width, g.height)
.ok_or_else(|| format!("Failed to allocate {name} mask canvas"))?;
@@ -448,6 +459,7 @@ fn blank_mask(g: &ExportGeometry, name: &str) -> Result<Pixmap, String> {
Ok(pixmap)
}
// Fills marker mask.
fn fill_marker_mask(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -458,6 +470,7 @@ fn fill_marker_mask(
}
}
// Saves mask image.
fn save_mask_image(
folder: &std::path::Path,
stem: &str,
@@ -472,6 +485,7 @@ fn save_mask_image(
Ok(())
}
// Selects image format.
fn mask_image_format(format: MaskFormat) -> ImageFormat {
match format {
MaskFormat::Png => ImageFormat::Png,
+25
View File
@@ -16,12 +16,14 @@ use image::{DynamicImage, RgbaImage, imageops::FilterType};
use std::collections::HashSet;
use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Rect, Stroke, StrokeDash, Transform};
// Renders a dungeon layout into a raster pixmap.
pub fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result<Pixmap, String> {
let g = ExportGeometry::new(settings.cols, settings.rows);
let scene = collect_scene_data(layout, settings);
render_pixmap_with_scene(layout, settings, &g, &scene)
}
// Renders pixmap with scene.
pub fn render_pixmap_with_scene(
layout: &DungeonLayout,
settings: &UiSettings,
@@ -150,6 +152,7 @@ pub fn render_pixmap_with_scene(
Ok(pixmap)
}
// Fills the entire pixmap with a background color.
pub fn fill_bg(pixmap: &mut Pixmap, color: (u8, u8, u8, u8)) {
let mut paint = Paint::default();
paint.set_color_rgba8(color.0, color.1, color.2, color.3);
@@ -160,6 +163,7 @@ pub fn fill_bg(pixmap: &mut Pixmap, color: (u8, u8, u8, u8)) {
pixmap.fill_rect(rect, &paint, Transform::identity(), None);
}
// Draws grid color.
pub fn draw_grid_color(pixmap: &mut Pixmap, g: &ExportGeometry, color: (u8, u8, u8, u8)) {
let left = g.left();
let top = g.top();
@@ -185,6 +189,7 @@ pub fn draw_grid_color(pixmap: &mut Pixmap, g: &ExportGeometry, color: (u8, u8,
}
}
// Fills rect cell.
pub fn fill_rect_cell(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -200,6 +205,7 @@ pub fn fill_rect_cell(
pixmap.fill_rect(rect, &paint, Transform::identity(), None);
}
// Fills rect room.
pub fn fill_rect_room(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -220,6 +226,7 @@ pub fn fill_rect_room(
pixmap.fill_rect(rect, &paint, Transform::identity(), None);
}
// Fills rect marker.
pub fn fill_rect_marker(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -239,6 +246,7 @@ pub fn fill_rect_marker(
pixmap.fill_rect(rect, &paint, Transform::identity(), None);
}
// Draws area marker.
pub fn draw_area_marker(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -374,6 +382,7 @@ pub fn draw_area_marker(
}
}
// Fills rect staircase.
pub fn fill_rect_staircase(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -393,6 +402,7 @@ pub fn fill_rect_staircase(
pixmap.fill_rect(rect, &paint, Transform::identity(), None);
}
// Draws a staircase marker into the raster export.
pub fn draw_staircase(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -451,6 +461,7 @@ pub fn draw_staircase(
}
}
// Draws staircase group.
pub fn draw_staircase_group(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -463,6 +474,7 @@ pub fn draw_staircase_group(
}
}
// Draws area marker group.
pub fn draw_area_marker_group(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -476,6 +488,7 @@ pub fn draw_area_marker_group(
}
}
// Draws a filled circular dot at a pixel position.
pub fn draw_dot(pixmap: &mut Pixmap, cx: f32, cy: f32, radius: f32, color: (u8, u8, u8, u8)) {
let mut pb = PathBuilder::new();
pb.push_circle(cx, cy, radius);
@@ -493,6 +506,7 @@ pub fn draw_dot(pixmap: &mut Pixmap, cx: f32, cy: f32, radius: f32, color: (u8,
);
}
// Draws room crosshatch.
pub fn draw_room_crosshatch(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -529,6 +543,7 @@ pub fn draw_room_crosshatch(
}
}
// Draws cell walls.
pub fn draw_cell_walls(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -611,6 +626,7 @@ pub fn draw_cell_walls(
}
}
// Draws wall segment.
pub fn draw_wall_segment(
pixmap: &mut Pixmap,
x1: f32,
@@ -647,6 +663,7 @@ pub fn draw_wall_segment(
pixmap.fill_rect(rect, &paint, Transform::identity(), None);
}
// Snaps even width.
pub fn snap_even_width(value: f32) -> f32 {
let mut w = value.round().max(1.0);
if (w as u32) % 2 == 1 {
@@ -655,6 +672,7 @@ pub fn snap_even_width(value: f32) -> f32 {
w
}
// Draws a styled door line onto the pixmap.
pub fn draw_door(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -670,6 +688,7 @@ pub fn draw_door(
draw_line(pixmap, x1, y1, x2, y2, width, color, style);
}
// Draws a styled window line onto the pixmap.
pub fn draw_window(
pixmap: &mut Pixmap,
g: &ExportGeometry,
@@ -684,6 +703,7 @@ pub fn draw_window(
draw_line(pixmap, x1, y1, x2, y2, width, color, style);
}
// Draws a solid or patterned line segment.
#[allow(clippy::too_many_arguments)]
pub fn draw_line(
pixmap: &mut Pixmap,
@@ -707,6 +727,7 @@ pub fn draw_line(
}
}
// Strokes a path segment with an optional dash pattern.
#[allow(clippy::too_many_arguments)]
pub fn stroke_path(
pixmap: &mut Pixmap,
@@ -739,6 +760,7 @@ pub fn stroke_path(
pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
}
// Handles dotted line.
pub fn dotted_line(
pixmap: &mut Pixmap,
x1: f32,
@@ -766,6 +788,7 @@ pub fn dotted_line(
}
}
// Handles raster image from pixmap.
pub fn raster_image_from_pixmap(
pixmap: &Pixmap,
settings: &UiSettings,
@@ -782,6 +805,7 @@ pub fn raster_image_from_pixmap(
Ok(img)
}
// Handles raster mask image from pixmap.
pub fn raster_mask_image_from_pixmap(
pixmap: &Pixmap,
settings: &UiSettings,
@@ -805,6 +829,7 @@ pub fn raster_mask_image_from_pixmap(
Ok(DynamicImage::ImageRgba8(out))
}
// Handles raster target size.
pub fn raster_target_size(settings: &UiSettings) -> (u32, u32) {
let width = settings.export_width.clamp(1, 10_000);
let mut height = settings.export_height.clamp(1, 10_000);
+4
View File
@@ -14,6 +14,7 @@ use crate::ui::UiSettings;
use std::collections::HashSet;
use std::fmt::Write as _;
// Builds the complete SVG document for a dungeon layout.
pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
let g = ExportGeometry::new(settings.cols, settings.rows);
let wall_w = (g.cell / 2.5).max(1.0);
@@ -253,6 +254,7 @@ pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
s
}
// Appends svg walls.
pub fn append_svg_walls(
out: &mut String,
g: &ExportGeometry,
@@ -329,6 +331,7 @@ pub fn append_svg_walls(
}
}
// Appends svg area marker.
pub fn append_svg_area_marker(
out: &mut String,
g: &ExportGeometry,
@@ -419,6 +422,7 @@ pub fn append_svg_area_marker(
}
}
// Appends svg area marker group.
pub fn append_svg_area_marker_group(
out: &mut String,
g: &ExportGeometry,
+9
View File
@@ -66,6 +66,7 @@ pub struct ExportGeometry {
}
impl ExportGeometry {
// Creates a new instance with the given inputs.
pub fn new(cols: usize, rows: usize) -> Self {
let cell = 64.0;
let pad = 0.0;
@@ -81,14 +82,17 @@ impl ExportGeometry {
}
}
// Returns the left edge of the export canvas.
pub fn left(&self) -> f32 {
self.pad
}
// Returns the top edge of the export canvas.
pub fn top(&self) -> f32 {
self.pad
}
// Returns the pixel rectangle for a grid cell.
pub fn cell_rect(&self, col: usize, row: usize) -> Option<Rect> {
Rect::from_xywh(
self.left() + col as f32 * self.cell,
@@ -98,6 +102,7 @@ impl ExportGeometry {
)
}
// Returns the pixel center point for a grid cell.
pub fn cell_center(&self, col: usize, row: usize) -> (f32, f32) {
(
self.left() + (col as f32 + 0.5) * self.cell,
@@ -147,10 +152,12 @@ pub struct CompositeExportContext<'a> {
pub door_width: f32,
}
// Normalizes a cell edge so either endpoint order compares equal.
pub fn norm_edge(a: Cell, b: Cell) -> CellEdge {
if a <= b { (a, b) } else { (b, a) }
}
// Computes edges for.
pub fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<CellEdge> {
let mut edges = Vec::new();
if door.from.0.abs_diff(door.to.0) + door.from.1.abs_diff(door.to.1) != 1 {
@@ -188,6 +195,7 @@ pub fn door_edges_for(door: &Door, cols: usize, rows: usize) -> Vec<CellEdge> {
edges
}
// Computes render width.
pub fn door_render_width(door: &Door) -> usize {
if door.span_width {
door.width.max(1)
@@ -196,6 +204,7 @@ pub fn door_render_width(door: &Door) -> usize {
}
}
// Computes render width.
pub fn window_render_width(window: &Window) -> usize {
if window.span_width {
window.width.max(1)
+5
View File
@@ -9,6 +9,7 @@ use crate::layout::{DungeonLayout, Room, Window, WindowSide, corridor_cells};
use crate::ui::UiSettings;
use std::collections::HashSet;
// Collects scene data.
pub fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> SceneData {
let mut scene = SceneData::default();
@@ -33,6 +34,7 @@ pub fn collect_scene_data(layout: &DungeonLayout, settings: &UiSettings) -> Scen
scene
}
// Computes edges from cells.
pub fn corridor_edges_from_cells(
corridor_cells: &HashSet<(usize, usize)>,
) -> HashSet<((usize, usize), (usize, usize))> {
@@ -50,6 +52,7 @@ pub fn corridor_edges_from_cells(
edges
}
// Computes edges from rooms.
pub fn room_edges_from_rooms(rooms: &[Room]) -> HashSet<((usize, usize), (usize, usize))> {
let mut edges = HashSet::new();
for room in rooms {
@@ -69,6 +72,7 @@ pub fn room_edges_from_rooms(rooms: &[Room]) -> HashSet<((usize, usize), (usize,
edges
}
// Computes line points.
pub fn door_line_points(
g: &ExportGeometry,
a: (usize, usize),
@@ -102,6 +106,7 @@ pub fn door_line_points(
}
}
// Computes line points.
pub fn window_line_points(g: &ExportGeometry, window: &Window) -> Option<(f32, f32, f32, f32)> {
let width_cells = window_render_width(window).max(1) as isize;
let min_offset = -((width_cells - 1) / 2);
+10
View File
@@ -17,6 +17,7 @@ use crate::layout::{
use crate::ui::AddTool;
use eframe::egui;
// Handles add tool.
pub fn handle_add_tool(app: &mut DungeonApp, response: &egui::Response, geometry: &GridGeometry) {
if app.settings.add_tool != AddTool::None && response.secondary_clicked() {
app.settings.add_tool = AddTool::None;
@@ -122,6 +123,7 @@ pub fn handle_add_tool(app: &mut DungeonApp, response: &egui::Response, geometry
}
}
// Deletes at pointer.
pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry: &GridGeometry) {
if app.settings.active_level_index >= app.levels.len() {
return;
@@ -236,6 +238,7 @@ pub fn delete_at_pointer(app: &mut DungeonApp, pointer_pos: egui::Pos2, geometry
}
}
// Adds room at cell.
pub fn add_room_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() {
@@ -275,6 +278,7 @@ pub fn add_room_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.refresh_doors();
}
// Adds corridor between.
pub fn add_corridor_between(app: &mut DungeonApp, start: (usize, usize), end: (usize, usize)) {
app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() {
@@ -319,6 +323,7 @@ pub fn add_corridor_between(app: &mut DungeonApp, start: (usize, usize), end: (u
app.refresh_doors();
}
// Adds text at cell.
pub fn add_text_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() {
@@ -351,6 +356,7 @@ pub fn add_text_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
});
}
// Adds marker at cell.
pub fn add_marker_at_cell(app: &mut DungeonApp, cell: (usize, usize), kind: MarkerKind) {
app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() {
@@ -374,6 +380,7 @@ pub fn add_marker_at_cell(app: &mut DungeonApp, cell: (usize, usize), kind: Mark
}
}
// Adds staircase at cell.
pub fn add_staircase_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
app.push_undo_snapshot();
if app.settings.active_level_index >= app.levels.len() {
@@ -416,6 +423,7 @@ pub fn add_staircase_at_cell(app: &mut DungeonApp, cell: (usize, usize)) {
}
}
// Deletes a room and removes dependent generated elements.
pub fn delete_room(app: &mut DungeonApp, room_idx: usize) {
if app.settings.active_level_index >= app.levels.len() {
return;
@@ -441,6 +449,7 @@ pub fn delete_room(app: &mut DungeonApp, room_idx: usize) {
app.refresh_doors();
}
// Adds door at pointer.
pub fn add_door_at_pointer(
app: &mut DungeonApp,
pointer_pos: egui::Pos2,
@@ -525,6 +534,7 @@ pub fn add_door_at_pointer(
app.suppressed_auto_door_edges.remove(&target);
}
// Checks whether manual door edge allowed.
pub fn manual_door_edge_allowed(
a_room: Option<usize>,
b_room: Option<usize>,
+7
View File
@@ -14,6 +14,7 @@ use crate::layout::{blocked_room_cells, shortest_path_cells};
use crate::ui::AddTool;
use eframe::egui;
// Handles active drag gestures for rooms, corridors, markers, stairs, and text.
pub fn handle_drag(
app: &mut DungeonApp,
ctx: &egui::Context,
@@ -80,6 +81,7 @@ pub fn handle_drag(
}
}
// Drags room to pointer.
pub fn drag_room_to_pointer(
app: &mut DungeonApp,
drag: RoomDragState,
@@ -121,6 +123,7 @@ pub fn drag_room_to_pointer(
app.refresh_doors();
}
// Drags text to pointer.
pub fn drag_text_to_pointer(
app: &mut DungeonApp,
drag: TextDragState,
@@ -152,6 +155,7 @@ pub fn drag_text_to_pointer(
app.levels[app.settings.active_level_index].text_labels[drag.text_idx].cell = target;
}
// Reroutes corridors for room.
pub fn reroute_corridors_for_room(app: &mut DungeonApp, room_idx: usize) {
if app.settings.active_level_index >= app.levels.len() {
return;
@@ -181,6 +185,7 @@ pub fn reroute_corridors_for_room(app: &mut DungeonApp, room_idx: usize) {
}
}
// Drags corridor to pointer.
pub fn drag_corridor_to_pointer(
app: &mut DungeonApp,
drag: CorridorDragState,
@@ -225,6 +230,7 @@ pub fn drag_corridor_to_pointer(
app.refresh_doors();
}
// Drags marker to pointer.
pub fn drag_marker_to_pointer(
app: &mut DungeonApp,
drag: MarkerDragState,
@@ -258,6 +264,7 @@ pub fn drag_marker_to_pointer(
marker.cell = (target_x.min(max_x), target_y.min(max_y));
}
// Drags stair to pointer.
pub fn drag_stair_to_pointer(
app: &mut DungeonApp,
drag: StaircaseDragState,
+32
View File
@@ -28,6 +28,7 @@ pub struct GridGeometry {
pub rows: usize,
}
// Draws the editable grid and returns its screen geometry.
pub fn draw_grid(
painter: &egui::Painter,
area: egui::Rect,
@@ -82,6 +83,7 @@ pub fn draw_grid(
}
}
// Handles update hover targets.
pub fn update_hover_targets(app: &mut DungeonApp, ctx: &egui::Context, geometry: &GridGeometry) {
if app.drag_state.is_some() || app.resize_state.is_some() || app.add_corridor_drag.is_some() {
return;
@@ -158,6 +160,7 @@ pub fn update_hover_targets(app: &mut DungeonApp, ctx: &egui::Context, geometry:
}
}
// Computes edge at pointer.
pub fn door_edge_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -210,6 +213,7 @@ pub fn door_edge_at_pointer(
Some(((ax as usize, ay as usize), (bx as usize, by as usize)))
}
// Computes at pointer.
pub fn room_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -236,6 +240,7 @@ pub fn room_at_pointer(
None
}
// Computes at pointer.
pub fn text_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -254,6 +259,7 @@ pub fn text_at_pointer(
.position(|label| label.cell == clicked)
}
// Computes at pointer.
pub fn marker_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -298,6 +304,7 @@ pub fn marker_at_pointer(
})
}
// Computes drag at pointer.
pub fn marker_drag_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -349,6 +356,7 @@ pub fn marker_drag_at_pointer(
}
}
// Handles stair at pointer.
pub fn stair_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -369,6 +377,7 @@ pub fn stair_at_pointer(
})
}
// Handles stair drag at pointer.
pub fn stair_drag_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -390,6 +399,7 @@ pub fn stair_drag_at_pointer(
})
}
// Resizes room at pointer.
pub fn resize_room_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -421,6 +431,7 @@ pub fn resize_room_at_pointer(
None
}
// Computes drag at pointer.
pub fn corridor_drag_at_pointer(
app: &DungeonApp,
pointer_pos: egui::Pos2,
@@ -445,6 +456,7 @@ pub fn corridor_drag_at_pointer(
})
}
// Converts to grid.
pub fn pointer_to_grid(pointer_pos: egui::Pos2, geometry: &GridGeometry) -> Option<(f32, f32)> {
if !geometry.rect.contains(pointer_pos) {
return None;
@@ -455,6 +467,7 @@ pub fn pointer_to_grid(pointer_pos: egui::Pos2, geometry: &GridGeometry) -> Opti
Some((x, y))
}
// Reroutes path through cell.
pub fn reroute_path_through_cell(
original_path: &[(usize, usize)],
original_cell: (usize, usize),
@@ -512,6 +525,7 @@ pub fn reroute_path_through_cell(
Some(full_path)
}
// Simplifies path loops.
pub fn simplify_path_loops(path: &mut Vec<(usize, usize)>) {
let mut out = Vec::new();
for &cell in path.iter() {
@@ -524,6 +538,7 @@ pub fn simplify_path_loops(path: &mut Vec<(usize, usize)>) {
*path = out;
}
// Resizes hit cells.
pub fn resize_hit_cells(
room: &layout::Room,
room_idx: usize,
@@ -587,6 +602,7 @@ pub fn resize_hit_cells(
cells
}
// Computes edges for.
pub fn door_edges_for(
door: &layout::Door,
cols: usize,
@@ -628,6 +644,7 @@ pub fn door_edges_for(
edges
}
// Computes render width.
pub fn door_render_width(door: &layout::Door) -> usize {
if door.span_width {
door.width.max(1)
@@ -636,6 +653,7 @@ pub fn door_render_width(door: &layout::Door) -> usize {
}
}
// Computes render width.
pub fn window_render_width(window: &layout::Window) -> usize {
if window.span_width {
window.width.max(1)
@@ -644,10 +662,12 @@ pub fn window_render_width(window: &layout::Window) -> usize {
}
}
// Normalizes a cell edge so either endpoint order compares equal.
pub fn normalized_edge(a: (usize, usize), b: (usize, usize)) -> ((usize, usize), (usize, usize)) {
if a <= b { (a, b) } else { (b, a) }
}
// Returns the screen center point for a grid cell.
pub fn cell_center(geometry: &GridGeometry, col: usize, row: usize) -> egui::Pos2 {
egui::pos2(
geometry.rect.left() + (col as f32 + 0.5) * geometry.cell_size,
@@ -655,6 +675,7 @@ pub fn cell_center(geometry: &GridGeometry, col: usize, row: usize) -> egui::Pos
)
}
// Returns the screen rectangle for a grid cell.
pub fn cell_rect(geometry: &GridGeometry, col: usize, row: usize) -> egui::Rect {
let left = geometry.rect.left() + col as f32 * geometry.cell_size;
let top = geometry.rect.top() + row as f32 * geometry.cell_size;
@@ -664,6 +685,7 @@ pub fn cell_rect(geometry: &GridGeometry, col: usize, row: usize) -> egui::Rect
)
}
// Computes the screen rectangle occupied by an area marker.
pub fn marker_rect(marker: &layout::AreaMarker, geometry: &GridGeometry) -> egui::Rect {
let left = geometry.rect.left() + marker.cell.0 as f32 * geometry.cell_size;
let top = geometry.rect.top() + marker.cell.1 as f32 * geometry.cell_size;
@@ -676,6 +698,7 @@ pub fn marker_rect(marker: &layout::AreaMarker, geometry: &GridGeometry) -> egui
)
}
// Computes contains cell.
pub fn marker_contains_cell(marker: Option<&layout::AreaMarker>, cell: (usize, usize)) -> bool {
let Some(marker) = marker else {
return false;
@@ -686,6 +709,7 @@ pub fn marker_contains_cell(marker: Option<&layout::AreaMarker>, cell: (usize, u
&& cell.1 < marker.cell.1 + marker.size
}
// Computes marker origin.
pub fn centered_marker_origin(
cell: (usize, usize),
size: usize,
@@ -702,6 +726,7 @@ pub fn centered_marker_origin(
)
}
// Draws add overlay.
pub fn draw_add_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
if app.settings.add_tool != AddTool::Corridor {
return;
@@ -717,6 +742,7 @@ pub fn draw_add_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &Gr
crate::rendering::draw_dashed_line(painter, start, end, stroke, 8.0, 6.0);
}
// Draws add tool ghost.
pub fn draw_add_tool_ghost(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
let tool = app.settings.add_tool;
if tool == AddTool::None || tool == AddTool::Corridor {
@@ -915,6 +941,7 @@ pub fn draw_add_tool_ghost(app: &DungeonApp, painter: &egui::Painter, geometry:
}
}
// Draws resize overlay.
pub fn draw_resize_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
if app.settings.active_level_index >= app.levels.len() {
return;
@@ -969,6 +996,7 @@ pub fn draw_resize_overlay(app: &DungeonApp, painter: &egui::Painter, geometry:
}
}
// Draws room resize visuals.
pub fn draw_room_resize_visuals(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -1013,6 +1041,7 @@ pub fn draw_room_resize_visuals(
}
}
// Draws staircase resize visuals.
pub fn draw_staircase_resize_visuals(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -1045,6 +1074,7 @@ pub fn draw_staircase_resize_visuals(
}
}
// Draws corridor hover overlay.
pub fn draw_corridor_hover_overlay(
app: &DungeonApp,
painter: &egui::Painter,
@@ -1071,6 +1101,7 @@ pub fn draw_corridor_hover_overlay(
}
}
// Draws door hover overlay.
pub fn draw_door_hover_overlay(app: &DungeonApp, painter: &egui::Painter, geometry: &GridGeometry) {
let Some(idx) = app.hover_door_idx else {
return;
@@ -1096,6 +1127,7 @@ pub fn draw_door_hover_overlay(app: &DungeonApp, painter: &egui::Painter, geomet
);
}
// Draws resize handle.
pub fn draw_resize_handle(
painter: &egui::Painter,
corner: egui::Pos2,
+7
View File
@@ -13,6 +13,7 @@ use crate::layout::{rects_overlap, rooms_overlap};
use crate::ui::AddTool;
use eframe::egui;
// Handles secondary-button resize gestures for resizable layout elements.
pub fn handle_resize(
app: &mut DungeonApp,
ctx: &egui::Context,
@@ -71,6 +72,7 @@ pub fn handle_resize(
false
}
// Starts text resize.
pub fn start_text_resize(app: &DungeonApp, text_idx: usize) -> Option<TextResizeState> {
if app.settings.active_level_index >= app.levels.len() {
return None;
@@ -85,6 +87,7 @@ pub fn start_text_resize(app: &DungeonApp, text_idx: usize) -> Option<TextResize
})
}
// Starts a room resize from a pointer position.
pub fn start_resize(
app: &DungeonApp,
room_idx: usize,
@@ -117,6 +120,7 @@ pub fn start_resize(
})
}
// Starts staircase resize.
pub fn start_staircase_resize(
app: &DungeonApp,
stair_idx: usize,
@@ -149,6 +153,7 @@ pub fn start_staircase_resize(
})
}
// Resizes a room and reroutes affected corridors.
pub fn resize_room(app: &mut DungeonApp, state: &RoomResizeState, target_cell: (usize, usize)) {
if app.settings.active_level_index >= app.levels.len() {
return;
@@ -207,6 +212,7 @@ pub fn resize_room(app: &mut DungeonApp, state: &RoomResizeState, target_cell: (
}
}
// Resizes a text label based on the drag target cell.
pub fn resize_text(app: &mut DungeonApp, state: &TextResizeState, target_cell: (usize, usize)) {
if app.settings.active_level_index >= app.levels.len() {
return;
@@ -225,6 +231,7 @@ pub fn resize_text(app: &mut DungeonApp, state: &TextResizeState, target_cell: (
label.font_size = new_size as u16;
}
// Resizes a staircase while clamping it to the grid.
pub fn resize_staircase(
app: &mut DungeonApp,
state: &StaircaseResizeState,
+6
View File
@@ -14,6 +14,7 @@ pub type Cell = (usize, usize);
pub type CellEdge = (Cell, Cell);
pub type RoomBoundary = (usize, usize, Vec<CellEdge>);
// Builds room connection edges.
pub fn build_room_connection_edges(
centers: &[(usize, usize)],
randomness: f32,
@@ -73,6 +74,7 @@ pub fn build_room_connection_edges(
edges
}
// Orders core rooms.
pub fn ordered_core_rooms(
core_rooms: &[usize],
centers: &[(usize, usize)],
@@ -107,6 +109,7 @@ pub fn ordered_core_rooms(
ordered
}
// Pushes unique room edge.
pub fn push_unique_room_edge(
a: usize,
b: usize,
@@ -122,6 +125,7 @@ pub fn push_unique_room_edge(
}
}
// Computes exit edge.
pub fn room_exit_edge(
path: &[(usize, usize)],
room: &Room,
@@ -151,6 +155,7 @@ pub fn room_exit_edge(
None
}
// Computes collision edges.
pub fn room_collision_edges(
path: &[(usize, usize)],
room: &Room,
@@ -180,6 +185,7 @@ pub fn room_collision_edges(
edges
}
// Finds room boundaries.
pub fn shared_room_boundaries(rooms: &[Room]) -> Vec<RoomBoundary> {
let mut boundaries = Vec::new();
for a_idx in 0..rooms.len() {
+24
View File
@@ -10,6 +10,7 @@ use crate::seed;
use crate::ui::UiSettings;
use std::collections::HashSet;
// Populates random markers.
pub fn populate_random_markers(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout {
layout.start_markers.clear();
layout.end_markers.clear();
@@ -86,6 +87,7 @@ pub fn populate_random_markers(mut layout: DungeonLayout, settings: &UiSettings)
layout
}
// Generates range inclusive.
pub fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize {
let min = min.max(1);
let max = max.max(min);
@@ -93,6 +95,7 @@ pub fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize
min + (seed_value as usize % span)
}
// Computes in room.
pub fn marker_in_room(
room: &Room,
min_size: usize,
@@ -120,6 +123,7 @@ pub fn marker_in_room(
AreaMarker { cell: (x, y), size }
}
// Handles assign extra markers.
#[allow(clippy::too_many_arguments)]
pub fn assign_extra_markers(
markers: &mut Vec<AreaMarker>,
@@ -149,6 +153,7 @@ pub fn assign_extra_markers(
}
}
// Finds room pair.
pub fn farthest_room_pair(
rooms: &[Room],
available_start_rooms: &[usize],
@@ -182,6 +187,7 @@ pub fn farthest_room_pair(
best
}
// Computes distance sq.
pub fn room_distance_sq(a: &Room, b: &Room) -> usize {
let ac = a.center_cell();
let bc = b.center_cell();
@@ -190,12 +196,14 @@ pub fn room_distance_sq(a: &Room, b: &Room) -> usize {
dx * dx + dy * dy
}
// Removes a room id from the available marker placement list.
pub fn consume_room(available_rooms: &mut Vec<usize>, room_idx: usize) {
if let Some(pos) = available_rooms.iter().position(|&idx| idx == room_idx) {
available_rooms.remove(pos);
}
}
// Picks room index.
pub fn pick_room_index(available_rooms: &[usize], room_count: usize, seed_value: u64) -> usize {
if !available_rooms.is_empty() {
available_rooms[seed_value as usize % available_rooms.len()]
@@ -204,6 +212,7 @@ pub fn pick_room_index(available_rooms: &[usize], room_count: usize, seed_value:
}
}
// Populates random traps.
pub fn populate_random_traps(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout {
layout.trap_markers = populate_random_area_markers(
&layout,
@@ -217,6 +226,7 @@ pub fn populate_random_traps(mut layout: DungeonLayout, settings: &UiSettings) -
layout
}
// Populates random monsters.
pub fn populate_random_monsters(mut layout: DungeonLayout, settings: &UiSettings) -> DungeonLayout {
layout.monster_markers = populate_random_area_markers(
&layout,
@@ -230,6 +240,7 @@ pub fn populate_random_monsters(mut layout: DungeonLayout, settings: &UiSettings
layout
}
// Populates random area markers.
pub fn populate_random_area_markers(
layout: &DungeonLayout,
master_seed: u64,
@@ -272,6 +283,7 @@ pub fn populate_random_area_markers(
markers
}
// Appends room markers.
pub fn append_room_markers(
markers: &mut Vec<AreaMarker>,
room: &Room,
@@ -295,6 +307,7 @@ pub fn append_room_markers(
}
}
// Appends corridor markers.
pub fn append_corridor_markers(
markers: &mut Vec<AreaMarker>,
corridor: &Corridor,
@@ -317,10 +330,12 @@ pub fn append_corridor_markers(
}
}
// Checks whether passes frequency roll.
pub fn passes_frequency_roll(area_seed: u64, frequency_percent: usize) -> bool {
(area_seed % 100) < frequency_percent as u64
}
// Picks random corridor cell.
pub fn pick_random_corridor_cell(corridor: &Corridor, seed_value: u64) -> Option<(usize, usize)> {
if corridor.path.is_empty() {
return None;
@@ -329,6 +344,7 @@ pub fn pick_random_corridor_cell(corridor: &Corridor, seed_value: u64) -> Option
Some(corridor.path[idx])
}
// Populates staircase transitions between generated levels.
pub fn populate_stairs(
mut layouts: Vec<DungeonLayout>,
settings: &UiSettings,
@@ -396,6 +412,7 @@ pub fn populate_stairs(
result
}
// Gets stair count.
pub fn get_stair_count(settings: &UiSettings, gap_idx: usize) -> usize {
if settings.min_stairs_per_level == settings.max_stairs_per_level {
settings.min_stairs_per_level
@@ -406,6 +423,7 @@ pub fn get_stair_count(settings: &UiSettings, gap_idx: usize) -> usize {
}
}
// Ensures stair in room.
pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &Staircase) -> bool {
let stair_x = stair.cell.0;
let stair_y = stair.cell.1;
@@ -458,6 +476,7 @@ pub fn ensure_stair_in_room(layout: &mut DungeonLayout, stair: &Staircase) -> bo
}
}
// Computes to stair min dist.
pub fn room_to_stair_min_dist(room: &Room, stair: &Staircase) -> usize {
let dx = if stair.cell.0 + stair.width <= room.x {
room.x - (stair.cell.0 + stair.width)
@@ -474,6 +493,7 @@ pub fn room_to_stair_min_dist(room: &Room, stair: &Staircase) -> usize {
dx + dy
}
// Picks stair positions.
pub fn pick_stair_positions(
layout: &DungeonLayout,
settings: &UiSettings,
@@ -548,6 +568,7 @@ pub fn pick_stair_positions(
.collect()
}
// Checks whether has start or end on bottom row.
pub fn has_start_or_end_on_bottom_row(
room: &Room,
start_markers: &[AreaMarker],
@@ -563,6 +584,7 @@ pub fn has_start_or_end_on_bottom_row(
false
}
// Generates stair size.
pub fn random_stair_size(settings: &UiSettings, seed_value: u64) -> (usize, usize) {
let w_span = settings
.max_stair_width
@@ -590,10 +612,12 @@ pub struct StairShuffleRng {
}
impl StairShuffleRng {
// Creates a new instance with the given inputs.
pub fn new(seed: u64) -> Self {
Self { state: seed }
}
// Generates the next pseudo-random 32-bit value.
pub fn next_u32(&mut self) -> u32 {
self.state = self
.state
+1
View File
@@ -18,6 +18,7 @@ pub use markers::*;
pub use passages::*;
pub use room_placement::*;
// Generates a complete dungeon layout from generation settings.
#[allow(clippy::too_many_arguments)]
pub fn generate_layout(
cols: usize,
+15
View File
@@ -14,6 +14,7 @@ use super::connections::{room_collision_edges, room_exit_edge, shared_room_bound
use crate::seed;
use std::collections::HashSet;
// Applies generated doors and archways to room-corridor transitions.
pub fn apply_doors(
layout: &mut DungeonLayout,
seed: u64,
@@ -139,6 +140,7 @@ pub fn apply_doors(
apply_secret_doors(layout, seed, settings);
}
// Applies packed room doors.
pub fn apply_packed_room_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) {
let mut rng = SimpleRng::new(seed::derive_seed(seed, 0xD005_5EED_u64));
let door_chance = ((settings.frequency_percent.min(100) as f32) / 100.0)
@@ -175,6 +177,7 @@ pub fn apply_packed_room_doors(layout: &mut DungeonLayout, seed: u64, settings:
apply_secret_doors(layout, seed, settings);
}
// Applies secret doors.
pub fn apply_secret_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorSettings) {
let secret_percent = settings.secret_percent.min(100);
let chance = (secret_percent as f32) / 100.0;
@@ -206,6 +209,7 @@ pub fn apply_secret_doors(layout: &mut DungeonLayout, seed: u64, settings: DoorS
}
}
// Computes is secret eligible for rooms.
pub fn door_is_secret_eligible_for_rooms(
layout: &DungeonLayout,
room_door_map: &[Vec<usize>],
@@ -230,6 +234,7 @@ pub fn door_is_secret_eligible_for_rooms(
true
}
// Computes to door indices.
pub fn room_to_door_indices(layout: &DungeonLayout) -> Vec<Vec<usize>> {
let mut room_door_map = vec![Vec::new(); layout.rooms.len()];
for (door_idx, door) in layout.doors.iter().enumerate() {
@@ -242,6 +247,7 @@ pub fn room_to_door_indices(layout: &DungeonLayout) -> Vec<Vec<usize>> {
room_door_map
}
// Handles secret room ids.
pub fn secret_room_ids(layout: &DungeonLayout) -> HashSet<usize> {
let mut secret_rooms = HashSet::new();
for door in layout.doors.iter().filter(|door| door.secret) {
@@ -254,10 +260,12 @@ pub fn secret_room_ids(layout: &DungeonLayout) -> HashSet<usize> {
secret_rooms
}
// Computes touches room.
pub fn door_touches_room(door: &Door, room: &Room) -> bool {
cell_in_room(door.from, room) || cell_in_room(door.to, room)
}
// Computes directly connects rooms.
pub fn door_directly_connects_rooms(layout: &DungeonLayout, door_idx: usize) -> bool {
let Some(door) = layout.doors.get(door_idx) else {
return false;
@@ -273,6 +281,7 @@ pub fn door_directly_connects_rooms(layout: &DungeonLayout, door_idx: usize) ->
touching_rooms.len() >= 2
}
// Builds in room.
pub fn cell_in_room(cell: (usize, usize), room: &Room) -> bool {
cell.0 >= room.x
&& cell.0 < room.x + room.width
@@ -280,6 +289,7 @@ pub fn cell_in_room(cell: (usize, usize), room: &Room) -> bool {
&& cell.1 < room.y + room.height
}
// Applies generated windows to eligible room boundaries.
pub fn apply_windows(
layout: &mut DungeonLayout,
seed: u64,
@@ -341,6 +351,7 @@ pub enum WindowTarget {
Room(usize),
}
// Collects window segments.
pub fn collect_window_segments(
layout: &DungeonLayout,
cols: usize,
@@ -404,6 +415,7 @@ pub fn collect_window_segments(
segments
}
// Collects room side segments.
#[allow(clippy::too_many_arguments)]
pub fn collect_room_side_segments(
room_idx: usize,
@@ -467,6 +479,7 @@ pub fn collect_room_side_segments(
push_window_segment(current_target, side, &mut current_cells, segments);
}
// Pushes window segment.
pub fn push_window_segment(
target: Option<WindowTarget>,
side: WindowSide,
@@ -488,6 +501,7 @@ pub fn push_window_segment(
});
}
// Finds the grid cell outside a room side.
pub fn outward_neighbor(
cell: (usize, usize),
side: WindowSide,
@@ -502,6 +516,7 @@ pub fn outward_neighbor(
}
}
// Selects segment cell.
pub fn select_segment_cell(
cells: &[(usize, usize)],
width: usize,
+7
View File
@@ -8,6 +8,7 @@ use super::super::types::Room;
use super::super::utils::{SimpleRng, rooms_overlap, rooms_touch, shuffle_indices};
use std::collections::VecDeque;
// Generates room sizes.
pub fn generate_room_sizes(
target_room_count: usize,
cols: usize,
@@ -48,6 +49,7 @@ pub fn generate_room_sizes(
sizes
}
// Generates random room center points inside the grid.
pub fn random_centers(
count: usize,
cols: usize,
@@ -64,6 +66,7 @@ pub fn random_centers(
centers
}
// Places packed rooms.
pub fn place_packed_rooms(
room_sizes: &[(usize, usize)],
room_edges: &[(usize, usize)],
@@ -116,6 +119,7 @@ pub fn place_packed_rooms(
placed.into_iter().flatten().collect()
}
// Computes the order used to place packed rooms.
pub fn placement_order(room_count: usize, room_edges: &[(usize, usize)]) -> Vec<usize> {
if room_count == 0 {
return Vec::new();
@@ -152,6 +156,7 @@ pub fn placement_order(room_count: usize, room_edges: &[(usize, usize)]) -> Vec<
order
}
// Tries to place packed room.
pub fn try_place_packed_room(
room_idx: usize,
room_sizes: &[(usize, usize)],
@@ -214,6 +219,7 @@ pub fn try_place_packed_room(
None
}
// Builds room candidates.
pub fn packed_room_candidates(
anchor: &Room,
width: usize,
@@ -261,6 +267,7 @@ pub fn packed_room_candidates(
candidates
}
// Shuffles rooms with the deterministic layout RNG.
pub fn shuffle_rooms(rooms: &mut [Room], rng: &mut SimpleRng) {
if rooms.len() <= 1 {
return;
+6
View File
@@ -181,6 +181,7 @@ pub fn corridor_cells(layout: &DungeonLayout, cols: usize, rows: usize) -> HashS
cells
}
// Computes index at cell.
pub fn room_index_at_cell(rooms: &[Room], cell: (usize, usize)) -> Option<usize> {
rooms.iter().position(|room| {
cell.0 >= room.x
@@ -218,6 +219,7 @@ pub fn overlaps_with_padding(a: &Room, b: &Room, padding: usize) -> bool {
a_left < b_right && a_right > b_left && a_top < b_bottom && a_bottom > b_top
}
// Checks whether rects overlap.
#[allow(clippy::too_many_arguments)]
pub fn rects_overlap(
ax: usize,
@@ -237,14 +239,17 @@ pub fn rects_overlap(
ax < b_right && a_right > bx && ay < b_bottom && a_bottom > by
}
// Checks whether rooms overlap.
pub 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)
}
// Checks whether rooms touch.
pub fn rooms_touch(a: &Room, b: &Room) -> bool {
!shared_boundary_edges(a, b).is_empty()
}
// Finds boundary edges.
pub fn shared_boundary_edges(a: &Room, b: &Room) -> Vec<((usize, usize), (usize, usize))> {
let mut edges = Vec::new();
@@ -277,6 +282,7 @@ pub fn shared_boundary_edges(a: &Room, b: &Room) -> Vec<((usize, usize), (usize,
edges
}
// Finds opening width.
pub fn shared_opening_width(a: &Room, b: &Room, span: usize, default_width: usize) -> usize {
let max_width = if a.x + a.width == b.x || b.x + b.width == a.x {
a.height.min(b.height)
+16
View File
@@ -13,6 +13,7 @@ use eframe::egui;
use egui::{Color32, Stroke};
use std::collections::HashSet;
// Draws the full dungeon layout into the interactive canvas.
pub fn draw_layout(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -325,6 +326,7 @@ pub fn draw_layout(
}
}
// Draws a staircase marker into the interactive canvas.
pub fn draw_staircase(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -380,6 +382,7 @@ pub fn draw_staircase(
}
}
// Computes label rect.
pub fn text_label_rect(geometry: &GridGeometry, label: &crate::layout::TextLabel) -> egui::Rect {
let center = cell_center(geometry, label.cell.0, label.cell.1);
let font_size = label.font_size as f32;
@@ -389,6 +392,7 @@ pub fn text_label_rect(geometry: &GridGeometry, label: &crate::layout::TextLabel
egui::Rect::from_center_size(center, egui::vec2(width, height))
}
// Draws area marker.
pub fn draw_area_marker(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -430,6 +434,7 @@ pub fn draw_area_marker(
);
}
// Draws area marker group.
#[allow(clippy::too_many_arguments)]
pub fn draw_area_marker_group(
painter: &egui::Painter,
@@ -461,6 +466,7 @@ pub fn draw_area_marker_group(
}
}
// Draws wall segment.
pub fn draw_wall_segment(
painter: &egui::Painter,
from: egui::Pos2,
@@ -490,6 +496,7 @@ pub fn draw_wall_segment(
}
}
// Computes edges from cells.
pub fn corridor_edges_from_cells(
corridor_cells: &HashSet<(usize, usize)>,
) -> HashSet<((usize, usize), (usize, usize))> {
@@ -507,6 +514,7 @@ pub fn corridor_edges_from_cells(
edges
}
// Computes edges from rooms.
pub fn room_edges_from_rooms(
rooms: &[crate::layout::Room],
) -> HashSet<((usize, usize), (usize, usize))> {
@@ -538,6 +546,7 @@ pub enum DoorLineStyle {
DashDotDot,
}
// Draws door line.
pub fn draw_door_line(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -574,6 +583,7 @@ pub fn draw_door_line(
}
}
// Draws window line.
pub fn draw_window_line(
painter: &egui::Painter,
geometry: &GridGeometry,
@@ -625,6 +635,7 @@ pub fn draw_window_line(
}
}
// Draws styled line.
pub fn draw_styled_line(
painter: &egui::Painter,
from: egui::Pos2,
@@ -644,6 +655,7 @@ pub fn draw_styled_line(
}
}
// Draws dashed line.
pub fn draw_dashed_line(
painter: &egui::Painter,
from: egui::Pos2,
@@ -672,6 +684,7 @@ pub fn draw_dashed_line(
}
}
// Draws dotted line.
pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos2, stroke: Stroke) {
let dx = to.x - from.x;
let dy = to.y - from.y;
@@ -692,6 +705,7 @@ pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos
}
}
// Draws dash dot line.
pub fn draw_dash_dot_line(
painter: &egui::Painter,
from: egui::Pos2,
@@ -723,6 +737,7 @@ pub fn draw_dash_dot_line(
}
}
// Draws dash dot dot line.
pub fn draw_dash_dot_dot_line(
painter: &egui::Painter,
from: egui::Pos2,
@@ -761,6 +776,7 @@ pub fn draw_dash_dot_dot_line(
}
}
// Draws room crosshatch.
pub fn draw_room_crosshatch(
painter: &egui::Painter,
geometry: &GridGeometry,
+2
View File
@@ -19,6 +19,7 @@ pub struct DungeonSave {
pub svgs: Vec<String>,
}
// Saves dungeon state to a user-selected JSON file.
pub fn save_dungeon(
settings: &UiSettings,
layouts: Vec<DungeonLayout>,
@@ -45,6 +46,7 @@ pub fn save_dungeon(
Ok(path)
}
// Loads dungeon state from a user-selected JSON file.
pub fn load_dungeon() -> Result<DungeonSave, String> {
let path = FileDialog::new()
.set_title("Load Dungeon State")
+12
View File
@@ -19,6 +19,7 @@ pub enum Tab {
}
impl Default for Tab {
// Provides default settings for this type.
fn default() -> Self {
Self::Generate
}
@@ -34,12 +35,14 @@ pub enum ExportFormat {
}
impl Default for ExportFormat {
// Provides default settings for this type.
fn default() -> Self {
Self::Png
}
}
impl ExportFormat {
// Returns the file extension for this format.
pub fn extension(self) -> &'static str {
match self {
ExportFormat::Png => "png",
@@ -50,6 +53,7 @@ impl ExportFormat {
}
}
// Returns the user-facing label for this option.
pub fn label(self) -> &'static str {
match self {
ExportFormat::Png => ".png",
@@ -60,6 +64,7 @@ impl ExportFormat {
}
}
// Reports whether this format supports resolution scaling.
pub fn supports_resolution(self) -> bool {
!matches!(self, ExportFormat::Svg)
}
@@ -73,12 +78,14 @@ pub enum MaskFormat {
}
impl Default for MaskFormat {
// Provides default settings for this type.
fn default() -> Self {
Self::Png
}
}
impl MaskFormat {
// Returns the file extension for this format.
pub fn extension(self) -> &'static str {
match self {
MaskFormat::Png => "png",
@@ -87,6 +94,7 @@ impl MaskFormat {
}
}
// Returns the user-facing label for this option.
pub fn label(self) -> &'static str {
match self {
MaskFormat::Png => ".png",
@@ -169,6 +177,7 @@ pub struct UiSettings {
}
impl Default for UiSettings {
// Provides default settings for this type.
fn default() -> Self {
UiSettings {
seed: 0,
@@ -270,6 +279,7 @@ pub struct SidePanelResult {
pub new_level_clicked: bool,
}
// Draws side panel.
pub fn draw_side_panel(
ctx: &egui::Context,
settings: &mut UiSettings,
@@ -343,6 +353,7 @@ pub fn draw_side_panel(
result
}
// Draws legend panel.
pub fn draw_legend_panel(ctx: &egui::Context, settings: &mut UiSettings) -> bool {
let mut composition_mode_changed = false;
use egui::Color32;
@@ -418,6 +429,7 @@ pub fn draw_legend_panel(ctx: &egui::Context, settings: &mut UiSettings) -> bool
composition_mode_changed
}
// Draws level tabs.
pub fn draw_level_tabs(
ui: &mut egui::Ui,
level_count: usize,
+3
View File
@@ -787,6 +787,7 @@ pub fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings, result: &mut S
}
}
// Draws start and end tab.
pub fn draw_start_and_end_tab(
ui: &mut egui::Ui,
settings: &mut UiSettings,
@@ -869,6 +870,7 @@ pub fn draw_start_and_end_tab(
ui.label("Markers are generated inside rooms. Matching start/end indices are paired as far apart as possible.");
}
// Draws monsters and traps tab.
pub fn draw_monsters_and_traps_tab(
ui: &mut egui::Ui,
settings: &mut UiSettings,
@@ -932,6 +934,7 @@ pub fn draw_monsters_and_traps_tab(
);
}
// Draws usize slider row.
pub fn draw_usize_slider_row(
ui: &mut egui::Ui,
result: &mut SidePanelResult,
+2
View File
@@ -218,6 +218,7 @@ pub fn draw_dotted_line(painter: &egui::Painter, from: egui::Pos2, to: egui::Pos
}
}
// Draws dash dot line.
pub fn draw_dash_dot_line(
painter: &egui::Painter,
from: egui::Pos2,
@@ -249,6 +250,7 @@ pub fn draw_dash_dot_line(
}
}
// Draws dash dot dot line.
pub fn draw_dash_dot_dot_line(
painter: &egui::Painter,
from: egui::Pos2,