1use crate::{Grid, Tile};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct SemanticConfig {
12 pub size_thresholds: Vec<(usize, String)>,
14 pub marker_types: Vec<(String, f32)>,
16 pub max_markers_per_region: usize,
18 pub marker_scaling_factor: f32,
20 pub connectivity_type: ConnectivityType,
22 pub region_analysis: RegionAnalysisConfig,
24 pub marker_placement: MarkerPlacementConfig,
26}
27
28#[derive(Debug, Clone)]
30pub enum ConnectivityType {
31 FourConnected,
33 EightConnected,
35}
36
37#[derive(Debug, Clone)]
39pub struct RegionAnalysisConfig {
40 pub analyze_shape: bool,
42 pub analyze_connectivity_patterns: bool,
44 pub min_analysis_size: usize,
46}
47
48#[derive(Debug, Clone)]
50pub struct MarkerPlacementConfig {
51 pub strategy: PlacementStrategy,
53 pub min_marker_distance: usize,
55 pub avoid_walls: bool,
57}
58
59#[derive(Debug, Clone)]
61pub enum PlacementStrategy {
62 Random,
64 Center,
66 Edges,
68 Corners,
70}
71
72impl SemanticConfig {
73 pub fn cave_system() -> Self {
75 Self {
76 size_thresholds: vec![
77 (80, "Chamber".to_string()),
78 (25, "Tunnel".to_string()),
79 (5, "Alcove".to_string()),
80 (0, "Crevice".to_string()),
81 ],
82 marker_types: vec![
83 ("PlayerStart".to_string(), 1.0),
84 ("Exit".to_string(), 0.8),
85 ("Treasure".to_string(), 0.4),
86 ("Enemy".to_string(), 0.6),
87 ("Crystal".to_string(), 0.2),
88 ],
89 max_markers_per_region: 2,
90 marker_scaling_factor: 80.0, connectivity_type: ConnectivityType::EightConnected, region_analysis: RegionAnalysisConfig {
93 analyze_shape: true, analyze_connectivity_patterns: true,
95 min_analysis_size: 15,
96 },
97 marker_placement: MarkerPlacementConfig {
98 strategy: PlacementStrategy::Random,
99 min_marker_distance: 5,
100 avoid_walls: true,
101 },
102 }
103 }
104
105 pub fn room_system() -> Self {
107 Self {
108 size_thresholds: vec![
109 (150, "Hall".to_string()),
110 (50, "Room".to_string()),
111 (15, "Chamber".to_string()),
112 (0, "Closet".to_string()),
113 ],
114 marker_types: vec![
115 ("PlayerStart".to_string(), 1.0),
116 ("Exit".to_string(), 1.0),
117 ("Treasure".to_string(), 0.3),
118 ("Enemy".to_string(), 0.4),
119 ("Furniture".to_string(), 0.7),
120 ],
121 max_markers_per_region: 4,
122 marker_scaling_factor: 60.0, connectivity_type: ConnectivityType::FourConnected, region_analysis: RegionAnalysisConfig {
125 analyze_shape: true, analyze_connectivity_patterns: false,
127 min_analysis_size: 8,
128 },
129 marker_placement: MarkerPlacementConfig {
130 strategy: PlacementStrategy::Center, min_marker_distance: 4,
132 avoid_walls: true,
133 },
134 }
135 }
136
137 pub fn maze_system() -> Self {
139 Self {
140 size_thresholds: vec![
141 (50, "Junction".to_string()),
142 (10, "Corridor".to_string()),
143 (0, "DeadEnd".to_string()),
144 ],
145 marker_types: vec![
146 ("PlayerStart".to_string(), 1.0),
147 ("Exit".to_string(), 1.0),
148 ("Treasure".to_string(), 0.1),
149 ("Trap".to_string(), 0.3),
150 ],
151 max_markers_per_region: 1,
152 marker_scaling_factor: 30.0, connectivity_type: ConnectivityType::FourConnected, region_analysis: RegionAnalysisConfig {
155 analyze_shape: false,
156 analyze_connectivity_patterns: true, min_analysis_size: 5,
158 },
159 marker_placement: MarkerPlacementConfig {
160 strategy: PlacementStrategy::Corners, min_marker_distance: 8,
162 avoid_walls: false, },
164 }
165 }
166}
167
168impl Default for SemanticConfig {
169 fn default() -> Self {
170 Self {
171 size_thresholds: vec![
172 (100, "Large".to_string()),
173 (25, "Medium".to_string()),
174 (5, "Small".to_string()),
175 (0, "Tiny".to_string()),
176 ],
177 marker_types: vec![
178 ("PlayerStart".to_string(), 1.0),
179 ("Exit".to_string(), 1.0),
180 ("Treasure".to_string(), 0.3),
181 ("Enemy".to_string(), 0.5),
182 ],
183 max_markers_per_region: 3,
184 marker_scaling_factor: 100.0,
185 connectivity_type: ConnectivityType::FourConnected,
186 region_analysis: RegionAnalysisConfig {
187 analyze_shape: false,
188 analyze_connectivity_patterns: false,
189 min_analysis_size: 10,
190 },
191 marker_placement: MarkerPlacementConfig {
192 strategy: PlacementStrategy::Random,
193 min_marker_distance: 3,
194 avoid_walls: true,
195 },
196 }
197 }
198}
199
200#[derive(Debug, Clone)]
202pub struct Region {
203 pub id: u32,
204 pub kind: String,
205 pub cells: Vec<(u32, u32)>,
206 pub tags: Vec<String>,
207}
208
209#[derive(Debug, Clone)]
211pub struct Marker {
212 pub x: u32,
213 pub y: u32,
214 pub tag: String,
215 pub weight: f32,
216 pub region_id: Option<u32>,
217 pub metadata: HashMap<String, String>,
218}
219
220#[derive(Debug, Clone)]
222pub struct Masks {
223 pub walkable: Vec<Vec<bool>>,
224 pub no_spawn: Vec<Vec<bool>>,
225 pub width: usize,
226 pub height: usize,
227}
228
229#[derive(Debug, Clone)]
231pub struct ConnectivityGraph {
232 pub regions: Vec<u32>,
233 pub edges: Vec<(u32, u32)>,
234}
235
236#[derive(Debug, Clone)]
238pub struct SemanticLayers {
239 pub regions: Vec<Region>,
240 pub markers: Vec<Marker>,
241 pub masks: Masks,
242 pub connectivity: ConnectivityGraph,
243}
244
245impl Region {
246 pub fn new(id: u32, kind: impl Into<String>) -> Self {
247 Self {
248 id,
249 kind: kind.into(),
250 cells: Vec::new(),
251 tags: Vec::new(),
252 }
253 }
254
255 pub fn add_cell(&mut self, x: u32, y: u32) {
256 self.cells.push((x, y));
257 }
258
259 pub fn add_tag(&mut self, tag: impl Into<String>) {
260 self.tags.push(tag.into());
261 }
262
263 pub fn area(&self) -> usize {
264 self.cells.len()
265 }
266}
267
268impl Marker {
269 pub fn new(x: u32, y: u32, tag: impl Into<String>) -> Self {
270 Self {
271 x,
272 y,
273 tag: tag.into(),
274 weight: 1.0,
275 region_id: None,
276 metadata: HashMap::new(),
277 }
278 }
279
280 pub fn with_weight(mut self, weight: f32) -> Self {
281 self.weight = weight;
282 self
283 }
284
285 pub fn with_region(mut self, region_id: u32) -> Self {
286 self.region_id = Some(region_id);
287 self
288 }
289
290 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
291 self.metadata.insert(key.into(), value.into());
292 self
293 }
294}
295
296impl Masks {
297 pub fn new(width: usize, height: usize) -> Self {
298 Self {
299 walkable: vec![vec![false; width]; height],
300 no_spawn: vec![vec![false; width]; height],
301 width,
302 height,
303 }
304 }
305
306 pub fn from_tiles(tiles: &Grid<Tile>) -> Self {
307 let mut masks = Self::new(tiles.width(), tiles.height());
308
309 for y in 0..tiles.height() {
310 for x in 0..tiles.width() {
311 let walkable = tiles.get(x as i32, y as i32).is_some_and(|t| t.is_floor());
312 masks.walkable[y][x] = walkable;
313 }
314 }
315
316 masks
317 }
318}
319
320impl ConnectivityGraph {
321 pub fn new() -> Self {
322 Self {
323 regions: Vec::new(),
324 edges: Vec::new(),
325 }
326 }
327
328 pub fn add_region(&mut self, id: u32) {
329 if !self.regions.contains(&id) {
330 self.regions.push(id);
331 }
332 }
333
334 pub fn add_edge(&mut self, from: u32, to: u32) {
335 self.add_region(from);
336 self.add_region(to);
337
338 if !self.edges.contains(&(from, to)) && !self.edges.contains(&(to, from)) {
339 self.edges.push((from, to));
340 }
341 }
342}
343
344impl Default for ConnectivityGraph {
345 fn default() -> Self {
346 Self::new()
347 }
348}