added basic laod save functionality

This commit is contained in:
grimsace
2026-04-20 10:23:49 -05:00
parent ca6e89dc45
commit c82c1f2b20
5 changed files with 97 additions and 11 deletions
+1 -1
View File
@@ -529,7 +529,7 @@ fn raster_target_size(settings: &UiSettings) -> (u32, u32) {
} }
// Build an SVG document for the current layout. // Build an SVG document for the current layout.
fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String { pub fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
let g = ExportGeometry::new(settings.cols, settings.rows); let g = ExportGeometry::new(settings.cols, settings.rows);
let wall_w = (g.cell / 5.0).max(1.0); let wall_w = (g.cell / 5.0).max(1.0);
let door_w = (g.cell / 10.0).max(1.0); let door_w = (g.cell / 10.0).max(1.0);
+11 -10
View File
@@ -1,8 +1,9 @@
use serde::{Deserialize, Serialize};
use std::collections::{HashSet, VecDeque}; use std::collections::{HashSet, VecDeque};
use crate::seed; use crate::seed;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Room { pub struct Room {
pub x: usize, pub x: usize,
pub y: usize, pub y: usize,
@@ -17,7 +18,7 @@ impl Room {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Corridor { pub struct Corridor {
#[allow(dead_code)] #[allow(dead_code)]
pub id: u64, pub id: u64,
@@ -27,7 +28,7 @@ pub struct Corridor {
pub width: usize, pub width: usize,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Door { pub struct Door {
pub from: (usize, usize), pub from: (usize, usize),
pub to: (usize, usize), pub to: (usize, usize),
@@ -39,7 +40,7 @@ pub struct Door {
pub manual: bool, pub manual: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Window { pub struct Window {
pub cell: (usize, usize), pub cell: (usize, usize),
pub side: WindowSide, pub side: WindowSide,
@@ -47,20 +48,20 @@ pub struct Window {
pub span_width: bool, pub span_width: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TextLabel { pub struct TextLabel {
pub cell: (usize, usize), pub cell: (usize, usize),
pub text: String, pub text: String,
pub font_size: u16, pub font_size: u16,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AreaMarker { pub struct AreaMarker {
pub cell: (usize, usize), pub cell: (usize, usize),
pub size: usize, pub size: usize,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WindowSide { pub enum WindowSide {
Left, Left,
Right, Right,
@@ -68,7 +69,7 @@ pub enum WindowSide {
Bottom, Bottom,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct DoorSettings { pub struct DoorSettings {
pub frequency_percent: usize, pub frequency_percent: usize,
pub room_hallway_percent: usize, pub room_hallway_percent: usize,
@@ -77,7 +78,7 @@ pub struct DoorSettings {
pub allow_middle_corridor_doors: bool, pub allow_middle_corridor_doors: bool,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct WindowSettings { pub struct WindowSettings {
pub enabled: bool, pub enabled: bool,
pub min_width: usize, pub min_width: usize,
@@ -87,7 +88,7 @@ pub struct WindowSettings {
pub allow_internal_windows: bool, pub allow_internal_windows: bool,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DungeonLayout { pub struct DungeonLayout {
pub rooms: Vec<Room>, pub rooms: Vec<Room>,
pub corridors: Vec<Corridor>, pub corridors: Vec<Corridor>,
+18
View File
@@ -1,5 +1,6 @@
mod exporter; mod exporter;
mod layout; mod layout;
mod saveandload;
mod seed; mod seed;
mod settings; mod settings;
mod startend; mod startend;
@@ -139,6 +140,23 @@ impl eframe::App for DungeonApp {
} else if panel_result.reset_clicked || panel_result.settings_changed { } else if panel_result.reset_clicked || panel_result.settings_changed {
self.push_undo_snapshot(); self.push_undo_snapshot();
self.regenerate_layout(); self.regenerate_layout();
} else if panel_result.save_clicked {
let svg = exporter::build_svg(&self.layout, &self.settings);
if let Err(err) = saveandload::save_dungeon(&self.settings, &self.layout, svg) {
eprintln!("{err}");
}
} else if panel_result.load_clicked {
match saveandload::load_dungeon() {
Ok(data) => {
self.push_undo_snapshot();
self.settings = data.settings;
self.layout = data.layout;
// Don't regenerate_layout() here to preserve the exact loaded state.
}
Err(err) => {
eprintln!("{err}");
}
}
} }
if ctx.input(|i| i.key_pressed(egui::Key::Delete) || i.key_pressed(egui::Key::Backspace)) { if ctx.input(|i| i.key_pressed(egui::Key::Delete) || i.key_pressed(egui::Key::Backspace)) {
self.pending_delete = true; self.pending_delete = true;
+55
View File
@@ -0,0 +1,55 @@
use rfd::FileDialog;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use crate::layout::DungeonLayout;
use crate::ui::UiSettings;
#[derive(Serialize, Deserialize)]
pub struct DungeonSave {
pub settings: UiSettings,
pub layout: DungeonLayout,
pub svg: String,
}
pub fn save_dungeon(
settings: &UiSettings,
layout: &DungeonLayout,
svg: String,
) -> Result<PathBuf, String> {
let path = FileDialog::new()
.set_title("Save Dungeon State")
.add_filter("Dungeon File", &["dungeon"])
.set_file_name("my_dungeon.dungeon")
.save_file()
.ok_or_else(|| "Save canceled".to_string())?;
let save_data = DungeonSave {
settings: settings.clone(),
layout: layout.clone(),
svg,
};
let json = serde_json::to_string_pretty(&save_data)
.map_err(|e| format!("Failed to serialize dungeon: {e}"))?;
fs::write(&path, json).map_err(|e| format!("Failed to write file: {e}"))?;
Ok(path)
}
pub fn load_dungeon() -> Result<DungeonSave, String> {
let path = FileDialog::new()
.set_title("Load Dungeon State")
.add_filter("Dungeon File", &["dungeon"])
.pick_file()
.ok_or_else(|| "Load canceled".to_string())?;
let content = fs::read_to_string(path).map_err(|e| format!("Failed to read file: {e}"))?;
let save_data: DungeonSave =
serde_json::from_str(&content).map_err(|e| format!("Failed to parse dungeon file: {e}"))?;
Ok(save_data)
}
+12
View File
@@ -226,6 +226,8 @@ pub struct SidePanelResult {
pub reset_clicked: bool, pub reset_clicked: bool,
pub clear_clicked: bool, pub clear_clicked: bool,
pub export_clicked: bool, pub export_clicked: bool,
pub save_clicked: bool,
pub load_clicked: bool,
} }
pub fn draw_side_panel( pub fn draw_side_panel(
@@ -354,6 +356,16 @@ fn draw_generate_tab(
} }
}); });
ui.add_space(8.0);
ui.horizontal(|ui| {
if ui.button("Save State").clicked() {
result.save_clicked = true;
}
if ui.button("Load State").clicked() {
result.load_clicked = true;
}
});
ui.add_space(12.0); ui.add_space(12.0);
ui.separator(); ui.separator();
ui.add_space(8.0); ui.add_space(8.0);