1use crate::{Grid, Tile, Rng};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct Region {
12 pub id: u32,
13 pub kind: String,
14 pub cells: Vec<(u32, u32)>,
15 pub tags: Vec<String>,
16}
17
18#[derive(Debug, Clone)]
20pub struct Marker {
21 pub x: u32,
22 pub y: u32,
23 pub tag: String,
24 pub weight: f32,
25 pub region_id: Option<u32>,
26 pub metadata: HashMap<String, String>,
27}
28
29#[derive(Debug, Clone)]
31pub struct Masks {
32 pub walkable: Vec<Vec<bool>>,
33 pub no_spawn: Vec<Vec<bool>>,
34 pub width: usize,
35 pub height: usize,
36}
37
38#[derive(Debug, Clone)]
40pub struct ConnectivityGraph {
41 pub regions: Vec<u32>,
42 pub edges: Vec<(u32, u32)>,
43}
44
45#[derive(Debug, Clone)]
47pub struct SemanticLayers {
48 pub regions: Vec<Region>,
49 pub markers: Vec<Marker>,
50 pub masks: Masks,
51 pub connectivity: ConnectivityGraph,
52}
53
54#[derive(Debug)]
56pub struct GenerationResult {
57 pub tiles: Grid<Tile>,
58 pub semantic: Option<SemanticLayers>,
59}
60
61pub trait SemanticGenerator<T: crate::grid::Cell> {
63 fn generate_semantic(&self, grid: &Grid<T>, rng: &mut Rng) -> SemanticLayers;
65}
66
67impl GenerationResult {
68 pub fn new(tiles: Grid<Tile>) -> Self {
69 Self { tiles, semantic: None }
70 }
71
72 pub fn with_semantic(tiles: Grid<Tile>, semantic: SemanticLayers) -> Self {
73 Self { tiles, semantic: Some(semantic) }
74 }
75}
76
77impl Region {
78 pub fn new(id: u32, kind: impl Into<String>) -> Self {
79 Self {
80 id,
81 kind: kind.into(),
82 cells: Vec::new(),
83 tags: Vec::new(),
84 }
85 }
86
87 pub fn add_cell(&mut self, x: u32, y: u32) {
88 self.cells.push((x, y));
89 }
90
91 pub fn add_tag(&mut self, tag: impl Into<String>) {
92 self.tags.push(tag.into());
93 }
94
95 pub fn area(&self) -> usize {
96 self.cells.len()
97 }
98}
99
100impl Marker {
101 pub fn new(x: u32, y: u32, tag: impl Into<String>) -> Self {
102 Self {
103 x, y,
104 tag: tag.into(),
105 weight: 1.0,
106 region_id: None,
107 metadata: HashMap::new(),
108 }
109 }
110
111 pub fn with_weight(mut self, weight: f32) -> Self {
112 self.weight = weight;
113 self
114 }
115
116 pub fn with_region(mut self, region_id: u32) -> Self {
117 self.region_id = Some(region_id);
118 self
119 }
120
121 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
122 self.metadata.insert(key.into(), value.into());
123 self
124 }
125}
126
127impl Masks {
128 pub fn new(width: usize, height: usize) -> Self {
129 Self {
130 walkable: vec![vec![false; width]; height],
131 no_spawn: vec![vec![false; width]; height],
132 width,
133 height,
134 }
135 }
136
137 pub fn from_tiles(tiles: &Grid<Tile>) -> Self {
138 let mut masks = Self::new(tiles.width(), tiles.height());
139
140 for y in 0..tiles.height() {
141 for x in 0..tiles.width() {
142 let walkable = tiles.get(x as i32, y as i32).map_or(false, |t| t.is_floor());
143 masks.walkable[y][x] = walkable;
144 }
145 }
146
147 masks
148 }
149}
150
151impl ConnectivityGraph {
152 pub fn new() -> Self {
153 Self {
154 regions: Vec::new(),
155 edges: Vec::new(),
156 }
157 }
158
159 pub fn add_region(&mut self, id: u32) {
160 if !self.regions.contains(&id) {
161 self.regions.push(id);
162 }
163 }
164
165 pub fn add_edge(&mut self, from: u32, to: u32) {
166 self.add_region(from);
167 self.add_region(to);
168
169 if !self.edges.contains(&(from, to)) && !self.edges.contains(&(to, from)) {
170 self.edges.push((from, to));
171 }
172 }
173}
174
175impl Default for ConnectivityGraph {
176 fn default() -> Self {
177 Self::new()
178 }
179}
180
181pub mod placement {
183 use super::*;
184 use crate::effects;
185
186 pub fn distribute_markers(
188 regions: &[Region],
189 tag: &str,
190 total: usize,
191 rng: &mut Rng
192 ) -> Vec<Marker> {
193 if regions.is_empty() || total == 0 {
194 return Vec::new();
195 }
196
197 let mut markers = Vec::new();
198 let total_area: usize = regions.iter().map(|r| r.area()).sum();
199
200 for region in regions {
201 let region_markers = if total_area > 0 {
202 (total * region.area()) / total_area
203 } else {
204 total / regions.len()
205 };
206
207 for _ in 0..region_markers {
208 if let Some(&(x, y)) = region.cells.get(rng.range_usize(0, region.cells.len())) {
209 markers.push(
210 Marker::new(x, y, tag)
211 .with_region(region.id)
212 );
213 }
214 }
215 }
216
217 markers
218 }
219
220 pub fn extract_regions(grid: &Grid<Tile>) -> Vec<Region> {
222 let (labels, count) = effects::label_regions(grid);
223 let mut regions = Vec::new();
224
225 for region_id in 1..=count {
226 let mut region = Region::new(region_id, "unknown");
227
228 for y in 0..grid.height() {
229 for x in 0..grid.width() {
230 let idx = y * grid.width() + x;
231 if labels.get(idx).copied().unwrap_or(0) == region_id {
232 region.add_cell(x as u32, y as u32);
233 }
234 }
235 }
236
237 if !region.cells.is_empty() {
238 regions.push(region);
239 }
240 }
241
242 regions
243 }
244
245 pub fn build_connectivity(grid: &Grid<Tile>, regions: &[Region]) -> ConnectivityGraph {
247 let mut graph = ConnectivityGraph::new();
248
249 for region in regions {
251 graph.add_region(region.id);
252 }
253
254 for i in 0..regions.len() {
256 for j in (i + 1)..regions.len() {
257 if are_regions_adjacent(grid, ®ions[i], ®ions[j]) {
258 graph.add_edge(regions[i].id, regions[j].id);
259 }
260 }
261 }
262
263 graph
264 }
265
266 fn are_regions_adjacent(_grid: &Grid<Tile>, region1: &Region, region2: &Region) -> bool {
268 for &(x1, y1) in ®ion1.cells {
269 for &(x2, y2) in ®ion2.cells {
270 let dx = (x1 as i32 - x2 as i32).abs();
271 let dy = (y1 as i32 - y2 as i32).abs();
272
273 if (dx == 1 && dy == 0) || (dx == 0 && dy == 1) {
275 return true;
276 }
277 }
278 }
279 false
280 }
281}