added start and end to exports
This commit is contained in:
@@ -341,11 +341,17 @@ fn export_composite_masks(
|
|||||||
.ok_or_else(|| "Failed to allocate secret doors mask canvas".to_string())?;
|
.ok_or_else(|| "Failed to allocate secret doors mask canvas".to_string())?;
|
||||||
let mut windows = Pixmap::new(g.width, g.height)
|
let mut windows = Pixmap::new(g.width, g.height)
|
||||||
.ok_or_else(|| "Failed to allocate windows mask canvas".to_string())?;
|
.ok_or_else(|| "Failed to allocate windows mask canvas".to_string())?;
|
||||||
|
let mut start_markers = Pixmap::new(g.width, g.height)
|
||||||
|
.ok_or_else(|| "Failed to allocate start markers mask canvas".to_string())?;
|
||||||
|
let mut end_markers = Pixmap::new(g.width, g.height)
|
||||||
|
.ok_or_else(|| "Failed to allocate end markers mask canvas".to_string())?;
|
||||||
fill_bg(&mut doors, BLACK);
|
fill_bg(&mut doors, BLACK);
|
||||||
fill_bg(&mut archways, BLACK);
|
fill_bg(&mut archways, BLACK);
|
||||||
fill_bg(&mut locked_doors, BLACK);
|
fill_bg(&mut locked_doors, BLACK);
|
||||||
fill_bg(&mut secret_doors, BLACK);
|
fill_bg(&mut secret_doors, BLACK);
|
||||||
fill_bg(&mut windows, BLACK);
|
fill_bg(&mut windows, BLACK);
|
||||||
|
fill_bg(&mut start_markers, BLACK);
|
||||||
|
fill_bg(&mut end_markers, BLACK);
|
||||||
for door in &layout.doors {
|
for door in &layout.doors {
|
||||||
if door.secret {
|
if door.secret {
|
||||||
draw_door(&mut secret_doors, &g, door, door_w, WHITE, DoorStyle::Solid);
|
draw_door(&mut secret_doors, &g, door, door_w, WHITE, DoorStyle::Solid);
|
||||||
@@ -360,6 +366,12 @@ fn export_composite_masks(
|
|||||||
for window in &layout.windows {
|
for window in &layout.windows {
|
||||||
draw_window(&mut windows, &g, window, door_w, WHITE, DoorStyle::Solid);
|
draw_window(&mut windows, &g, window, door_w, WHITE, DoorStyle::Solid);
|
||||||
}
|
}
|
||||||
|
for marker in &layout.start_markers {
|
||||||
|
fill_rect_marker(&mut start_markers, &g, marker, WHITE);
|
||||||
|
}
|
||||||
|
for marker in &layout.end_markers {
|
||||||
|
fill_rect_marker(&mut end_markers, &g, marker, WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
let composite = render_pixmap(layout, settings)?;
|
let composite = render_pixmap(layout, settings)?;
|
||||||
let composite_img = raster_image_from_pixmap(&composite, settings)?;
|
let composite_img = raster_image_from_pixmap(&composite, settings)?;
|
||||||
@@ -376,6 +388,8 @@ fn export_composite_masks(
|
|||||||
save_mask_image(folder, "locked_doors_mask", &locked_doors, settings)?;
|
save_mask_image(folder, "locked_doors_mask", &locked_doors, settings)?;
|
||||||
save_mask_image(folder, "secret_doors_mask", &secret_doors, settings)?;
|
save_mask_image(folder, "secret_doors_mask", &secret_doors, settings)?;
|
||||||
save_mask_image(folder, "windows_mask", &windows, settings)?;
|
save_mask_image(folder, "windows_mask", &windows, settings)?;
|
||||||
|
save_mask_image(folder, "start_markers_mask", &start_markers, settings)?;
|
||||||
|
save_mask_image(folder, "end_markers_mask", &end_markers, settings)?;
|
||||||
if settings.export_show_grid {
|
if settings.export_show_grid {
|
||||||
let mut grid = Pixmap::new(g.width, g.height)
|
let mut grid = Pixmap::new(g.width, g.height)
|
||||||
.ok_or_else(|| "Failed to allocate grid mask canvas".to_string())?;
|
.ok_or_else(|| "Failed to allocate grid mask canvas".to_string())?;
|
||||||
|
|||||||
+3
-175
@@ -2,6 +2,7 @@ mod exporter;
|
|||||||
mod layout;
|
mod layout;
|
||||||
mod seed;
|
mod seed;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
mod startend;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -14,6 +15,7 @@ use layout::{
|
|||||||
DoorSettings, DungeonLayout, WindowSettings, blocked_room_cells, corridor_cells,
|
DoorSettings, DungeonLayout, WindowSettings, blocked_room_cells, corridor_cells,
|
||||||
shortest_path_cells,
|
shortest_path_cells,
|
||||||
};
|
};
|
||||||
|
use startend::{manual_marker_size, populate_random_markers};
|
||||||
use ui::{AddTool, UiSettings, draw_side_panel};
|
use ui::{AddTool, UiSettings, draw_side_panel};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -848,7 +850,7 @@ impl DungeonApp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = manual_marker_size(&self.settings, kind);
|
let size = manual_marker_size(&self.settings, matches!(kind, MarkerKind::Start));
|
||||||
let origin = centered_marker_origin(cell, size, self.settings.cols, self.settings.rows);
|
let origin = centered_marker_origin(cell, size, self.settings.cols, self.settings.rows);
|
||||||
let marker = layout::AreaMarker { cell: origin, size };
|
let marker = layout::AreaMarker { cell: origin, size };
|
||||||
|
|
||||||
@@ -2004,180 +2006,6 @@ fn centered_marker_origin(
|
|||||||
(cell.0.saturating_sub(half).min(max_x), cell.1.saturating_sub(half).min(max_y))
|
(cell.0.saturating_sub(half).min(max_x), cell.1.saturating_sub(half).min(max_y))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_random_markers(
|
|
||||||
mut layout: DungeonLayout,
|
|
||||||
settings: &UiSettings,
|
|
||||||
) -> DungeonLayout {
|
|
||||||
layout.start_markers.clear();
|
|
||||||
layout.end_markers.clear();
|
|
||||||
|
|
||||||
if layout.rooms.is_empty() {
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
let start_count = random_range_inclusive(
|
|
||||||
settings.min_start_marker_count,
|
|
||||||
settings.max_start_marker_count,
|
|
||||||
seed::derive_seed(settings.seed, 10_001),
|
|
||||||
);
|
|
||||||
let end_count = random_range_inclusive(
|
|
||||||
settings.min_end_marker_count,
|
|
||||||
settings.max_end_marker_count,
|
|
||||||
seed::derive_seed(settings.seed, 10_002),
|
|
||||||
);
|
|
||||||
let pair_count = start_count.min(end_count);
|
|
||||||
|
|
||||||
let mut available_start_rooms: Vec<usize> = (0..layout.rooms.len()).collect();
|
|
||||||
let mut available_end_rooms: Vec<usize> = (0..layout.rooms.len()).collect();
|
|
||||||
|
|
||||||
for pair_idx in 0..pair_count {
|
|
||||||
let (start_room_idx, end_room_idx) =
|
|
||||||
farthest_room_pair(&layout.rooms, &available_start_rooms, &available_end_rooms);
|
|
||||||
let start_room = &layout.rooms[start_room_idx];
|
|
||||||
let end_room = &layout.rooms[end_room_idx];
|
|
||||||
layout.start_markers.push(marker_in_room(
|
|
||||||
start_room,
|
|
||||||
settings.min_start_marker_size,
|
|
||||||
settings.max_start_marker_size,
|
|
||||||
seed::derive_seed(settings.seed, 11_000 + pair_idx as u64),
|
|
||||||
));
|
|
||||||
layout.end_markers.push(marker_in_room(
|
|
||||||
end_room,
|
|
||||||
settings.min_end_marker_size,
|
|
||||||
settings.max_end_marker_size,
|
|
||||||
seed::derive_seed(settings.seed, 12_000 + pair_idx as u64),
|
|
||||||
));
|
|
||||||
consume_room(&mut available_start_rooms, start_room_idx);
|
|
||||||
consume_room(&mut available_end_rooms, end_room_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx in pair_count..start_count {
|
|
||||||
let room_idx = pick_room_index(
|
|
||||||
&available_start_rooms,
|
|
||||||
layout.rooms.len(),
|
|
||||||
seed::derive_seed(settings.seed, 13_000 + idx as u64),
|
|
||||||
);
|
|
||||||
let room = &layout.rooms[room_idx];
|
|
||||||
layout.start_markers.push(marker_in_room(
|
|
||||||
room,
|
|
||||||
settings.min_start_marker_size,
|
|
||||||
settings.max_start_marker_size,
|
|
||||||
seed::derive_seed(settings.seed, 14_000 + idx as u64),
|
|
||||||
));
|
|
||||||
consume_room(&mut available_start_rooms, room_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx in pair_count..end_count {
|
|
||||||
let room_idx = pick_room_index(
|
|
||||||
&available_end_rooms,
|
|
||||||
layout.rooms.len(),
|
|
||||||
seed::derive_seed(settings.seed, 15_000 + idx as u64),
|
|
||||||
);
|
|
||||||
let room = &layout.rooms[room_idx];
|
|
||||||
layout.end_markers.push(marker_in_room(
|
|
||||||
room,
|
|
||||||
settings.min_end_marker_size,
|
|
||||||
settings.max_end_marker_size,
|
|
||||||
seed::derive_seed(settings.seed, 16_000 + idx as u64),
|
|
||||||
));
|
|
||||||
consume_room(&mut available_end_rooms, room_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
layout
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize {
|
|
||||||
let min = min.max(1);
|
|
||||||
let max = max.max(min);
|
|
||||||
let span = max - min + 1;
|
|
||||||
min + (seed_value as usize % span)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn manual_marker_size(settings: &UiSettings, kind: MarkerKind) -> usize {
|
|
||||||
match kind {
|
|
||||||
MarkerKind::Start => settings.min_start_marker_size.min(settings.max_start_marker_size),
|
|
||||||
MarkerKind::End => settings.min_end_marker_size.min(settings.max_end_marker_size),
|
|
||||||
}
|
|
||||||
.clamp(1, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn marker_in_room(room: &layout::Room, min_size: usize, max_size: usize, seed_value: u64) -> layout::AreaMarker {
|
|
||||||
let room_limit = room.width.min(room.height).max(1);
|
|
||||||
let min_size = min_size.clamp(1, 10).min(room_limit);
|
|
||||||
let max_size = max_size.clamp(min_size, 10).min(room_limit);
|
|
||||||
let size = random_range_inclusive(min_size, max_size, seed_value.rotate_left(7));
|
|
||||||
let x_span = room.width.saturating_sub(size);
|
|
||||||
let y_span = room.height.saturating_sub(size);
|
|
||||||
let x = room.x
|
|
||||||
+ if x_span == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
(seed_value as usize) % (x_span + 1)
|
|
||||||
};
|
|
||||||
let y = room.y
|
|
||||||
+ if y_span == 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
(seed_value.rotate_left(19) as usize) % (y_span + 1)
|
|
||||||
};
|
|
||||||
layout::AreaMarker { cell: (x, y), size }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn farthest_room_pair(
|
|
||||||
rooms: &[layout::Room],
|
|
||||||
available_start_rooms: &[usize],
|
|
||||||
available_end_rooms: &[usize],
|
|
||||||
) -> (usize, usize) {
|
|
||||||
let start_rooms = if available_start_rooms.is_empty() {
|
|
||||||
(0..rooms.len()).collect::<Vec<_>>()
|
|
||||||
} else {
|
|
||||||
available_start_rooms.to_vec()
|
|
||||||
};
|
|
||||||
let end_rooms = if available_end_rooms.is_empty() {
|
|
||||||
(0..rooms.len()).collect::<Vec<_>>()
|
|
||||||
} else {
|
|
||||||
available_end_rooms.to_vec()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut best = (start_rooms[0], end_rooms[0]);
|
|
||||||
let mut best_dist = 0usize;
|
|
||||||
for &start_idx in &start_rooms {
|
|
||||||
for &end_idx in &end_rooms {
|
|
||||||
if rooms.len() > 1 && start_idx == end_idx {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let dist = room_distance_sq(&rooms[start_idx], &rooms[end_idx]);
|
|
||||||
if dist > best_dist {
|
|
||||||
best = (start_idx, end_idx);
|
|
||||||
best_dist = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
best
|
|
||||||
}
|
|
||||||
|
|
||||||
fn room_distance_sq(a: &layout::Room, b: &layout::Room) -> usize {
|
|
||||||
let ac = a.center_cell();
|
|
||||||
let bc = b.center_cell();
|
|
||||||
let dx = ac.0.abs_diff(bc.0);
|
|
||||||
let dy = ac.1.abs_diff(bc.1);
|
|
||||||
dx * dx + dy * dy
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()]
|
|
||||||
} else {
|
|
||||||
seed_value as usize % room_count.max(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_area_marker(
|
fn draw_area_marker(
|
||||||
painter: &egui::Painter,
|
painter: &egui::Painter,
|
||||||
geometry: &GridGeometry,
|
geometry: &GridGeometry,
|
||||||
|
|||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
use crate::layout::{self, DungeonLayout};
|
||||||
|
use crate::seed;
|
||||||
|
use crate::ui::UiSettings;
|
||||||
|
|
||||||
|
pub fn populate_random_markers(
|
||||||
|
mut layout: DungeonLayout,
|
||||||
|
settings: &UiSettings,
|
||||||
|
) -> DungeonLayout {
|
||||||
|
layout.start_markers.clear();
|
||||||
|
layout.end_markers.clear();
|
||||||
|
|
||||||
|
if layout.rooms.is_empty() {
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start_count = random_range_inclusive(
|
||||||
|
settings.min_start_marker_count,
|
||||||
|
settings.max_start_marker_count,
|
||||||
|
seed::derive_seed(settings.seed, 10_001),
|
||||||
|
);
|
||||||
|
let end_count = random_range_inclusive(
|
||||||
|
settings.min_end_marker_count,
|
||||||
|
settings.max_end_marker_count,
|
||||||
|
seed::derive_seed(settings.seed, 10_002),
|
||||||
|
);
|
||||||
|
let pair_count = start_count.min(end_count);
|
||||||
|
|
||||||
|
let mut available_start_rooms: Vec<usize> = (0..layout.rooms.len()).collect();
|
||||||
|
let mut available_end_rooms: Vec<usize> = (0..layout.rooms.len()).collect();
|
||||||
|
|
||||||
|
for pair_idx in 0..pair_count {
|
||||||
|
let (start_room_idx, end_room_idx) =
|
||||||
|
farthest_room_pair(&layout.rooms, &available_start_rooms, &available_end_rooms);
|
||||||
|
let start_room = &layout.rooms[start_room_idx];
|
||||||
|
let end_room = &layout.rooms[end_room_idx];
|
||||||
|
layout.start_markers.push(marker_in_room(
|
||||||
|
start_room,
|
||||||
|
settings.min_start_marker_size,
|
||||||
|
settings.max_start_marker_size,
|
||||||
|
seed::derive_seed(settings.seed, 11_000 + pair_idx as u64),
|
||||||
|
));
|
||||||
|
layout.end_markers.push(marker_in_room(
|
||||||
|
end_room,
|
||||||
|
settings.min_end_marker_size,
|
||||||
|
settings.max_end_marker_size,
|
||||||
|
seed::derive_seed(settings.seed, 12_000 + pair_idx as u64),
|
||||||
|
));
|
||||||
|
consume_room(&mut available_start_rooms, start_room_idx);
|
||||||
|
consume_room(&mut available_end_rooms, end_room_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx in pair_count..start_count {
|
||||||
|
let room_idx = pick_room_index(
|
||||||
|
&available_start_rooms,
|
||||||
|
layout.rooms.len(),
|
||||||
|
seed::derive_seed(settings.seed, 13_000 + idx as u64),
|
||||||
|
);
|
||||||
|
let room = &layout.rooms[room_idx];
|
||||||
|
layout.start_markers.push(marker_in_room(
|
||||||
|
room,
|
||||||
|
settings.min_start_marker_size,
|
||||||
|
settings.max_start_marker_size,
|
||||||
|
seed::derive_seed(settings.seed, 14_000 + idx as u64),
|
||||||
|
));
|
||||||
|
consume_room(&mut available_start_rooms, room_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx in pair_count..end_count {
|
||||||
|
let room_idx = pick_room_index(
|
||||||
|
&available_end_rooms,
|
||||||
|
layout.rooms.len(),
|
||||||
|
seed::derive_seed(settings.seed, 15_000 + idx as u64),
|
||||||
|
);
|
||||||
|
let room = &layout.rooms[room_idx];
|
||||||
|
layout.end_markers.push(marker_in_room(
|
||||||
|
room,
|
||||||
|
settings.min_end_marker_size,
|
||||||
|
settings.max_end_marker_size,
|
||||||
|
seed::derive_seed(settings.seed, 16_000 + idx as u64),
|
||||||
|
));
|
||||||
|
consume_room(&mut available_end_rooms, room_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
layout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn manual_marker_size(settings: &UiSettings, is_start: bool) -> usize {
|
||||||
|
if is_start {
|
||||||
|
settings
|
||||||
|
.min_start_marker_size
|
||||||
|
.min(settings.max_start_marker_size)
|
||||||
|
} else {
|
||||||
|
settings.min_end_marker_size.min(settings.max_end_marker_size)
|
||||||
|
}
|
||||||
|
.clamp(1, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_range_inclusive(min: usize, max: usize, seed_value: u64) -> usize {
|
||||||
|
let min = min.max(1);
|
||||||
|
let max = max.max(min);
|
||||||
|
let span = max - min + 1;
|
||||||
|
min + (seed_value as usize % span)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn marker_in_room(
|
||||||
|
room: &layout::Room,
|
||||||
|
min_size: usize,
|
||||||
|
max_size: usize,
|
||||||
|
seed_value: u64,
|
||||||
|
) -> layout::AreaMarker {
|
||||||
|
let room_limit = room.width.min(room.height).max(1);
|
||||||
|
let min_size = min_size.clamp(1, 10).min(room_limit);
|
||||||
|
let max_size = max_size.clamp(min_size, 10).min(room_limit);
|
||||||
|
let size = random_range_inclusive(min_size, max_size, seed_value.rotate_left(7));
|
||||||
|
let x_span = room.width.saturating_sub(size);
|
||||||
|
let y_span = room.height.saturating_sub(size);
|
||||||
|
let x = room.x
|
||||||
|
+ if x_span == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(seed_value as usize) % (x_span + 1)
|
||||||
|
};
|
||||||
|
let y = room.y
|
||||||
|
+ if y_span == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(seed_value.rotate_left(19) as usize) % (y_span + 1)
|
||||||
|
};
|
||||||
|
layout::AreaMarker { cell: (x, y), size }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn farthest_room_pair(
|
||||||
|
rooms: &[layout::Room],
|
||||||
|
available_start_rooms: &[usize],
|
||||||
|
available_end_rooms: &[usize],
|
||||||
|
) -> (usize, usize) {
|
||||||
|
let start_rooms = if available_start_rooms.is_empty() {
|
||||||
|
(0..rooms.len()).collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
available_start_rooms.to_vec()
|
||||||
|
};
|
||||||
|
let end_rooms = if available_end_rooms.is_empty() {
|
||||||
|
(0..rooms.len()).collect::<Vec<_>>()
|
||||||
|
} else {
|
||||||
|
available_end_rooms.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut best = (start_rooms[0], end_rooms[0]);
|
||||||
|
let mut best_dist = 0usize;
|
||||||
|
for &start_idx in &start_rooms {
|
||||||
|
for &end_idx in &end_rooms {
|
||||||
|
if rooms.len() > 1 && start_idx == end_idx {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let dist = room_distance_sq(&rooms[start_idx], &rooms[end_idx]);
|
||||||
|
if dist > best_dist {
|
||||||
|
best = (start_idx, end_idx);
|
||||||
|
best_dist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
best
|
||||||
|
}
|
||||||
|
|
||||||
|
fn room_distance_sq(a: &layout::Room, b: &layout::Room) -> usize {
|
||||||
|
let ac = a.center_cell();
|
||||||
|
let bc = b.center_cell();
|
||||||
|
let dx = ac.0.abs_diff(bc.0);
|
||||||
|
let dy = ac.1.abs_diff(bc.1);
|
||||||
|
dx * dx + dy * dy
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()]
|
||||||
|
} else {
|
||||||
|
seed_value as usize % room_count.max(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user