massive commenting pass to explain random functions and their purpose
This commit is contained in:
+22
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user