added generation of start/end and settings for that, along with stuff for place, move, and remove
This commit is contained in:
+4
-4
@@ -276,10 +276,10 @@ fn render_pixmap(layout: &DungeonLayout, settings: &UiSettings) -> Result<Pixmap
|
||||
draw_window(&mut pixmap, &g, window, door_w, WINDOW, style);
|
||||
}
|
||||
|
||||
if let Some(marker) = layout.start_marker.as_ref() {
|
||||
for marker in &layout.start_markers {
|
||||
draw_area_marker(&mut pixmap, &g, marker, "S", START_MARKER, settings.colorblind_mode);
|
||||
}
|
||||
if let Some(marker) = layout.end_marker.as_ref() {
|
||||
for marker in &layout.end_markers {
|
||||
draw_area_marker(&mut pixmap, &g, marker, "E", END_MARKER, settings.colorblind_mode);
|
||||
}
|
||||
|
||||
@@ -677,10 +677,10 @@ fn build_svg(layout: &DungeonLayout, settings: &UiSettings) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(marker) = layout.start_marker.as_ref() {
|
||||
for marker in &layout.start_markers {
|
||||
append_svg_area_marker(&mut s, &g, marker, "S", START_MARKER, settings.colorblind_mode);
|
||||
}
|
||||
if let Some(marker) = layout.end_marker.as_ref() {
|
||||
for marker in &layout.end_markers {
|
||||
append_svg_area_marker(&mut s, &g, marker, "E", END_MARKER, settings.colorblind_mode);
|
||||
}
|
||||
|
||||
|
||||
+14
-14
@@ -94,8 +94,8 @@ pub struct DungeonLayout {
|
||||
pub doors: Vec<Door>,
|
||||
pub windows: Vec<Window>,
|
||||
pub text_labels: Vec<TextLabel>,
|
||||
pub start_marker: Option<AreaMarker>,
|
||||
pub end_marker: Option<AreaMarker>,
|
||||
pub start_markers: Vec<AreaMarker>,
|
||||
pub end_markers: Vec<AreaMarker>,
|
||||
pub packed_rooms: bool,
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ impl Default for DungeonLayout {
|
||||
doors: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
text_labels: Vec::new(),
|
||||
start_marker: None,
|
||||
end_marker: None,
|
||||
start_markers: Vec::new(),
|
||||
end_markers: Vec::new(),
|
||||
packed_rooms: false,
|
||||
}
|
||||
}
|
||||
@@ -176,8 +176,8 @@ pub fn generate_layout(
|
||||
doors: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
text_labels: Vec::new(),
|
||||
start_marker: None,
|
||||
end_marker: None,
|
||||
start_markers: Vec::new(),
|
||||
end_markers: Vec::new(),
|
||||
packed_rooms: pack_rooms_without_corridors,
|
||||
};
|
||||
}
|
||||
@@ -201,8 +201,8 @@ pub fn generate_layout(
|
||||
doors: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
text_labels: Vec::new(),
|
||||
start_marker: None,
|
||||
end_marker: None,
|
||||
start_markers: Vec::new(),
|
||||
end_markers: Vec::new(),
|
||||
packed_rooms: pack_rooms_without_corridors,
|
||||
};
|
||||
}
|
||||
@@ -231,8 +231,8 @@ pub fn generate_layout(
|
||||
doors: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
text_labels: Vec::new(),
|
||||
start_marker: None,
|
||||
end_marker: None,
|
||||
start_markers: Vec::new(),
|
||||
end_markers: Vec::new(),
|
||||
packed_rooms: true,
|
||||
};
|
||||
apply_doors(&mut layout, seed, door_settings, cols, rows);
|
||||
@@ -290,8 +290,8 @@ pub fn generate_layout(
|
||||
doors: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
text_labels: Vec::new(),
|
||||
start_marker: None,
|
||||
end_marker: None,
|
||||
start_markers: Vec::new(),
|
||||
end_markers: Vec::new(),
|
||||
packed_rooms: false,
|
||||
};
|
||||
}
|
||||
@@ -346,8 +346,8 @@ pub fn generate_layout(
|
||||
doors: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
text_labels: Vec::new(),
|
||||
start_marker: None,
|
||||
end_marker: None,
|
||||
start_markers: Vec::new(),
|
||||
end_markers: Vec::new(),
|
||||
packed_rooms: false,
|
||||
};
|
||||
apply_doors(&mut layout, seed, door_settings, cols, rows);
|
||||
|
||||
+311
-34
@@ -57,7 +57,7 @@ struct DungeonApp {
|
||||
hover_corridor_idx: Option<usize>,
|
||||
hover_door_idx: Option<usize>,
|
||||
hover_text_idx: Option<usize>,
|
||||
hover_marker_kind: Option<MarkerKind>,
|
||||
hover_marker: Option<HoverMarker>,
|
||||
}
|
||||
|
||||
impl Default for DungeonApp {
|
||||
@@ -80,6 +80,7 @@ impl Default for DungeonApp {
|
||||
door_settings_from_ui(&settings),
|
||||
window_settings_from_ui(&settings),
|
||||
);
|
||||
let layout = populate_random_markers(layout, &settings);
|
||||
Self {
|
||||
settings,
|
||||
layout,
|
||||
@@ -95,7 +96,7 @@ impl Default for DungeonApp {
|
||||
hover_corridor_idx: None,
|
||||
hover_door_idx: None,
|
||||
hover_text_idx: None,
|
||||
hover_marker_kind: None,
|
||||
hover_marker: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,6 +132,18 @@ impl eframe::App for DungeonApp {
|
||||
if self.settings.min_window_width > self.settings.max_window_width {
|
||||
self.settings.max_window_width = self.settings.min_window_width;
|
||||
}
|
||||
if self.settings.min_start_marker_size > self.settings.max_start_marker_size {
|
||||
self.settings.max_start_marker_size = self.settings.min_start_marker_size;
|
||||
}
|
||||
if self.settings.min_end_marker_size > self.settings.max_end_marker_size {
|
||||
self.settings.max_end_marker_size = self.settings.min_end_marker_size;
|
||||
}
|
||||
if self.settings.min_start_marker_count > self.settings.max_start_marker_count {
|
||||
self.settings.max_start_marker_count = self.settings.min_start_marker_count;
|
||||
}
|
||||
if self.settings.min_end_marker_count > self.settings.max_end_marker_count {
|
||||
self.settings.max_end_marker_count = self.settings.min_end_marker_count;
|
||||
}
|
||||
|
||||
if undo_requested {
|
||||
self.undo();
|
||||
@@ -255,7 +268,7 @@ impl eframe::App for DungeonApp {
|
||||
&self.layout,
|
||||
self.settings.colorblind_mode,
|
||||
self.hover_text_idx,
|
||||
self.hover_marker_kind,
|
||||
self.hover_marker,
|
||||
);
|
||||
self.draw_add_overlay(&painter, &geometry);
|
||||
self.draw_resize_overlay(&painter, &geometry);
|
||||
@@ -297,7 +310,7 @@ impl DungeonApp {
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
self.hover_text_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
self.pending_delete = false;
|
||||
}
|
||||
|
||||
@@ -331,7 +344,7 @@ impl DungeonApp {
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
self.hover_text_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
self.pending_delete = false;
|
||||
}
|
||||
|
||||
@@ -339,7 +352,7 @@ impl DungeonApp {
|
||||
fn regenerate_layout(&mut self) {
|
||||
self.drag_state = None;
|
||||
self.suppressed_auto_door_edges.clear();
|
||||
self.layout = layout::generate_layout(
|
||||
let layout = layout::generate_layout(
|
||||
self.settings.cols,
|
||||
self.settings.rows,
|
||||
self.settings.room_count,
|
||||
@@ -355,6 +368,7 @@ impl DungeonApp {
|
||||
door_settings_from_ui(&self.settings),
|
||||
window_settings_from_ui(&self.settings),
|
||||
);
|
||||
self.layout = populate_random_markers(layout, &self.settings);
|
||||
}
|
||||
|
||||
// Handle dragging rooms or corridors with the primary mouse button.
|
||||
@@ -376,6 +390,12 @@ impl DungeonApp {
|
||||
{
|
||||
self.push_undo_snapshot();
|
||||
self.drag_state = Some(DragState::Text(TextDragState { text_idx }));
|
||||
} else if response.drag_started()
|
||||
&& let Some(pointer_pos) = response.interact_pointer_pos()
|
||||
&& let Some(marker_drag) = self.marker_drag_at_pointer(pointer_pos, geometry)
|
||||
{
|
||||
self.push_undo_snapshot();
|
||||
self.drag_state = Some(DragState::Marker(marker_drag));
|
||||
} else if response.drag_started()
|
||||
&& let Some(pointer_pos) = response.interact_pointer_pos()
|
||||
&& let Some((room_idx, offset_x, offset_y)) =
|
||||
@@ -402,6 +422,7 @@ impl DungeonApp {
|
||||
match drag_state {
|
||||
DragState::Room(drag) => self.drag_room_to_pointer(drag, pointer_pos, geometry),
|
||||
DragState::Text(drag) => self.drag_text_to_pointer(drag, pointer_pos, geometry),
|
||||
DragState::Marker(drag) => self.drag_marker_to_pointer(drag, pointer_pos, geometry),
|
||||
DragState::Corridor(drag) => {
|
||||
self.drag_corridor_to_pointer(drag, pointer_pos, geometry)
|
||||
}
|
||||
@@ -827,18 +848,14 @@ impl DungeonApp {
|
||||
return;
|
||||
}
|
||||
|
||||
let size = match kind {
|
||||
MarkerKind::Start => self.settings.start_marker_size,
|
||||
MarkerKind::End => self.settings.end_marker_size,
|
||||
}
|
||||
.clamp(1, 10);
|
||||
let size = manual_marker_size(&self.settings, kind);
|
||||
let origin = centered_marker_origin(cell, size, self.settings.cols, self.settings.rows);
|
||||
let marker = layout::AreaMarker { cell: origin, size };
|
||||
|
||||
self.push_undo_snapshot();
|
||||
match kind {
|
||||
MarkerKind::Start => self.layout.start_marker = Some(marker),
|
||||
MarkerKind::End => self.layout.end_marker = Some(marker),
|
||||
MarkerKind::Start => self.layout.start_markers.push(marker),
|
||||
MarkerKind::End => self.layout.end_markers.push(marker),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,7 +866,7 @@ impl DungeonApp {
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
self.hover_text_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
return;
|
||||
};
|
||||
if let Some(edge) = self.door_edge_at_pointer(pointer_pos, geometry) {
|
||||
@@ -863,7 +880,7 @@ impl DungeonApp {
|
||||
self.hover_room_idx = None;
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_text_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -873,12 +890,12 @@ impl DungeonApp {
|
||||
self.hover_room_idx = None;
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
return;
|
||||
}
|
||||
|
||||
self.hover_marker_kind = self.marker_at_pointer(pointer_pos, geometry);
|
||||
if self.hover_marker_kind.is_some() {
|
||||
self.hover_marker = self.marker_at_pointer(pointer_pos, geometry);
|
||||
if self.hover_marker.is_some() {
|
||||
self.hover_room_idx = None;
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
@@ -889,17 +906,17 @@ impl DungeonApp {
|
||||
self.hover_room_idx = Some(room_idx);
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
} else {
|
||||
self.hover_room_idx = None;
|
||||
if let Some(corridor_drag) = self.corridor_drag_at_pointer(pointer_pos, geometry) {
|
||||
self.hover_corridor_idx = Some(corridor_drag.corridor_index);
|
||||
self.hover_door_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
} else {
|
||||
self.hover_corridor_idx = None;
|
||||
self.hover_door_idx = None;
|
||||
self.hover_marker_kind = None;
|
||||
self.hover_marker = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -917,16 +934,79 @@ impl DungeonApp {
|
||||
&self,
|
||||
pointer_pos: egui::Pos2,
|
||||
geometry: &GridGeometry,
|
||||
) -> Option<MarkerKind> {
|
||||
) -> Option<HoverMarker> {
|
||||
let (grid_x, grid_y) = pointer_to_grid(pointer_pos, geometry)?;
|
||||
let clicked = (grid_x.floor() as usize, grid_y.floor() as usize);
|
||||
marker_contains_cell(self.layout.start_marker.as_ref(), clicked)
|
||||
.then_some(MarkerKind::Start)
|
||||
self.layout
|
||||
.start_markers
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, marker)| marker_contains_cell(Some(marker), clicked))
|
||||
.map(|(idx, _)| HoverMarker::Start(idx))
|
||||
.or_else(|| {
|
||||
marker_contains_cell(self.layout.end_marker.as_ref(), clicked).then_some(MarkerKind::End)
|
||||
self.layout
|
||||
.end_markers
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, marker)| marker_contains_cell(Some(marker), clicked))
|
||||
.map(|(idx, _)| HoverMarker::End(idx))
|
||||
})
|
||||
}
|
||||
|
||||
fn marker_drag_at_pointer(
|
||||
&self,
|
||||
pointer_pos: egui::Pos2,
|
||||
geometry: &GridGeometry,
|
||||
) -> Option<MarkerDragState> {
|
||||
let (grid_x, grid_y) = pointer_to_grid(pointer_pos, geometry)?;
|
||||
match self.marker_at_pointer(pointer_pos, geometry)? {
|
||||
HoverMarker::Start(marker_idx) => {
|
||||
let marker = self.layout.start_markers.get(marker_idx)?;
|
||||
Some(MarkerDragState {
|
||||
kind: MarkerKind::Start,
|
||||
marker_idx,
|
||||
offset_x: grid_x - marker.cell.0 as f32,
|
||||
offset_y: grid_y - marker.cell.1 as f32,
|
||||
})
|
||||
}
|
||||
HoverMarker::End(marker_idx) => {
|
||||
let marker = self.layout.end_markers.get(marker_idx)?;
|
||||
Some(MarkerDragState {
|
||||
kind: MarkerKind::End,
|
||||
marker_idx,
|
||||
offset_x: grid_x - marker.cell.0 as f32,
|
||||
offset_y: grid_y - marker.cell.1 as f32,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn drag_marker_to_pointer(
|
||||
&mut self,
|
||||
drag: MarkerDragState,
|
||||
pointer_pos: egui::Pos2,
|
||||
geometry: &GridGeometry,
|
||||
) {
|
||||
let Some((grid_x, grid_y)) = pointer_to_grid(pointer_pos, geometry) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let markers = match drag.kind {
|
||||
MarkerKind::Start => &mut self.layout.start_markers,
|
||||
MarkerKind::End => &mut self.layout.end_markers,
|
||||
};
|
||||
let Some(marker) = markers.get_mut(drag.marker_idx) else {
|
||||
self.drag_state = None;
|
||||
return;
|
||||
};
|
||||
|
||||
let max_x = self.settings.cols.saturating_sub(marker.size);
|
||||
let max_y = self.settings.rows.saturating_sub(marker.size);
|
||||
let target_x = (grid_x - drag.offset_x).floor().max(0.0) as usize;
|
||||
let target_y = (grid_y - drag.offset_y).floor().max(0.0) as usize;
|
||||
marker.cell = (target_x.min(max_x), target_y.min(max_y));
|
||||
}
|
||||
|
||||
// Handle right-click resizing of rooms and return whether resizing is active.
|
||||
fn handle_resize(
|
||||
&mut self,
|
||||
@@ -1241,11 +1321,19 @@ impl DungeonApp {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(kind) = self.marker_at_pointer(pointer_pos, geometry) {
|
||||
if let Some(marker) = self.marker_at_pointer(pointer_pos, geometry) {
|
||||
self.push_undo_snapshot();
|
||||
match kind {
|
||||
MarkerKind::Start => self.layout.start_marker = None,
|
||||
MarkerKind::End => self.layout.end_marker = None,
|
||||
match marker {
|
||||
HoverMarker::Start(idx) => {
|
||||
if idx < self.layout.start_markers.len() {
|
||||
self.layout.start_markers.remove(idx);
|
||||
}
|
||||
}
|
||||
HoverMarker::End(idx) => {
|
||||
if idx < self.layout.end_markers.len() {
|
||||
self.layout.end_markers.remove(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1461,6 +1549,14 @@ struct TextDragState {
|
||||
text_idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MarkerDragState {
|
||||
kind: MarkerKind,
|
||||
marker_idx: usize,
|
||||
offset_x: f32,
|
||||
offset_y: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CorridorDragState {
|
||||
corridor_index: usize,
|
||||
@@ -1488,6 +1584,12 @@ enum MarkerKind {
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum HoverMarker {
|
||||
Start(usize),
|
||||
End(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct RoomResizeState {
|
||||
room_idx: usize,
|
||||
@@ -1499,6 +1601,7 @@ struct RoomResizeState {
|
||||
enum DragState {
|
||||
Room(RoomDragState),
|
||||
Text(TextDragState),
|
||||
Marker(MarkerDragState),
|
||||
Corridor(CorridorDragState),
|
||||
}
|
||||
|
||||
@@ -1579,7 +1682,7 @@ fn draw_layout(
|
||||
layout: &DungeonLayout,
|
||||
colorblind_mode: bool,
|
||||
hover_text_idx: Option<usize>,
|
||||
hover_marker_kind: Option<MarkerKind>,
|
||||
hover_marker: Option<HoverMarker>,
|
||||
) {
|
||||
let wall_width_px = (geometry.cell_size / 5.0).max(1.0);
|
||||
let door_width_px = (geometry.cell_size / 10.0).max(1.0);
|
||||
@@ -1832,7 +1935,7 @@ fn draw_layout(
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(marker) = layout.start_marker.as_ref() {
|
||||
for (idx, marker) in layout.start_markers.iter().enumerate() {
|
||||
draw_area_marker(
|
||||
painter,
|
||||
geometry,
|
||||
@@ -1840,10 +1943,10 @@ fn draw_layout(
|
||||
"S",
|
||||
Color32::from_rgb(60, 220, 200),
|
||||
colorblind_mode,
|
||||
hover_marker_kind == Some(MarkerKind::Start),
|
||||
hover_marker == Some(HoverMarker::Start(idx)),
|
||||
);
|
||||
}
|
||||
if let Some(marker) = layout.end_marker.as_ref() {
|
||||
for (idx, marker) in layout.end_markers.iter().enumerate() {
|
||||
draw_area_marker(
|
||||
painter,
|
||||
geometry,
|
||||
@@ -1851,7 +1954,7 @@ fn draw_layout(
|
||||
"E",
|
||||
Color32::from_rgb(240, 90, 90),
|
||||
colorblind_mode,
|
||||
hover_marker_kind == Some(MarkerKind::End),
|
||||
hover_marker == Some(HoverMarker::End(idx)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1901,6 +2004,180 @@ fn centered_marker_origin(
|
||||
(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(
|
||||
painter: &egui::Painter,
|
||||
geometry: &GridGeometry,
|
||||
|
||||
@@ -7,8 +7,8 @@ use serde::{Deserialize, Serialize};
|
||||
enum Tab {
|
||||
Generate,
|
||||
Layout,
|
||||
StartAndEnd,
|
||||
Add,
|
||||
MonstersAndTraps,
|
||||
}
|
||||
|
||||
impl Default for Tab {
|
||||
@@ -132,8 +132,14 @@ pub struct UiSettings {
|
||||
pub export_show_grid: bool,
|
||||
pub add_tool: AddTool,
|
||||
pub add_text_value: String,
|
||||
pub start_marker_size: usize,
|
||||
pub end_marker_size: usize,
|
||||
pub min_start_marker_size: usize,
|
||||
pub max_start_marker_size: usize,
|
||||
pub min_end_marker_size: usize,
|
||||
pub max_end_marker_size: usize,
|
||||
pub min_start_marker_count: usize,
|
||||
pub max_start_marker_count: usize,
|
||||
pub min_end_marker_count: usize,
|
||||
pub max_end_marker_count: usize,
|
||||
active_tab: Tab,
|
||||
}
|
||||
|
||||
@@ -173,8 +179,14 @@ impl Default for UiSettings {
|
||||
export_show_grid: true,
|
||||
add_tool: AddTool::None,
|
||||
add_text_value: "Text".to_string(),
|
||||
start_marker_size: 1,
|
||||
end_marker_size: 1,
|
||||
min_start_marker_size: 1,
|
||||
max_start_marker_size: 1,
|
||||
min_end_marker_size: 1,
|
||||
max_end_marker_size: 1,
|
||||
min_start_marker_count: 1,
|
||||
max_start_marker_count: 1,
|
||||
min_end_marker_count: 1,
|
||||
max_end_marker_count: 1,
|
||||
active_tab: Tab::Generate,
|
||||
}
|
||||
}
|
||||
@@ -219,8 +231,8 @@ pub fn draw_side_panel(
|
||||
ui.horizontal(|ui| {
|
||||
let is_generate = settings.active_tab == Tab::Generate;
|
||||
let is_layout = settings.active_tab == Tab::Layout;
|
||||
let is_start_and_end = settings.active_tab == Tab::StartAndEnd;
|
||||
let is_add = settings.active_tab == Tab::Add;
|
||||
let is_monsters_and_traps = settings.active_tab == Tab::MonstersAndTraps;
|
||||
|
||||
if ui.selectable_label(is_generate, "Generate").clicked() {
|
||||
settings.active_tab = Tab::Generate;
|
||||
@@ -228,23 +240,20 @@ pub fn draw_side_panel(
|
||||
if ui.selectable_label(is_layout, "Layout").clicked() {
|
||||
settings.active_tab = Tab::Layout;
|
||||
}
|
||||
if ui.selectable_label(is_start_and_end, "Start & End").clicked() {
|
||||
settings.active_tab = Tab::StartAndEnd;
|
||||
}
|
||||
if ui.selectable_label(is_add, "Add").clicked() {
|
||||
settings.active_tab = Tab::Add;
|
||||
}
|
||||
if ui
|
||||
.selectable_label(is_monsters_and_traps, "Monsters & Traps")
|
||||
.clicked()
|
||||
{
|
||||
settings.active_tab = Tab::MonstersAndTraps;
|
||||
}
|
||||
});
|
||||
ui.separator();
|
||||
|
||||
match settings.active_tab {
|
||||
Tab::Generate => draw_generate_tab(ui, settings, &mut result, export_in_progress),
|
||||
Tab::Layout => draw_layout_tab(ui, settings, &mut result),
|
||||
Tab::StartAndEnd => draw_start_and_end_tab(ui, settings, &mut result),
|
||||
Tab::Add => draw_add_tab(ui, settings),
|
||||
Tab::MonstersAndTraps => draw_monsters_and_traps_tab(ui, settings),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -740,6 +749,8 @@ fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings) {
|
||||
ui.add_space(4.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.selectable_value(&mut settings.add_tool, AddTool::Text, "Add Text");
|
||||
ui.selectable_value(&mut settings.add_tool, AddTool::StartMarker, "Place Start");
|
||||
ui.selectable_value(&mut settings.add_tool, AddTool::EndMarker, "Place End");
|
||||
});
|
||||
|
||||
ui.add_space(6.0);
|
||||
@@ -759,72 +770,136 @@ fn draw_add_tab(ui: &mut egui::Ui, settings: &mut UiSettings) {
|
||||
ui.label("Note: Left click on the canvas to place the text.");
|
||||
response
|
||||
}
|
||||
AddTool::StartMarker | AddTool::EndMarker => {
|
||||
ui.label("Use the Monsters & Traps tab to place start and end markers.")
|
||||
}
|
||||
AddTool::StartMarker => ui.label("Click the grid to place the start marker."),
|
||||
AddTool::EndMarker => ui.label("Click the grid to place the end marker."),
|
||||
AddTool::None => ui.label("Select a tool to add rooms or corridors."),
|
||||
};
|
||||
}
|
||||
|
||||
fn draw_monsters_and_traps_tab(ui: &mut egui::Ui, settings: &mut UiSettings) {
|
||||
ui.label(RichText::new("Start / End Markers").strong());
|
||||
fn draw_start_and_end_tab(
|
||||
ui: &mut egui::Ui,
|
||||
settings: &mut UiSettings,
|
||||
result: &mut SidePanelResult,
|
||||
) {
|
||||
ui.label(RichText::new("Start Markers").strong());
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.label("Start Marker Size");
|
||||
ui.label("Min Start Marker Size");
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(egui::Slider::new(&mut settings.start_marker_size, 1..=10).show_value(false));
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut settings.start_marker_size)
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.min_start_marker_size, 1..=10).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.min_start_marker_size)
|
||||
.speed(1.0)
|
||||
.range(1..=10),
|
||||
);
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
|
||||
ui.add_space(4.0);
|
||||
if ui
|
||||
.selectable_label(settings.add_tool == AddTool::StartMarker, "Place Start Marker")
|
||||
.clicked()
|
||||
{
|
||||
settings.add_tool = if settings.add_tool == AddTool::StartMarker {
|
||||
AddTool::None
|
||||
} else {
|
||||
AddTool::StartMarker
|
||||
};
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
ui.label("End Marker Size");
|
||||
ui.add_space(6.0);
|
||||
ui.label("Max Start Marker Size");
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(egui::Slider::new(&mut settings.end_marker_size, 1..=10).show_value(false));
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut settings.end_marker_size)
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.max_start_marker_size, 1..=10).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.max_start_marker_size)
|
||||
.speed(1.0)
|
||||
.range(1..=10),
|
||||
);
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.label("Min Start Marker Count");
|
||||
ui.horizontal(|ui| {
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.min_start_marker_count, 1..=20).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.min_start_marker_count)
|
||||
.speed(1.0)
|
||||
.range(1..=100),
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.label("Max Start Marker Count");
|
||||
ui.horizontal(|ui| {
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.max_start_marker_count, 1..=20).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.max_start_marker_count)
|
||||
.speed(1.0)
|
||||
.range(1..=100),
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
|
||||
ui.add_space(4.0);
|
||||
if ui
|
||||
.selectable_label(settings.add_tool == AddTool::EndMarker, "Place End Marker")
|
||||
.clicked()
|
||||
{
|
||||
settings.add_tool = if settings.add_tool == AddTool::EndMarker {
|
||||
AddTool::None
|
||||
} else {
|
||||
AddTool::EndMarker
|
||||
};
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
ui.separator();
|
||||
ui.add_space(8.0);
|
||||
ui.label(RichText::new("End Markers").strong());
|
||||
ui.add_space(8.0);
|
||||
ui.label("Min End Marker Size");
|
||||
ui.horizontal(|ui| {
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.min_end_marker_size, 1..=10).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.min_end_marker_size)
|
||||
.speed(1.0)
|
||||
.range(1..=10),
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.label("Max End Marker Size");
|
||||
ui.horizontal(|ui| {
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.max_end_marker_size, 1..=10).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.max_end_marker_size)
|
||||
.speed(1.0)
|
||||
.range(1..=10),
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.label("Min End Marker Count");
|
||||
ui.horizontal(|ui| {
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.min_end_marker_count, 1..=20).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.min_end_marker_count)
|
||||
.speed(1.0)
|
||||
.range(1..=100),
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.label("Max End Marker Count");
|
||||
ui.horizontal(|ui| {
|
||||
result.settings_changed |= ui
|
||||
.add(egui::Slider::new(&mut settings.max_end_marker_count, 1..=20).show_value(false))
|
||||
.changed();
|
||||
result.settings_changed |= ui
|
||||
.add(
|
||||
egui::DragValue::new(&mut settings.max_end_marker_count)
|
||||
.speed(1.0)
|
||||
.range(1..=100),
|
||||
)
|
||||
.changed();
|
||||
});
|
||||
|
||||
ui.add_space(8.0);
|
||||
match settings.add_tool {
|
||||
AddTool::StartMarker => {
|
||||
ui.label("Click the grid to place the start marker. It is centered on the clicked cell.");
|
||||
}
|
||||
AddTool::EndMarker => {
|
||||
ui.label("Click the grid to place the end marker. It is centered on the clicked cell.");
|
||||
}
|
||||
_ => {
|
||||
ui.label("Monsters, traps, and bosses will live here next. For now, place start and end markers.");
|
||||
}
|
||||
}
|
||||
ui.label("Markers are generated inside rooms. Matching start/end indices are paired as far apart as possible.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user