1pub mod economy;
2mod generation;
3pub mod genstat;
4pub mod namegen;
5pub mod plot;
6mod tile;
7pub mod util;
8
9use self::tile::{HazardKind, KeepKind, RoofKind, TILE_SIZE, Tile, TileGrid};
10pub use self::{
11 economy::Economy,
12 generation::{Fill, Painter, Primitive, PrimitiveRef, Structure, aabr_with_z},
13 genstat::{GenStatPlotKind, GenStatSiteKind, SitesGenMeta},
14 plot::{Plot, PlotKind, foreach_plot},
15 tile::TileKind,
16};
17use crate::{
18 Canvas, IndexRef, Land,
19 config::CONFIG,
20 sim::Path,
21 util::{CARDINALS, DHashSet, Grid, SQUARE_4, SQUARE_9, attempt},
22};
23use common::{
24 astar::Astar,
25 calendar::Calendar,
26 comp::Alignment,
27 generation::{EntityInfo, EntitySpawn},
28 lottery::Lottery,
29 map::MarkerKind,
30 spiral::Spiral2d,
31 store::{Id, Store},
32 terrain::{
33 Block, BlockKind, SiteKindMeta, SpriteKind, TerrainChunkSize,
34 site::{DungeonKindMeta, SettlementKindMeta},
35 },
36 util::Dir2,
37 vol::RectVolSize,
38};
39use hashbrown::DefaultHashBuilder;
40use namegen::NameGen;
41use rand::{SeedableRng, prelude::*, seq::IndexedRandom};
42use rand_chacha::{ChaCha8Rng, ChaChaRng};
43use std::ops::Range;
44use vek::*;
45
46fn reseed<R: Rng>(rng: &mut R) -> impl Rng + use<R> {
54 ChaChaRng::from_seed(rng.random::<[u8; 32]>())
55}
56
57pub struct SpawnRules {
58 pub trees: bool,
59 pub max_warp: f32,
60 pub paths: bool,
61 pub waypoints: bool,
62 pub preferred_alt: (f32, f32, f32),
66}
67
68impl SpawnRules {
69 pub fn prefer_alt(&mut self, alt: f32, weight: f32) {
79 self.preferred_alt.0 += alt * weight;
80 self.preferred_alt.1 += weight;
81 self.preferred_alt.2 = self.preferred_alt.2.max(weight);
82 }
83
84 pub fn get_preferred_alt(&self) -> (f32, f32) {
86 (
88 self.preferred_alt.0 / self.preferred_alt.1.max(0.0001) + 0.1,
89 self.preferred_alt.2,
90 )
91 }
92}
93
94impl Default for SpawnRules {
95 fn default() -> Self {
96 Self {
97 trees: true,
98 max_warp: 1.0,
99 paths: true,
100 waypoints: true,
101 preferred_alt: (0.0, 0.0, f32::NEG_INFINITY),
102 }
103 }
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum SiteKind {
108 Refactor,
109 CliffTown,
110 SavannahTown,
111 DesertCity,
112 ChapelSite,
113 DwarvenMine,
114 CoastalTown,
115 Citadel,
116 Terracotta,
117 GiantTree,
118 Gnarling,
119 Bridge(Vec2<i32>, Vec2<i32>),
120 Adlet,
121 Haniwa,
122 PirateHideout,
123 JungleRuin,
124 RockCircle,
125 TrollCave,
126 Camp,
127 Cultist,
128 Sahagin,
129 VampireCastle,
130 GliderCourse,
131 Myrmidon,
132}
133
134impl SiteKind {
135 pub fn meta(&self) -> Option<SiteKindMeta> {
136 match self {
137 SiteKind::Refactor => Some(SiteKindMeta::Settlement(SettlementKindMeta::Default)),
138 SiteKind::CliffTown => Some(SiteKindMeta::Settlement(SettlementKindMeta::CliffTown)),
139 SiteKind::SavannahTown => {
140 Some(SiteKindMeta::Settlement(SettlementKindMeta::SavannahTown))
141 },
142 SiteKind::CoastalTown => {
143 Some(SiteKindMeta::Settlement(SettlementKindMeta::CoastalTown))
144 },
145 SiteKind::DesertCity => Some(SiteKindMeta::Settlement(SettlementKindMeta::DesertCity)),
146 SiteKind::Gnarling => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Gnarling)),
147 SiteKind::Adlet => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Adlet)),
148 SiteKind::Terracotta => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Terracotta)),
149 SiteKind::Haniwa => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Haniwa)),
150 SiteKind::Myrmidon => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Myrmidon)),
151 SiteKind::DwarvenMine => Some(SiteKindMeta::Dungeon(DungeonKindMeta::DwarvenMine)),
152 SiteKind::ChapelSite => Some(SiteKindMeta::Dungeon(DungeonKindMeta::SeaChapel)),
153 SiteKind::Cultist => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Cultist)),
154 SiteKind::Sahagin => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Sahagin)),
155 SiteKind::VampireCastle => Some(SiteKindMeta::Dungeon(DungeonKindMeta::VampireCastle)),
156
157 _ => None,
158 }
159 }
160
161 pub fn should_do_economic_simulation(&self) -> bool {
162 matches!(
163 self,
164 SiteKind::Refactor
165 | SiteKind::CliffTown
166 | SiteKind::SavannahTown
167 | SiteKind::CoastalTown
168 | SiteKind::DesertCity
169 )
170 }
171
172 pub fn marker(&self) -> Option<MarkerKind> {
173 match self {
174 SiteKind::Refactor
175 | SiteKind::CliffTown
176 | SiteKind::SavannahTown
177 | SiteKind::CoastalTown
178 | SiteKind::DesertCity => Some(MarkerKind::Town),
179 SiteKind::Citadel => Some(MarkerKind::Castle),
180 SiteKind::Bridge(_, _) => Some(MarkerKind::Bridge),
181 SiteKind::GiantTree => Some(MarkerKind::Tree),
182 SiteKind::Gnarling => Some(MarkerKind::Gnarling),
183 SiteKind::DwarvenMine => Some(MarkerKind::DwarvenMine),
184 SiteKind::ChapelSite => Some(MarkerKind::ChapelSite),
185 SiteKind::Terracotta => Some(MarkerKind::Terracotta),
186 SiteKind::GliderCourse => Some(MarkerKind::GliderCourse),
187 SiteKind::Cultist => Some(MarkerKind::Cultist),
188 SiteKind::Sahagin => Some(MarkerKind::Sahagin),
189 SiteKind::Myrmidon => Some(MarkerKind::Myrmidon),
190 SiteKind::Adlet => Some(MarkerKind::Adlet),
191 SiteKind::Haniwa => Some(MarkerKind::Haniwa),
192 SiteKind::VampireCastle => Some(MarkerKind::VampireCastle),
193
194 SiteKind::PirateHideout
195 | SiteKind::JungleRuin
196 | SiteKind::RockCircle
197 | SiteKind::TrollCave
198 | SiteKind::Camp => None,
199 }
200 }
201}
202
203#[derive(Default)]
204pub struct Site {
205 pub origin: Vec2<i32>,
206 name: Option<String>,
207 pub tiles: TileGrid,
209 pub plots: Store<Plot>,
210 pub plazas: Vec<Id<Plot>>,
211 pub roads: Vec<Id<Plot>>,
212 pub economy: Option<Box<Economy>>,
213 pub kind: Option<SiteKind>,
214}
215
216impl Site {
217 pub fn filter_plots<'a, F: FnMut(&'a Plot) -> bool>(
218 &'a self,
219 mut f: F,
220 ) -> std::iter::Filter<impl ExactSizeIterator<Item = &'a Plot>, impl FnMut(&&'a Plot) -> bool>
221 {
222 self.plots.values().filter(move |p| f(p))
223 }
224
225 pub fn any_plot<F: FnMut(&Plot) -> bool>(&self, f: F) -> bool { self.plots.values().any(f) }
226
227 pub fn meta(&self) -> Option<SiteKindMeta> { self.kind.and_then(|s| s.meta()) }
228
229 pub fn economy_mut(&mut self) -> &mut Economy { self.economy.get_or_insert_default() }
230
231 pub fn do_economic_simulation(&self) -> bool {
232 self.kind.is_some_and(|s| s.should_do_economic_simulation())
233 }
234
235 pub fn trade_information(&self, id: Id<Site>) -> Option<common::trade::SiteInformation> {
236 self.economy
237 .as_ref()
238 .map(|econ| common::trade::SiteInformation {
239 id: id.id(),
240 unconsumed_stock: econ.get_available_stock(),
241 })
242 }
243
244 pub fn radius(&self) -> f32 {
245 ((self
246 .tiles
247 .bounds
248 .min
249 .map(|e| e.abs())
250 .reduce_max()
251 .max(self.tiles.bounds.max.map(|e| e.abs()).reduce_max())
252 + if self
256 .plots
257 .values()
258 .any(|p| matches!(&p.kind, PlotKind::GiantTree(_)))
259 {
260 25
262 } else {
263 5
264 })
265 * TILE_SIZE as i32) as f32
266 }
267
268 pub fn spawn_rules(&self, spawn_rules: &mut SpawnRules, land: &Land, wpos: Vec2<i32>) {
269 let tile_pos = self.wpos_tile_pos(wpos);
270 let max_warp = SQUARE_9
271 .iter()
272 .filter_map(|rpos| {
273 let tile_pos = tile_pos + rpos;
274 if self.tiles.get(tile_pos).is_natural() {
275 None
276 } else {
277 let clamped =
278 wpos.clamped(self.tile_wpos(tile_pos), self.tile_wpos(tile_pos + 1) - 1);
279 Some(clamped.as_::<f32>().distance_squared(wpos.as_::<f32>()))
280 }
281 })
282 .min_by_key(|d2| *d2 as i32)
283 .map(|d2| d2.sqrt() / TILE_SIZE as f32)
284 .unwrap_or(1.0);
285 SQUARE_9.iter().for_each(|rpos| {
286 let tile_pos = tile_pos + rpos;
287 let tile = self.tiles.get(tile_pos);
288 let clamped = wpos.clamped(self.tile_wpos(tile_pos), self.tile_wpos(tile_pos + 1) - 1);
289 let dist = clamped.as_::<f32>().distance(wpos.as_::<f32>()) / TILE_SIZE as f32;
290 let weight = (1.0 - dist).clamped(0.001, 1.0);
291 if let TileKind::Road { alt, .. } = &tile.kind {
294 spawn_rules.prefer_alt(*alt, weight.powi(4) * 25.0);
295 } else if let TileKind::Path { closest_pos, path } = &tile.kind
296 && let Some(hard_alt) = tile.hard_alt
297 {
298 spawn_rules.prefer_alt(
299 hard_alt as f32,
300 (1.0 - (closest_pos.distance(wpos.as_()) - path.width * 1.25).max(0.0) * 0.15)
301 .clamped(0.001, 1.0)
302 .powi(2),
303 );
304 } else if let Some(plot) = tile.plot {
305 self.plot(plot).spawn_rules(spawn_rules, land, wpos, weight);
306 }
307 });
308
309 spawn_rules.trees &= max_warp == 1.0;
310 spawn_rules.max_warp = spawn_rules.max_warp.min(max_warp);
311 }
312
313 pub fn bounds(&self) -> Aabr<i32> {
314 let border = 1;
315 Aabr {
316 min: self.tile_wpos(self.tiles.bounds.min - border),
317 max: self.tile_wpos(self.tiles.bounds.max + 1 + border),
318 }
319 }
320
321 pub fn plot(&self, id: Id<Plot>) -> &Plot { &self.plots[id] }
322
323 pub fn plots(&self) -> impl ExactSizeIterator<Item = &Plot> + '_ { self.plots.values() }
324
325 pub fn plazas(&self) -> impl ExactSizeIterator<Item = Id<Plot>> + '_ {
326 self.plazas.iter().copied()
327 }
328
329 pub fn create_plot(&mut self, plot: Plot) -> Id<Plot> { self.plots.insert(plot) }
330
331 pub fn blit_aabr(&mut self, aabr: Aabr<i32>, tile: Tile) {
332 for y in 0..aabr.size().h {
333 for x in 0..aabr.size().w {
334 self.tiles.set(aabr.min + Vec2::new(x, y), tile.clone());
335 }
336 }
337 }
338
339 pub fn create_road(
340 &mut self,
341 land: &Land,
342 a: Vec2<i32>,
343 b: Vec2<i32>,
344 w: u16,
345 kind: plot::RoadKind,
346 (src, src_alt): (Id<Plot>, f32),
347 (dst, dst_alt): (Id<Plot>, f32),
348 ) {
349 const MAX_ITERS: usize = 4096;
350 let range = &(-(w as i32) / 2..w as i32 - (w as i32 + 1) / 2);
351 let heuristic =
353 |(tile, _): &(Vec2<i32>, Vec2<i32>)| (tile - b).map(|e| e.abs()).sum() as f32;
354 let Some((path, _cost)) =
355 Astar::new(MAX_ITERS, (a, Vec2::zero()), DefaultHashBuilder::default())
356 .poll(
357 MAX_ITERS,
358 &heuristic,
359 |(tile, prev_dir)| {
360 let tile = *tile;
361 let prev_dir = *prev_dir;
362 let this = &self;
363 CARDINALS.iter().map(move |dir| {
364 let neighbor = (tile + *dir, *dir);
365
366 let alt_a = land.get_alt_approx(this.tile_center_wpos(tile));
368 let alt_b = land.get_alt_approx(this.tile_center_wpos(neighbor.0));
369 let mut cost = 1.0
370 + (alt_a - alt_b).abs() / TILE_SIZE as f32
371 + (prev_dir != *dir) as i32 as f32;
372
373 for i in range.clone() {
374 let orth = dir.yx() * i;
375 let tile = this.tiles.get(neighbor.0 + orth);
376 if tile.is_obstacle() {
377 cost += 1000.0;
378 } else if !tile.is_empty() && !tile.is_road() {
379 cost += 25.0;
380 }
381 }
382
383 (neighbor, cost)
384 })
385 },
386 |(tile, _)| self.tiles.get(*tile).plot == Some(dst),
387 )
388 .into_path()
389 else {
390 return;
391 };
392
393 let mut path = path.nodes();
395 while let Some((tile, _)) = path.first()
396 && self.tiles.get(*tile).plot == Some(src)
397 {
398 path = &path[1..];
399 }
400 let Some((a, _)) = path.first().copied() else {
401 return;
402 };
403
404 let plot = self.create_plot(Plot {
411 kind: PlotKind::Road(plot::Road {
412 path: path.iter().map(|(tile, _)| *tile).collect(),
413 kind,
414 }),
415 root_tile: a,
416 tiles: path.iter().map(|(tile, _)| *tile).collect(),
417 });
418
419 self.roads.push(plot);
420
421 for (i, (tile, _)) in path.iter().enumerate() {
422 for y in range.clone() {
423 for x in range.clone() {
424 let tile = tile + Vec2::new(x, y);
425 let old_tile = self.tiles.get(tile);
426 if matches!(
427 old_tile.kind,
428 TileKind::Empty | TileKind::Path { .. } ) {
430 self.tiles.set(tile, Tile {
431 kind: TileKind::Road {
432 a: i.saturating_sub(1) as u16,
433 b: (i + 1).min(path.len() - 1) as u16,
434 w,
435 alt: land.get_alt_approx(self.tile_center_wpos(tile))
436 .clamp(
438 src_alt - i as f32 * tile::TILE_SIZE as f32,
439 src_alt + i as f32 * tile::TILE_SIZE as f32,
440 )
441 .clamp(
442 dst_alt - path.len().saturating_sub(i + 1) as f32 * tile::TILE_SIZE as f32,
443 dst_alt + path.len().saturating_sub(i + 1) as f32 * tile::TILE_SIZE as f32,
444 ),},
446 plot: old_tile.plot.or(Some(plot)),
447 hard_alt: Some(land.get_alt_approx(self.tile_center_wpos(tile)) as i32),
448 });
449 }
450 }
451 }
452 }
453 }
454
455 pub fn find_aabr(
456 &mut self,
457 search_pos: Vec2<i32>,
458 area_range: Range<u32>,
459 min_dims: Extent2<u32>,
460 ) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>, Option<i32>)> {
461 let ((aabr, (door_dir, hard_alt)), door_pos) =
462 self.tiles.find_near(search_pos, |center, _| {
463 let dir = CARDINALS
464 .iter()
465 .find(|dir| self.tiles.get(center + *dir).is_road())?;
466 let hard_alt = self.tiles.get(center + *dir).hard_alt.or(self
467 .tiles
468 .get(center + *dir)
469 .plot
470 .and_then(|plot| {
471 if let PlotKind::Plaza(p) = self.plots.get(plot).kind() {
472 Some(p.hard_alt.unwrap_or(p.alt))
473 } else {
474 None
475 }
476 }));
477 self.tiles
478 .grow_aabr(center, area_range.clone(), min_dims)
479 .ok()
480 .zip(Some((*dir, hard_alt)))
481 })?;
482 Some((aabr, door_pos, door_dir, hard_alt))
483 }
484
485 pub fn find_roadside_aabr(
486 &mut self,
487 rng: &mut impl Rng,
488 area_range: Range<u32>,
489 min_dims: Extent2<u32>,
490 ) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>, Option<i32>)> {
491 let dir = Vec2::<f32>::zero()
492 .map(|_| rng.random_range(-1.0..1.0))
493 .normalized();
494 let search_pos = if rng.random() {
495 let plot = self.plot(*self.plazas.choose(rng)?);
496 let sz = plot.find_bounds().size();
497 plot.root_tile + dir.map(|e: f32| e.round() as i32) * (sz + 1)
498 } else if let PlotKind::Road(plot::Road { path, .. }) =
499 &self.plot(*self.roads.choose(rng)?).kind
500 {
501 *path.nodes().choose(rng)? + (dir * 1.0).map(|e: f32| e.round() as i32)
502 } else {
503 return None;
504 };
505
506 let (aabr, door_pos, door_dir, hard_alt) =
507 self.find_aabr(search_pos, area_range, min_dims)?;
508
509 let alt = if let TileKind::Road { alt, .. } = &self.tiles.get(door_pos + door_dir).kind {
510 Some(*alt as i32 - 1)
511 } else {
512 None
513 };
514
515 Some((aabr, door_pos, door_dir, hard_alt.or(alt)))
516 }
517
518 pub fn find_rural_aabr(
519 &mut self,
520 rng: &mut impl Rng,
521 area_range: Range<u32>,
522 min_dims: Extent2<u32>,
523 ) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>, Option<i32>)> {
524 let search_center = self
526 .plazas
527 .choose(rng)
528 .map(|&p| self.plot(p).root_tile)
529 .unwrap_or_default();
530
531 let dir = Vec2::<f32>::zero()
533 .map(|_| rng.random_range(-1.0..1.0))
534 .normalized();
535 let search_offset = dir.map2(min_dims.into(), |e: f32, sz: u32| {
536 (e * sz as f32 * 0.75 + 10.0).round() as i32
537 });
538
539 self.find_aabr(search_center + search_offset, area_range, min_dims)
540 }
541
542 pub fn make_plaza_at(
543 &mut self,
544 land: &Land,
545 index: IndexRef,
546 tile_aabr: Aabr<i32>,
547 rng: &mut impl Rng,
548 road_kind: plot::RoadKind,
549 ) -> Option<Id<Plot>> {
550 let tpos = tile_aabr.center();
551 let plaza_alt = land.get_alt_approx(self.tile_center_wpos(tpos)) as i32;
552
553 let plaza = self.create_plot(Plot {
554 kind: PlotKind::Plaza(plot::Plaza::generate(
555 tile_aabr, road_kind, self, land, index, rng,
556 )),
557 root_tile: tpos,
558 tiles: aabr_tiles(tile_aabr).collect(),
559 });
560 self.plazas.push(plaza);
561 self.blit_aabr(tile_aabr, Tile {
562 kind: TileKind::Road {
563 a: 0,
564 b: 0,
565 w: 0,
566 alt: plaza_alt as f32,
567 },
568 plot: Some(plaza),
569 hard_alt: Some(plaza_alt),
570 });
571
572 let mut already_pathed = vec![];
573 for _ in (0..rng.random_range(1.25..2.25) as u16).rev() {
575 if let Some(&p) = self
576 .plazas
577 .iter()
578 .filter(|&&p| {
579 !already_pathed.contains(&p)
580 && p != plaza
581 && already_pathed.iter().all(|&ap| {
582 (self.plot(ap).root_tile - tpos)
583 .map(|e| e as f32)
584 .normalized()
585 .dot(
586 (self.plot(p).root_tile - tpos)
587 .map(|e| e as f32)
588 .normalized(),
589 )
590 < 0.0
591 })
592 })
593 .min_by_key(|&&p| self.plot(p).root_tile.distance_squared(tpos))
594 && let PlotKind::Plaza(src_plaza) = &self.plot(p).kind
595 && let PlotKind::Plaza(dst_plaza) = &self.plot(plaza).kind
596 {
597 self.create_road(
598 land,
599 self.plot(p).root_tile,
600 tpos,
601 2, road_kind,
603 (p, src_plaza.alt as f32),
604 (plaza, dst_plaza.alt as f32),
605 );
606 already_pathed.push(p);
607 } else {
608 break;
609 }
610 }
611
612 Some(plaza)
613 }
614
615 pub fn make_plaza(
616 &mut self,
617 land: &Land,
618 index: IndexRef,
619 rng: &mut impl Rng,
620 generator_stats: &mut SitesGenMeta,
621 site_name: &str,
622 road_kind: plot::RoadKind,
623 ) -> Option<Id<Plot>> {
624 generator_stats.attempt(site_name, GenStatPlotKind::Plaza);
625 let plaza_radius = rng.random_range(1..3);
626 let plaza_dist = 6.5 + plaza_radius as f32 * 3.0;
627 let aabr = attempt(32, || {
628 self.plazas
629 .choose(rng)
630 .map(|&p| {
631 self.plot(p).root_tile
632 + (Vec2::new(rng.random_range(-1.0..1.0), rng.random_range(-1.0..1.0))
633 .normalized()
634 * plaza_dist)
635 .map(|e| e as i32)
636 })
637 .or_else(|| Some(Vec2::zero()))
638 .map(|center_tile| Aabr {
639 min: center_tile + Vec2::broadcast(-plaza_radius),
640 max: center_tile + Vec2::broadcast(plaza_radius + 1),
641 })
642 .filter(|&aabr| {
643 rng.random_range(0..48) > aabr.center().map(|e| e.abs()).reduce_max()
644 && aabr_tiles(aabr).all(|tile| !self.tiles.get(tile).is_obstacle())
645 })
646 .filter(|&aabr| {
647 self.plazas.iter().all(|&p| {
648 let dist_sqr = if let PlotKind::Plaza(plaza) = &self.plot(p).kind {
649 let intersection = plaza.aabr.intersection(aabr);
650 intersection
653 .size()
654 .map(|e| e.min(0) as f32)
655 .magnitude_squared()
656 } else {
657 let r = self.plot(p).root_tile();
658 let closest_point = aabr.projected_point(r);
659 closest_point.as_::<f32>().distance_squared(r.as_::<f32>())
660 };
661 dist_sqr > (plaza_dist * 0.85).powi(2)
662 })
663 })
664 })?;
665 generator_stats.success(site_name, GenStatPlotKind::Plaza);
666 self.make_plaza_at(land, index, aabr, rng, road_kind)
667 }
668
669 pub fn demarcate_obstacles(&mut self, land: &Land) {
670 const SEARCH_RADIUS: u32 = 96;
671
672 Spiral2d::new()
673 .take((SEARCH_RADIUS * 2 + 1).pow(2) as usize)
674 .for_each(|tile| {
675 let wpos = self.tile_center_wpos(tile);
676 if let Some(kind) = Spiral2d::new()
677 .take(9)
678 .find_map(|rpos| wpos_is_hazard(land, wpos + rpos))
679 {
680 for &rpos in &SQUARE_4 {
681 self.tiles
683 .get_mut(tile - rpos - 1)
684 .filter(|tile| tile.is_natural())
685 .map(|tile| tile.kind = TileKind::Hazard(kind));
686 }
687 }
688 if let Some((_, path_wpos, path, _)) = land.get_nearest_path(wpos) {
689 let tile_aabr = Aabr {
690 min: self.tile_wpos(tile),
691 max: self.tile_wpos(tile + 1) - 1,
692 };
693
694 if tile_aabr
695 .projected_point(path_wpos.as_())
696 .as_()
697 .distance_squared(path_wpos)
698 < path.width.powi(2)
699 {
700 self.tiles
701 .get_mut(tile)
702 .filter(|tile| tile.is_natural())
703 .map(|tile| {
704 tile.kind = TileKind::Path {
705 closest_pos: path_wpos,
706 path,
707 };
708 tile.hard_alt = Some(land.get_alt_approx(path_wpos.as_()) as i32);
709 });
710 }
711 }
712 });
713 }
714
715 pub fn make_initial_plaza(
732 &mut self,
733 land: &Land,
734 index: IndexRef,
735 rng: &mut impl Rng,
736 plaza_radius: u32,
737 search_inner_radius: u32,
738 search_width: u32,
739 generator_stats: &mut SitesGenMeta,
740 site_name: &str,
741 road_kind: plot::RoadKind,
742 ) -> Option<Id<Plot>> {
743 generator_stats.attempt(site_name, GenStatPlotKind::InitialPlaza);
744 let mut plaza_locations = vec![];
746 Spiral2d::with_ring(search_inner_radius, search_width).for_each(|tpos| {
748 let aabr = Aabr {
749 min: tpos - Vec2::broadcast(plaza_radius as i32),
750 max: tpos + Vec2::broadcast(plaza_radius as i32 + 1),
751 };
752 if aabr_tiles(aabr).all(|tpos| self.tiles.get(tpos).is_empty()) {
755 plaza_locations.push(aabr);
756 }
757 });
758 if plaza_locations.is_empty() {
759 self.make_plaza(land, index, rng, generator_stats, site_name, road_kind)
763 } else {
764 plaza_locations.sort_by_key(|&aabr| {
766 aabr.min
767 .map2(aabr.max, |a, b| a.abs().min(b.abs()))
768 .magnitude_squared()
769 });
770 let aabr = plaza_locations.first()?;
772 generator_stats.success(site_name, GenStatPlotKind::InitialPlaza);
773 self.make_plaza_at(land, index, *aabr, rng, road_kind)
774 }
775 }
776
777 pub fn make_initial_plaza_default(
797 &mut self,
798 land: &Land,
799 index: IndexRef,
800 rng: &mut impl Rng,
801 generator_stats: &mut SitesGenMeta,
802 site_name: &str,
803 road_kind: plot::RoadKind,
804 ) -> Option<Id<Plot>> {
805 let plaza_radius = rng.random_range(1..3);
807 let search_inner_radius = 7 + plaza_radius;
811 const PLAZA_MAX_SEARCH_RADIUS: u32 = 24;
812 self.make_initial_plaza(
813 land,
814 index,
815 rng,
816 plaza_radius,
817 search_inner_radius,
818 PLAZA_MAX_SEARCH_RADIUS - search_inner_radius,
819 generator_stats,
820 site_name,
821 road_kind,
822 )
823 }
824
825 pub fn name(&self) -> Option<&str> { self.name.as_deref() }
826
827 pub fn generate_mine(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
828 let mut rng = reseed(rng);
829 let mut site = Site {
830 origin,
831 kind: Some(SiteKind::DwarvenMine),
832 ..Site::default()
833 };
834
835 let size = 60.0;
836
837 let aabr = Aabr {
838 min: Vec2::broadcast(-size as i32),
839 max: Vec2::broadcast(size as i32),
840 };
841
842 let wpos: Vec2<i32> = [1, 2].into();
843
844 let dwarven_mine =
845 plot::DwarvenMine::generate(land, &mut reseed(&mut rng), &site, wpos, aabr);
846 site.name = Some(dwarven_mine.name().to_string());
847 let plot = site.create_plot(Plot {
848 kind: PlotKind::DwarvenMine(dwarven_mine),
849 root_tile: aabr.center(),
850 tiles: aabr_tiles(aabr).collect(),
851 });
852
853 site.blit_aabr(aabr, Tile {
854 kind: TileKind::Empty,
855 plot: Some(plot),
856 hard_alt: Some(1_i32),
857 });
858
859 site
860 }
861
862 pub fn generate_citadel(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
863 let mut rng = reseed(rng);
864 let mut site = Site {
865 origin,
866 kind: Some(SiteKind::Citadel),
867 ..Site::default()
868 };
869 site.demarcate_obstacles(land);
870 let citadel = plot::Citadel::generate(origin, land, &mut rng);
871 site.name = Some(citadel.name().to_string());
872 let size = citadel.radius() / tile::TILE_SIZE as i32;
873 let aabr = Aabr {
874 min: Vec2::broadcast(-size),
875 max: Vec2::broadcast(size),
876 };
877 let plot = site.create_plot(Plot {
878 kind: PlotKind::Citadel(citadel),
879 root_tile: aabr.center(),
880 tiles: aabr_tiles(aabr).collect(),
881 });
882 site.blit_aabr(aabr, Tile {
883 kind: TileKind::Building,
884 plot: Some(plot),
885 hard_alt: None,
886 });
887 site
888 }
889
890 pub fn generate_gnarling(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
891 let mut rng = reseed(rng);
892 let mut site = Site {
893 origin,
894 kind: Some(SiteKind::Gnarling),
895 ..Site::default()
896 };
897 site.demarcate_obstacles(land);
898 let gnarling_fortification = plot::GnarlingFortification::generate(origin, land, &mut rng);
899 site.name = Some(gnarling_fortification.name().to_string());
900 let size = gnarling_fortification.radius() / TILE_SIZE as i32;
901 let aabr = Aabr {
902 min: Vec2::broadcast(-size),
903 max: Vec2::broadcast(size),
904 };
905 let plot = site.create_plot(Plot {
906 kind: PlotKind::Gnarling(gnarling_fortification),
907 root_tile: aabr.center(),
908 tiles: aabr_tiles(aabr).collect(),
909 });
910 site.blit_aabr(aabr, Tile {
911 kind: TileKind::GnarlingFortification,
912 plot: Some(plot),
913 hard_alt: None,
914 });
915 site
916 }
917
918 pub fn generate_adlet(
919 land: &Land,
920 rng: &mut impl Rng,
921 origin: Vec2<i32>,
922 index: IndexRef,
923 ) -> Self {
924 let mut rng = reseed(rng);
925 let mut site = Site {
926 origin,
927 kind: Some(SiteKind::Adlet),
928 ..Site::default()
929 };
930 site.demarcate_obstacles(land);
931 let adlet_stronghold = plot::AdletStronghold::generate(origin, land, &mut rng, index);
932 site.name = Some(adlet_stronghold.name().to_string());
933 let (cavern_aabr, wall_aabr) = adlet_stronghold.plot_tiles(origin);
934 let plot = site.create_plot(Plot {
935 kind: PlotKind::Adlet(adlet_stronghold),
936 root_tile: cavern_aabr.center(),
937 tiles: aabr_tiles(cavern_aabr)
938 .chain(aabr_tiles(wall_aabr))
939 .collect(),
940 });
941 site.blit_aabr(cavern_aabr, Tile {
942 kind: TileKind::AdletStronghold,
943 plot: Some(plot),
944 hard_alt: None,
945 });
946 site.blit_aabr(wall_aabr, Tile {
947 kind: TileKind::AdletStronghold,
948 plot: Some(plot),
949 hard_alt: None,
950 });
951 site
952 }
953
954 pub fn generate_terracotta(
955 land: &Land,
956 index: IndexRef,
957 rng: &mut impl Rng,
958 origin: Vec2<i32>,
959 generator_stats: &mut SitesGenMeta,
960 ) -> Self {
961 let mut rng = reseed(rng);
962 let gen_name = NameGen::location(&mut rng).generate_terracotta();
963 let suffix = [
964 "Tombs",
965 "Necropolis",
966 "Ruins",
967 "Mausoleum",
968 "Cemetery",
969 "Burial Grounds",
970 "Remains",
971 "Temples",
972 "Gardens",
973 ]
974 .choose(&mut rng)
975 .unwrap();
976 let name = match rng.random_range(0..2) {
977 0 => format!("{} {}", gen_name, suffix),
978 _ => format!("{} of {}", suffix, gen_name),
979 };
980 let mut site = Site {
981 origin,
982 name: Some(name.clone()),
983 kind: Some(SiteKind::Terracotta),
984 ..Site::default()
985 };
986
987 site.demarcate_obstacles(land);
989 const TERRACOTTA_PLAZA_RADIUS: u32 = 3;
992 const TERRACOTTA_PLAZA_SEARCH_INNER: u32 = 17;
993 const TERRACOTTA_PLAZA_SEARCH_WIDTH: u32 = 12;
994 generator_stats.add(site.name(), GenStatSiteKind::Terracotta);
995 let road_kind = plot::RoadKind {
996 lights: plot::RoadLights::Terracotta,
997 material: plot::RoadMaterial::Sandstone,
998 };
999 site.make_initial_plaza(
1000 land,
1001 index,
1002 &mut rng,
1003 TERRACOTTA_PLAZA_RADIUS,
1004 TERRACOTTA_PLAZA_SEARCH_INNER,
1005 TERRACOTTA_PLAZA_SEARCH_WIDTH,
1006 generator_stats,
1007 &name,
1008 road_kind,
1009 );
1010
1011 let size = 15.0 as i32;
1012 let aabr = Aabr {
1013 min: Vec2::broadcast(-size),
1014 max: Vec2::broadcast(size),
1015 };
1016 {
1017 let terracotta_palace =
1018 plot::TerracottaPalace::generate(land, &mut reseed(&mut rng), &site, aabr);
1019 let terracotta_palace_alt = terracotta_palace.alt;
1020 let plot = site.create_plot(Plot {
1021 kind: PlotKind::TerracottaPalace(terracotta_palace),
1022 root_tile: aabr.center(),
1023 tiles: aabr_tiles(aabr).collect(),
1024 });
1025
1026 site.blit_aabr(aabr, Tile {
1027 kind: TileKind::Building,
1028 plot: Some(plot),
1029 hard_alt: Some(terracotta_palace_alt),
1030 });
1031 }
1032 let build_chance = Lottery::from(vec![(12.0, 1), (4.0, 2)]);
1033 for _ in 0..16 {
1034 match *build_chance.choose_seeded(rng.random()) {
1035 1 => {
1036 generator_stats.attempt(site.name(), GenStatPlotKind::House);
1038 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
1039 if let Some((aabr, _, _, alt)) = attempt(32, || {
1040 site.find_roadside_aabr(
1041 &mut rng,
1042 9..(size + 1).pow(2),
1043 Extent2::broadcast(size),
1044 )
1045 }) {
1046 let terracotta_house = plot::TerracottaHouse::generate(
1047 land,
1048 &mut reseed(&mut rng),
1049 &site,
1050 aabr,
1051 alt,
1052 );
1053 let terracotta_house_alt = terracotta_house.alt;
1054 let plot = site.create_plot(Plot {
1055 kind: PlotKind::TerracottaHouse(terracotta_house),
1056 root_tile: aabr.center(),
1057 tiles: aabr_tiles(aabr).collect(),
1058 });
1059
1060 site.blit_aabr(aabr, Tile {
1061 kind: TileKind::Building,
1062 plot: Some(plot),
1063 hard_alt: Some(terracotta_house_alt),
1064 });
1065
1066 generator_stats.success(site.name(), GenStatPlotKind::House);
1067 } else {
1068 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1069 }
1070 },
1071
1072 2 => {
1073 generator_stats.attempt(site.name(), GenStatPlotKind::Yard);
1075 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
1076 if let Some((aabr, _, _, alt)) = attempt(32, || {
1077 site.find_roadside_aabr(
1078 &mut rng,
1079 9..(size + 1).pow(2),
1080 Extent2::broadcast(size),
1081 )
1082 }) {
1083 let terracotta_yard = plot::TerracottaYard::generate(
1084 land,
1085 &mut reseed(&mut rng),
1086 &site,
1087 aabr,
1088 alt,
1089 );
1090 let terracotta_yard_alt = terracotta_yard.alt;
1091 let plot = site.create_plot(Plot {
1092 kind: PlotKind::TerracottaYard(terracotta_yard),
1093 root_tile: aabr.center(),
1094 tiles: aabr_tiles(aabr).collect(),
1095 });
1096
1097 site.blit_aabr(aabr, Tile {
1098 kind: TileKind::Building,
1099 plot: Some(plot),
1100 hard_alt: Some(terracotta_yard_alt),
1101 });
1102
1103 generator_stats.success(site.name(), GenStatPlotKind::Yard);
1104 } else {
1105 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1106 }
1107 },
1108 _ => {},
1109 }
1110 }
1111 site
1112 }
1113
1114 pub fn generate_myrmidon(
1115 land: &Land,
1116 index: IndexRef,
1117 rng: &mut impl Rng,
1118 origin: Vec2<i32>,
1119 generator_stats: &mut SitesGenMeta,
1120 ) -> Self {
1121 let mut rng = reseed(rng);
1122 let gen_name = NameGen::location(&mut rng).generate_danari();
1123 let suffix = ["City", "Metropolis"].choose(&mut rng).unwrap();
1124 let name = match rng.random_range(0..2) {
1125 0 => format!("{} {}", gen_name, suffix),
1126 _ => format!("{} of {}", suffix, gen_name),
1127 };
1128 let mut site = Site {
1129 origin,
1130 name: Some(name.clone()),
1131 kind: Some(SiteKind::Myrmidon),
1132 ..Site::default()
1133 };
1134
1135 let road_kind = plot::RoadKind {
1136 lights: plot::RoadLights::Default,
1137 material: plot::RoadMaterial::Dirt,
1138 };
1139
1140 site.demarcate_obstacles(land);
1142 const MYRMIDON_PLAZA_RADIUS: u32 = 3;
1145 const MYRMIDON_PLAZA_SEARCH_INNER: u32 = 18;
1146 const MYRMIDON_PLAZA_SEARCH_WIDTH: u32 = 12;
1147 generator_stats.add(site.name(), GenStatSiteKind::Myrmidon);
1148 generator_stats.attempt(site.name(), GenStatPlotKind::InitialPlaza);
1149 site.make_initial_plaza(
1150 land,
1151 index,
1152 &mut rng,
1153 MYRMIDON_PLAZA_RADIUS,
1154 MYRMIDON_PLAZA_SEARCH_INNER,
1155 MYRMIDON_PLAZA_SEARCH_WIDTH,
1156 generator_stats,
1157 &name,
1158 road_kind,
1159 );
1160
1161 let size = 16.0 as i32;
1162 let aabr = Aabr {
1163 min: Vec2::broadcast(-size),
1164 max: Vec2::broadcast(size),
1165 };
1166 {
1167 let myrmidon_arena =
1168 plot::MyrmidonArena::generate(land, &mut reseed(&mut rng), &site, aabr);
1169 let myrmidon_arena_alt = myrmidon_arena.alt;
1170 let plot = site.create_plot(Plot {
1171 kind: PlotKind::MyrmidonArena(myrmidon_arena),
1172 root_tile: aabr.center(),
1173 tiles: aabr_tiles(aabr).collect(),
1174 });
1175
1176 site.blit_aabr(aabr, Tile {
1177 kind: TileKind::Building,
1178 plot: Some(plot),
1179 hard_alt: Some(myrmidon_arena_alt),
1180 });
1181 }
1182 for _ in 0..30 {
1183 generator_stats.attempt(site.name(), GenStatPlotKind::House);
1185 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
1186 if let Some((aabr, _, _, alt)) = attempt(32, || {
1187 site.find_roadside_aabr(&mut rng, 9..(size + 1).pow(2), Extent2::broadcast(size))
1188 }) {
1189 let myrmidon_house =
1190 plot::MyrmidonHouse::generate(land, &mut reseed(&mut rng), &site, aabr, alt);
1191 let myrmidon_house_alt = myrmidon_house.alt;
1192 let plot = site.create_plot(Plot {
1193 kind: PlotKind::MyrmidonHouse(myrmidon_house),
1194 root_tile: aabr.center(),
1195 tiles: aabr_tiles(aabr).collect(),
1196 });
1197
1198 site.blit_aabr(aabr, Tile {
1199 kind: TileKind::Building,
1200 plot: Some(plot),
1201 hard_alt: Some(myrmidon_house_alt),
1202 });
1203
1204 generator_stats.success(site.name(), GenStatPlotKind::House);
1205 } else {
1206 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1207 }
1208 }
1209
1210 site
1211 }
1212
1213 pub fn generate_giant_tree(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
1214 let mut rng = reseed(rng);
1215 let mut site = Site {
1216 origin,
1217 kind: Some(SiteKind::GiantTree),
1218 ..Site::default()
1219 };
1220 site.demarcate_obstacles(land);
1221 let giant_tree = plot::GiantTree::generate(&site, Vec2::zero(), land, &mut rng);
1222 site.name = Some(giant_tree.name().to_string());
1223 let size = (giant_tree.radius() / TILE_SIZE as f32).ceil() as i32;
1224 let aabr = Aabr {
1225 min: Vec2::broadcast(-size),
1226 max: Vec2::broadcast(size) + 1,
1227 };
1228 let plot = site.create_plot(Plot {
1229 kind: PlotKind::GiantTree(giant_tree),
1230 root_tile: aabr.center(),
1231 tiles: aabr_tiles(aabr).collect(),
1232 });
1233 site.blit_aabr(aabr, Tile {
1234 kind: TileKind::Building,
1235 plot: Some(plot),
1236 hard_alt: None,
1237 });
1238 site
1239 }
1240
1241 pub fn generate_city(
1243 land: &Land,
1244 index: IndexRef,
1245 rng: &mut impl Rng,
1246 origin: Vec2<i32>,
1247 size: f32,
1248 calendar: Option<&Calendar>,
1249 generator_stats: &mut SitesGenMeta,
1250 ) -> Self {
1251 let mut rng = reseed(rng);
1252 let name = NameGen::location(&mut rng).generate_town();
1253 let mut site = Site {
1254 origin,
1255 name: Some(name.clone()),
1256 kind: Some(SiteKind::Refactor),
1257 ..Site::default()
1258 };
1259 let road_kind = plot::RoadKind {
1260 lights: plot::RoadLights::Default,
1261 material: plot::RoadMaterial::Cobblestone,
1262 };
1263
1264 site.demarcate_obstacles(land);
1266 generator_stats.add(site.name(), GenStatSiteKind::City);
1267 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1268
1269 let build_chance = Lottery::from(vec![
1270 (64.0, 1), (5.0, 2), (25.0, 3), (5.0, 5), (15.0, 6), (15.0, 7), (5.0, 8), ]);
1279
1280 let mut workshops = 0;
1282 let mut castles = 0;
1283 let mut taverns = 0;
1284 let mut airship_docks = 0;
1285
1286 for _ in 0..(size * 200.0) as i32 {
1287 match *build_chance.choose_seeded(rng.random()) {
1288 n if (n == 5 && workshops < (size * 5.0) as i32) || workshops == 0 => {
1290 generator_stats.attempt(site.name(), GenStatPlotKind::Workshop);
1291 let size = (3.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
1292 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1293 site.find_roadside_aabr(
1294 &mut rng,
1295 4..(size + 1).pow(2),
1296 Extent2::broadcast(size),
1297 )
1298 }) {
1299 let workshop = plot::Workshop::generate(
1300 land,
1301 &mut reseed(&mut rng),
1302 &site,
1303 door_tile,
1304 door_dir,
1305 aabr,
1306 alt,
1307 );
1308 let workshop_alt = workshop.alt;
1309 let plot = site.create_plot(Plot {
1310 kind: PlotKind::Workshop(workshop),
1311 root_tile: aabr.center(),
1312 tiles: aabr_tiles(aabr).collect(),
1313 });
1314
1315 site.blit_aabr(aabr, Tile {
1316 kind: TileKind::Building,
1317 plot: Some(plot),
1318 hard_alt: Some(workshop_alt),
1319 });
1320 workshops += 1;
1321 generator_stats.success(site.name(), GenStatPlotKind::Workshop);
1322 } else {
1323 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1324 }
1325 },
1326 1 => {
1328 let size = (1.5 + rng.random::<f32>().powf(5.0) * 1.0).round() as u32;
1329 generator_stats.attempt(site.name(), GenStatPlotKind::House);
1330 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1331 site.find_roadside_aabr(
1332 &mut rng,
1333 4..(size + 1).pow(2),
1334 Extent2::broadcast(size),
1335 )
1336 }) {
1337 let house = plot::House::generate(
1338 land,
1339 &mut reseed(&mut rng),
1340 &site,
1341 door_tile,
1342 door_dir,
1343 aabr,
1344 calendar,
1345 alt,
1346 );
1347 let house_alt = house.alt;
1348 let plot = site.create_plot(Plot {
1349 kind: PlotKind::House(house),
1350 root_tile: aabr.center(),
1351 tiles: aabr_tiles(aabr).collect(),
1352 });
1353
1354 site.blit_aabr(aabr, Tile {
1355 kind: TileKind::Building,
1356 plot: Some(plot),
1357 hard_alt: Some(house_alt),
1358 });
1359 generator_stats.success(site.name(), GenStatPlotKind::House);
1360 } else {
1361 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1362 }
1363 },
1364 2 => {
1366 generator_stats.attempt(site.name(), GenStatPlotKind::GuardTower);
1367 if let Some((_aabr, _, _door_dir, _)) = attempt(10, || {
1368 site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
1369 }) {
1370 }
1387 },
1388 3 => {
1390 Self::generate_farm(false, &mut rng, &mut site, land);
1391 },
1392 4 if size > 0.2 && castles < 1 => {
1394 generator_stats.attempt(site.name(), GenStatPlotKind::Castle);
1395 if let Some((aabr, _entrance_tile, _door_dir, _alt)) = attempt(64, || {
1396 site.find_rural_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))
1397 }) {
1398 let offset = rng.random_range(5..(aabr.size().w.min(aabr.size().h) - 4));
1399 let gate_aabr = Aabr {
1400 min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y),
1401 max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1),
1402 };
1403 let castle = plot::Castle::generate(land, &mut rng, &site, aabr, gate_aabr);
1404 let castle_alt = castle.alt;
1405 let plot = site.create_plot(Plot {
1406 kind: PlotKind::Castle(castle),
1407 root_tile: aabr.center(),
1408 tiles: aabr_tiles(aabr).collect(),
1409 });
1410
1411 let wall_north = Tile {
1412 kind: TileKind::Wall(Dir2::Y),
1413 plot: Some(plot),
1414 hard_alt: Some(castle_alt),
1415 };
1416
1417 let wall_east = Tile {
1418 kind: TileKind::Wall(Dir2::X),
1419 plot: Some(plot),
1420 hard_alt: Some(castle_alt),
1421 };
1422 for x in 0..aabr.size().w {
1423 site.tiles
1424 .set(aabr.min + Vec2::new(x, 0), wall_east.clone());
1425 site.tiles.set(
1426 aabr.min + Vec2::new(x, aabr.size().h - 1),
1427 wall_east.clone(),
1428 );
1429 }
1430 for y in 0..aabr.size().h {
1431 site.tiles
1432 .set(aabr.min + Vec2::new(0, y), wall_north.clone());
1433 site.tiles.set(
1434 aabr.min + Vec2::new(aabr.size().w - 1, y),
1435 wall_north.clone(),
1436 );
1437 }
1438
1439 let gate = Tile {
1440 kind: TileKind::Gate,
1441 plot: Some(plot),
1442 hard_alt: Some(castle_alt),
1443 };
1444 let tower_parapet = Tile {
1445 kind: TileKind::Tower(RoofKind::Parapet),
1446 plot: Some(plot),
1447 hard_alt: Some(castle_alt),
1448 };
1449 let tower_pyramid = Tile {
1450 kind: TileKind::Tower(RoofKind::Pyramid),
1451 plot: Some(plot),
1452 hard_alt: Some(castle_alt),
1453 };
1454
1455 site.tiles.set(
1456 Vec2::new(aabr.min.x + offset - 2, aabr.min.y),
1457 tower_parapet.clone(),
1458 );
1459 site.tiles
1460 .set(Vec2::new(aabr.min.x + offset - 1, aabr.min.y), gate.clone());
1461 site.tiles
1462 .set(Vec2::new(aabr.min.x + offset, aabr.min.y), gate.clone());
1463 site.tiles
1464 .set(Vec2::new(aabr.min.x + offset + 1, aabr.min.y), gate.clone());
1465 site.tiles.set(
1466 Vec2::new(aabr.min.x + offset + 2, aabr.min.y),
1467 tower_parapet.clone(),
1468 );
1469
1470 site.tiles
1471 .set(Vec2::new(aabr.min.x, aabr.min.y), tower_parapet.clone());
1472 site.tiles
1473 .set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower_parapet.clone());
1474 site.tiles
1475 .set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower_parapet.clone());
1476 site.tiles.set(
1477 Vec2::new(aabr.max.x - 1, aabr.max.y - 1),
1478 tower_parapet.clone(),
1479 );
1480
1481 site.blit_aabr(
1483 Aabr {
1484 min: aabr.min + 1,
1485 max: aabr.max - 1,
1486 },
1487 Tile {
1488 kind: TileKind::Road {
1489 a: 0,
1490 b: 0,
1491 w: 0,
1492 alt: castle_alt as f32,
1493 },
1494 plot: Some(plot),
1495 hard_alt: Some(castle_alt),
1496 },
1497 );
1498
1499 site.blit_aabr(
1501 Aabr {
1502 min: aabr.center() - 3,
1503 max: aabr.center() + 3,
1504 },
1505 Tile {
1506 kind: TileKind::Wall(Dir2::Y),
1507 plot: Some(plot),
1508 hard_alt: Some(castle_alt),
1509 },
1510 );
1511 site.tiles.set(
1512 Vec2::new(aabr.center().x + 2, aabr.center().y + 2),
1513 tower_pyramid.clone(),
1514 );
1515 site.tiles.set(
1516 Vec2::new(aabr.center().x + 2, aabr.center().y - 3),
1517 tower_pyramid.clone(),
1518 );
1519 site.tiles.set(
1520 Vec2::new(aabr.center().x - 3, aabr.center().y + 2),
1521 tower_pyramid.clone(),
1522 );
1523 site.tiles.set(
1524 Vec2::new(aabr.center().x - 3, aabr.center().y - 3),
1525 tower_pyramid.clone(),
1526 );
1527
1528 site.blit_aabr(
1529 Aabr {
1530 min: aabr.center() - 2,
1531 max: aabr.center() + 2,
1532 },
1533 Tile {
1534 kind: TileKind::Keep(KeepKind::Middle),
1535 plot: Some(plot),
1536 hard_alt: Some(castle_alt),
1537 },
1538 );
1539
1540 castles += 1;
1541 generator_stats.success(site.name(), GenStatPlotKind::Castle);
1542 }
1543 },
1544 6 if (size > 0.125 && airship_docks == 0) => {
1546 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
1547 let size = 9u32;
1553 if let Some((aabr, door_tile, door_dir, _)) = attempt(32, || {
1554 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
1555 }) {
1556 let airship_dock = plot::AirshipDock::generate(
1557 land,
1558 index,
1559 &mut reseed(&mut rng),
1560 &site,
1561 door_tile,
1562 door_dir,
1563 aabr,
1564 );
1565 let airship_dock_alt = airship_dock.alt;
1566 let plot = site.create_plot(Plot {
1567 kind: PlotKind::AirshipDock(airship_dock),
1568 root_tile: aabr.center(),
1569 tiles: aabr_tiles(aabr).collect(),
1570 });
1571
1572 site.blit_aabr(aabr, Tile {
1573 kind: TileKind::Building,
1574 plot: Some(plot),
1575 hard_alt: Some(airship_dock_alt),
1576 });
1577 airship_docks += 1;
1578 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
1579 } else {
1580 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1581 }
1582 },
1583 7 if (size > 0.125 && taverns < 2) => {
1584 generator_stats.attempt(site.name(), GenStatPlotKind::Tavern);
1585 let size = (4.5 + rng.random::<f32>().powf(5.0) * 2.0).round() as u32;
1586 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1587 site.find_roadside_aabr(
1588 &mut rng,
1589 8..(size + 1).pow(2),
1590 Extent2::broadcast(size),
1591 )
1592 }) {
1593 let tavern = plot::Tavern::generate(
1594 land,
1595 index,
1596 &mut reseed(&mut rng),
1597 &site,
1598 door_tile,
1599 Dir2::from_vec2(door_dir),
1600 aabr,
1601 alt,
1602 );
1603 let tavern_alt = tavern.door_wpos.z;
1604 let plot = site.create_plot(Plot {
1605 kind: PlotKind::Tavern(tavern),
1606 root_tile: aabr.center(),
1607 tiles: aabr_tiles(aabr).collect(),
1608 });
1609
1610 site.blit_aabr(aabr, Tile {
1611 kind: TileKind::Building,
1612 plot: Some(plot),
1613 hard_alt: Some(tavern_alt),
1614 });
1615
1616 taverns += 1;
1617 generator_stats.success(site.name(), GenStatPlotKind::Tavern);
1618 } else {
1619 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1620 }
1621 },
1622 8 => {
1623 Self::generate_barn(false, &mut rng, &mut site, land, index);
1624 },
1625 _ => {},
1626 }
1627 }
1628
1629 site
1630 }
1631
1632 pub fn generate_glider_course(
1633 land: &Land,
1634 _index: IndexRef,
1635 rng: &mut impl Rng,
1636 origin: Vec2<i32>,
1637 ) -> Self {
1638 let mut rng = reseed(rng);
1639 let mut site = Site {
1640 origin,
1641 kind: Some(SiteKind::GliderCourse),
1642 ..Site::default()
1643 };
1644
1645 site.name = Some(NameGen::location(&mut rng).generate_town() + " Glider Course");
1648
1649 let origin_alt = land.get_alt_approx(origin);
1652 let alt_drops: Vec<f32> = CARDINALS
1653 .iter()
1654 .map(|c| {
1655 origin_alt
1656 - 0.5
1657 * (land.get_alt_approx(origin + *c * TerrainChunkSize::RECT_SIZE.x as i32)
1658 + land.get_alt_approx(
1659 origin + 2 * *c * TerrainChunkSize::RECT_SIZE.x as i32,
1660 ))
1661 })
1662 .collect();
1663 let mut cardinal = 0;
1664 let mut max_drop = 0.0;
1665 for (i, drop) in alt_drops.iter().enumerate() {
1666 if *drop > max_drop {
1667 max_drop = *drop;
1668 cardinal = i;
1669 }
1670 }
1671 let dir = match cardinal {
1672 0 => Dir2::X,
1673 1 => Dir2::Y,
1674 2 => Dir2::NegX,
1675 3 => Dir2::NegY,
1676 _ => Dir2::X,
1677 };
1678 let size = 2.0;
1679
1680 let mut valid_course = true;
1681 let mut positions = Vec::new();
1682
1683 let pos = origin;
1685 let tile_pos: Vec2<i32> = Vec2::zero();
1686 positions.push((pos, tile_pos));
1687
1688 const CHUNK_OFFSET: usize = 5;
1692 let offset = CHUNK_OFFSET as i32 * TerrainChunkSize::RECT_SIZE.x as i32;
1694 let tile_offset = offset / TILE_SIZE as i32;
1697 let pos_offset = tile_offset * TILE_SIZE as i32;
1698
1699 let pos = origin + pos_offset * dir.to_vec2();
1701 let tile_pos = tile_offset * dir.to_vec2();
1702 positions.push((pos, tile_pos));
1703
1704 let mut last_pos = pos;
1708 let mut last_tile_pos = tile_pos;
1709 for j in 1..(CHUNK_OFFSET * 9 + 1) {
1710 let c_downhill = land.get_chunk_wpos(last_pos).and_then(|c| c.downhill);
1711 if let Some(downhill) = c_downhill {
1712 let downhill_chunk =
1713 downhill.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / (sz as i32));
1714 let downhill_chunk_pos = TerrainChunkSize::center_wpos(downhill_chunk);
1715 let downhill_vec = downhill_chunk_pos - last_pos;
1716 let tile_offset = downhill_vec / (TILE_SIZE as i32);
1719 let pos_offset = tile_offset * TILE_SIZE as i32;
1720 let pos = last_pos + pos_offset;
1721 let tile_pos = last_tile_pos + tile_offset;
1722 last_pos = pos;
1723 last_tile_pos = tile_pos;
1724 if j % CHUNK_OFFSET == 0 {
1727 positions.push((pos, tile_pos));
1728 }
1729 } else {
1730 valid_course = false;
1731 }
1732 }
1733 if valid_course && positions.len() > 1 {
1740 for (i, window) in positions.windows(2).enumerate() {
1741 if !window.is_empty() {
1742 let [(pos, tile_pos), (next_pos, next_tile_pos)] = window else {
1743 panic!(
1744 "previous condition required positions Vec to have at least two \
1745 elements"
1746 );
1747 };
1748 if i == 0 {
1749 let aabr = Aabr {
1751 min: Vec2::broadcast(-size as i32),
1752 max: Vec2::broadcast(size as i32),
1753 };
1754 let glider_platform = plot::GliderPlatform::generate(
1755 land,
1756 &mut reseed(&mut rng),
1757 &site,
1758 *pos,
1759 dir,
1760 );
1761 let alt = glider_platform.alt - 5;
1762 let plot = site.create_plot(Plot {
1763 kind: PlotKind::GliderPlatform(glider_platform),
1764 root_tile: aabr.center(),
1765 tiles: aabr_tiles(aabr).collect(),
1766 });
1767 site.blit_aabr(aabr, Tile {
1768 kind: TileKind::Building,
1769 plot: Some(plot),
1770 hard_alt: Some(alt),
1771 });
1772 } else if i < 9 {
1773 let dir = if i > 1 {
1776 Dir2::from_vec2(next_pos - pos)
1777 } else {
1778 dir
1779 };
1780 let aabr = Aabr {
1781 min: Vec2::broadcast(-size as i32) + tile_pos,
1782 max: Vec2::broadcast(size as i32) + tile_pos,
1783 };
1784 let glider_ring = plot::GliderRing::generate(
1785 land,
1786 &mut reseed(&mut rng),
1787 &site,
1788 pos,
1789 i,
1790 dir,
1791 );
1792 let plot = site.create_plot(Plot {
1793 kind: PlotKind::GliderRing(glider_ring),
1794 root_tile: aabr.center(),
1795 tiles: aabr_tiles(aabr).collect(),
1796 });
1797 site.blit_aabr(aabr, Tile {
1798 kind: TileKind::Building,
1799 plot: Some(plot),
1800 hard_alt: None,
1801 });
1802 } else if i == 9 {
1803 let dir = Dir2::from_vec2(next_pos - pos);
1807 let aabr = Aabr {
1808 min: Vec2::broadcast(-size as i32) + tile_pos,
1809 max: Vec2::broadcast(size as i32) + tile_pos,
1810 };
1811 let glider_ring = plot::GliderRing::generate(
1812 land,
1813 &mut reseed(&mut rng),
1814 &site,
1815 pos,
1816 i,
1817 dir,
1818 );
1819 let plot = site.create_plot(Plot {
1820 kind: PlotKind::GliderRing(glider_ring),
1821 root_tile: aabr.center(),
1822 tiles: aabr_tiles(aabr).collect(),
1823 });
1824 site.blit_aabr(aabr, Tile {
1825 kind: TileKind::Building,
1826 plot: Some(plot),
1827 hard_alt: None,
1828 });
1829 let size = 10.0;
1831 let aabr = Aabr {
1832 min: Vec2::broadcast(-size as i32) + next_tile_pos,
1833 max: Vec2::broadcast(size as i32) + next_tile_pos,
1834 };
1835 let glider_finish = plot::GliderFinish::generate(
1836 land,
1837 &mut reseed(&mut rng),
1838 &site,
1839 *next_pos,
1840 );
1841 let plot = site.create_plot(Plot {
1842 kind: PlotKind::GliderFinish(glider_finish),
1843 root_tile: aabr.center(),
1844 tiles: aabr_tiles(aabr).collect(),
1845 });
1846 site.blit_aabr(aabr, Tile {
1847 kind: TileKind::Building,
1848 plot: Some(plot),
1849 hard_alt: None,
1850 });
1851 }
1852 }
1853 }
1854 }
1855
1856 site
1857 }
1858
1859 pub fn generate_cliff_town(
1860 land: &Land,
1861 index: IndexRef,
1862 rng: &mut impl Rng,
1863 origin: Vec2<i32>,
1864 generator_stats: &mut SitesGenMeta,
1865 ) -> Self {
1866 let mut rng = reseed(rng);
1867 let name = NameGen::location(&mut rng).generate_arabic();
1868 let mut site = Site {
1869 origin,
1870 name: Some(name.clone()),
1871 kind: Some(SiteKind::CliffTown),
1872 ..Site::default()
1873 };
1874 let mut campfires = 0;
1875 let road_kind = plot::RoadKind {
1876 lights: plot::RoadLights::Default,
1877 material: plot::RoadMaterial::Sandstone,
1878 };
1879
1880 generator_stats.add(site.name(), GenStatSiteKind::CliffTown);
1882 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1883
1884 let build_chance = Lottery::from(vec![(30.0, 1), (50.0, 2)]);
1885 let mut airship_docks = 0;
1886 for _ in 0..80 {
1887 match *build_chance.choose_seeded(rng.random()) {
1888 1 => {
1889 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.0).round() as u32;
1891 generator_stats.attempt(site.name(), GenStatPlotKind::House);
1892 let campfire = campfires < 4;
1893 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1894 site.find_roadside_aabr(
1895 &mut rng,
1896 8..(size + 1).pow(2),
1897 Extent2::broadcast(size),
1898 )
1899 }) {
1900 let cliff_tower = plot::CliffTower::generate(
1901 land,
1902 index,
1903 &mut reseed(&mut rng),
1904 &site,
1905 door_tile,
1906 door_dir,
1907 aabr,
1908 campfire,
1909 alt,
1910 );
1911 let cliff_tower_alt = cliff_tower.alt;
1912 let plot = site.create_plot(Plot {
1913 kind: PlotKind::CliffTower(cliff_tower),
1914 root_tile: aabr.center(),
1915 tiles: aabr_tiles(aabr).collect(),
1916 });
1917 site.blit_aabr(aabr, Tile {
1918 kind: TileKind::Building,
1919 plot: Some(plot),
1920 hard_alt: Some(cliff_tower_alt),
1921 });
1922 campfires += 1;
1923 generator_stats.success(site.name(), GenStatPlotKind::House);
1924 } else {
1925 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1926 }
1927 },
1928 2 if airship_docks < 1 => {
1929 let size = 25u32;
1931 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
1932 if let Some((aabr, door_tile, door_dir, _)) = attempt(32, || {
1933 site.find_roadside_aabr(&mut rng, 625..626, Extent2::broadcast(size))
1934 }) {
1935 let cliff_town_airship_dock = plot::CliffTownAirshipDock::generate(
1936 land,
1937 index,
1938 &mut reseed(&mut rng),
1939 &site,
1940 door_tile,
1941 door_dir,
1942 aabr,
1943 );
1944 let cliff_town_airship_dock_alt = cliff_town_airship_dock.alt;
1945 let plot = site.create_plot(Plot {
1946 kind: PlotKind::CliffTownAirshipDock(cliff_town_airship_dock),
1947 root_tile: aabr.center(),
1948 tiles: aabr_tiles(aabr).collect(),
1949 });
1950
1951 site.blit_aabr(aabr, Tile {
1952 kind: TileKind::Building,
1953 plot: Some(plot),
1954 hard_alt: Some(cliff_town_airship_dock_alt),
1955 });
1956 airship_docks += 1;
1957 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
1958 } else {
1959 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1960 }
1961 },
1962 _ => {},
1963 }
1964 }
1965
1966 site.demarcate_obstacles(land);
1967 site
1968 }
1969
1970 pub fn generate_savannah_town(
1971 land: &Land,
1972 index: IndexRef,
1973 rng: &mut impl Rng,
1974 origin: Vec2<i32>,
1975 generator_stats: &mut SitesGenMeta,
1976 ) -> Self {
1977 let mut rng = reseed(rng);
1978 let name = NameGen::location(&mut rng).generate_savannah_custom();
1979 let mut site = Site {
1980 origin,
1981 name: Some(name.clone()),
1982 kind: Some(SiteKind::SavannahTown),
1983 ..Site::default()
1984 };
1985 let road_kind = plot::RoadKind {
1986 lights: plot::RoadLights::Default,
1987 material: plot::RoadMaterial::Dirt,
1988 };
1989
1990 site.demarcate_obstacles(land);
1992 generator_stats.add(site.name(), GenStatSiteKind::SavannahTown);
1993 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1994
1995 let mut workshops = 0;
1996 let mut airship_dock = 0;
1997 let build_chance = Lottery::from(vec![
1998 (25.0, 1),
1999 (5.0, 2),
2000 (5.0, 3),
2001 (15.0, 4),
2002 (5.0, 5),
2003 (5.0, 6),
2004 ]);
2005
2006 for _ in 0..50 {
2007 match *build_chance.choose_seeded(rng.random()) {
2008 n if (n == 2 && workshops < 3) || workshops == 0 => {
2009 let size = (4.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2011 generator_stats.attempt(site.name(), GenStatPlotKind::Workshop);
2012 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2013 site.find_roadside_aabr(
2014 &mut rng,
2015 4..(size + 1).pow(2),
2016 Extent2::broadcast(size),
2017 )
2018 }) {
2019 let savannah_workshop = plot::SavannahWorkshop::generate(
2020 land,
2021 &mut reseed(&mut rng),
2022 &site,
2023 door_tile,
2024 door_dir,
2025 aabr,
2026 alt,
2027 );
2028 let savannah_workshop_alt = savannah_workshop.alt;
2029 let plot = site.create_plot(Plot {
2030 kind: PlotKind::SavannahWorkshop(savannah_workshop),
2031 root_tile: aabr.center(),
2032 tiles: aabr_tiles(aabr).collect(),
2033 });
2034
2035 site.blit_aabr(aabr, Tile {
2036 kind: TileKind::Building,
2037 plot: Some(plot),
2038 hard_alt: Some(savannah_workshop_alt),
2039 });
2040 workshops += 1;
2041 generator_stats.success(site.name(), GenStatPlotKind::Workshop);
2042 } else {
2043 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2044 }
2045 },
2046 1 => {
2047 let size = (4.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2049 generator_stats.attempt(site.name(), GenStatPlotKind::House);
2050 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2051 site.find_roadside_aabr(
2052 &mut rng,
2053 4..(size + 1).pow(2),
2054 Extent2::broadcast(size),
2055 )
2056 }) {
2057 let savannah_hut = plot::SavannahHut::generate(
2058 land,
2059 &mut reseed(&mut rng),
2060 &site,
2061 door_tile,
2062 door_dir,
2063 aabr,
2064 alt,
2065 );
2066 let savannah_hut_alt = savannah_hut.alt;
2067 let plot = site.create_plot(Plot {
2068 kind: PlotKind::SavannahHut(savannah_hut),
2069 root_tile: aabr.center(),
2070 tiles: aabr_tiles(aabr).collect(),
2071 });
2072
2073 site.blit_aabr(aabr, Tile {
2074 kind: TileKind::Building,
2075 plot: Some(plot),
2076 hard_alt: Some(savannah_hut_alt),
2077 });
2078 generator_stats.success(site.name(), GenStatPlotKind::House);
2079 } else {
2080 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2081 }
2082 },
2083 3 if airship_dock < 1 => {
2084 let size = 9u32;
2086 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
2087 if let Some((aabr, door_tile, _, _)) = attempt(48, || {
2088 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
2089 }) {
2090 let savannah_airship_dock = plot::SavannahAirshipDock::generate(
2091 land,
2092 &mut reseed(&mut rng),
2093 &site,
2094 door_tile,
2095 aabr,
2096 );
2097 let savannah_airship_dock_alt = savannah_airship_dock.alt;
2098 let plot = site.create_plot(Plot {
2099 kind: PlotKind::SavannahAirshipDock(savannah_airship_dock),
2100 root_tile: aabr.center(),
2101 tiles: aabr_tiles(aabr).collect(),
2102 });
2103
2104 site.blit_aabr(aabr, Tile {
2105 kind: TileKind::Building,
2106 plot: Some(plot),
2107 hard_alt: Some(savannah_airship_dock_alt),
2108 });
2109 airship_dock += 1;
2110 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
2111 } else {
2112 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2113 }
2114 },
2115 4 => {
2117 Self::generate_farm(false, &mut rng, &mut site, land);
2118 },
2119 5 => {
2120 Self::generate_barn(false, &mut rng, &mut site, land, index);
2121 },
2122 6 => {
2123 let size = (4.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2125 generator_stats.attempt(site.name(), GenStatPlotKind::House);
2126 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2127 site.find_roadside_aabr(
2128 &mut rng,
2129 2..(size + 1).pow(2),
2130 Extent2::broadcast(size),
2131 )
2132 }) {
2133 let savannah_guard_hut = plot::SavannahGuardHut::generate(
2134 land,
2135 &mut reseed(&mut rng),
2136 &site,
2137 door_tile,
2138 door_dir,
2139 aabr,
2140 alt,
2141 );
2142 let savannah_guard_hut_alt = savannah_guard_hut.alt;
2143 let plot = site.create_plot(Plot {
2144 kind: PlotKind::SavannahGuardHut(savannah_guard_hut),
2145 root_tile: aabr.center(),
2146 tiles: aabr_tiles(aabr).collect(),
2147 });
2148
2149 site.blit_aabr(aabr, Tile {
2150 kind: TileKind::Building,
2151 plot: Some(plot),
2152 hard_alt: Some(savannah_guard_hut_alt),
2153 });
2154 generator_stats.success(site.name(), GenStatPlotKind::House);
2155 } else {
2156 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2157 }
2158 },
2159 _ => {},
2160 }
2161 }
2162 site
2163 }
2164
2165 pub fn generate_coastal_town(
2166 land: &Land,
2167 index: IndexRef,
2168 rng: &mut impl Rng,
2169 origin: Vec2<i32>,
2170 generator_stats: &mut SitesGenMeta,
2171 ) -> Self {
2172 let mut rng = reseed(rng);
2173 let name = NameGen::location(&mut rng).generate_danari();
2174 let mut site = Site {
2175 origin,
2176 name: Some(name.clone()),
2177 kind: Some(SiteKind::CoastalTown),
2178 ..Site::default()
2179 };
2180 let road_kind = plot::RoadKind {
2181 lights: plot::RoadLights::Default,
2182 material: plot::RoadMaterial::Marble,
2183 };
2184
2185 site.demarcate_obstacles(land);
2187 generator_stats.add(site.name(), GenStatSiteKind::CoastalTown);
2188 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
2189
2190 let mut workshops = 0;
2191 let build_chance = Lottery::from(vec![(38.0, 1), (5.0, 2), (15.0, 3), (15.0, 4), (5.0, 5)]);
2192 let mut airship_docks = 0;
2193 for _ in 0..55 {
2194 match *build_chance.choose_seeded(rng.random()) {
2195 n if (n == 2 && workshops < 3) || workshops == 0 => {
2196 let size = (7.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2198 generator_stats.attempt(site.name(), GenStatPlotKind::Workshop);
2199 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2200 site.find_roadside_aabr(
2201 &mut rng,
2202 7..(size + 1).pow(2),
2203 Extent2::broadcast(size),
2204 )
2205 }) {
2206 let coastal_workshop = plot::CoastalWorkshop::generate(
2207 land,
2208 &mut reseed(&mut rng),
2209 &site,
2210 door_tile,
2211 door_dir,
2212 aabr,
2213 alt,
2214 );
2215 let coastal_workshop_alt = coastal_workshop.alt;
2216 let plot = site.create_plot(Plot {
2217 kind: PlotKind::CoastalWorkshop(coastal_workshop),
2218 root_tile: aabr.center(),
2219 tiles: aabr_tiles(aabr).collect(),
2220 });
2221
2222 site.blit_aabr(aabr, Tile {
2223 kind: TileKind::Building,
2224 plot: Some(plot),
2225 hard_alt: Some(coastal_workshop_alt),
2226 });
2227 workshops += 1;
2228 generator_stats.success(site.name(), GenStatPlotKind::Workshop);
2229 } else {
2230 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2231 }
2232 },
2233 1 => {
2234 let size = (7.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2236 generator_stats.attempt(site.name(), GenStatPlotKind::House);
2237 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2238 site.find_roadside_aabr(
2239 &mut rng,
2240 7..(size + 1).pow(2),
2241 Extent2::broadcast(size),
2242 )
2243 }) {
2244 let coastal_house = plot::CoastalHouse::generate(
2245 land,
2246 &mut reseed(&mut rng),
2247 &site,
2248 door_tile,
2249 door_dir,
2250 aabr,
2251 alt,
2252 );
2253 let coastal_house_alt = coastal_house.alt;
2254 let plot = site.create_plot(Plot {
2255 kind: PlotKind::CoastalHouse(coastal_house),
2256 root_tile: aabr.center(),
2257 tiles: aabr_tiles(aabr).collect(),
2258 });
2259
2260 site.blit_aabr(aabr, Tile {
2261 kind: TileKind::Building,
2262 plot: Some(plot),
2263 hard_alt: Some(coastal_house_alt),
2264 });
2265
2266 generator_stats.success(site.name(), GenStatPlotKind::House);
2267 } else {
2268 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2269 }
2270 },
2271 3 if airship_docks < 1 => {
2272 let size = 9u32;
2279 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
2280 if let Some((aabr, door_tile, _, _)) = attempt(32, || {
2281 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
2282 }) {
2283 let coastal_airship_dock = plot::CoastalAirshipDock::generate(
2284 land,
2285 &mut reseed(&mut rng),
2286 &site,
2287 door_tile,
2288 aabr,
2289 );
2290 let coastal_airship_dock_alt = coastal_airship_dock.alt;
2291 let plot = site.create_plot(Plot {
2292 kind: PlotKind::CoastalAirshipDock(coastal_airship_dock),
2293 root_tile: aabr.center(),
2294 tiles: aabr_tiles(aabr).collect(),
2295 });
2296
2297 site.blit_aabr(aabr, Tile {
2298 kind: TileKind::Building,
2299 plot: Some(plot),
2300 hard_alt: Some(coastal_airship_dock_alt),
2301 });
2302 airship_docks += 1;
2303 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
2304 } else {
2305 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2306 }
2307 },
2308 4 => {
2310 Self::generate_farm(false, &mut rng, &mut site, land);
2311 },
2312 5 => {
2313 Self::generate_barn(false, &mut rng, &mut site, land, index);
2314 },
2315 _ => {},
2316 }
2317 }
2318 site
2319 }
2320
2321 pub fn generate_desert_city(
2322 land: &Land,
2323 index: IndexRef,
2324 rng: &mut impl Rng,
2325 origin: Vec2<i32>,
2326 generator_stats: &mut SitesGenMeta,
2327 ) -> Self {
2328 let mut rng = reseed(rng);
2329
2330 let name = NameGen::location(&mut rng).generate_arabic();
2331 let mut site = Site {
2332 origin,
2333 name: Some(name.clone()),
2334 kind: Some(SiteKind::DesertCity),
2335 ..Site::default()
2336 };
2337 let road_kind = plot::RoadKind {
2338 lights: plot::RoadLights::Default,
2339 material: plot::RoadMaterial::Sandstone,
2340 };
2341
2342 site.demarcate_obstacles(land);
2344 const DESERT_CITY_PLAZA_RADIUS: u32 = 3;
2347 const DESERT_CITY_PLAZA_SEARCH_INNER: u32 = 19;
2348 const DESERT_CITY_PLAZA_SEARCH_WIDTH: u32 = 12;
2349 generator_stats.add(site.name(), GenStatSiteKind::DesertCity);
2350 site.make_initial_plaza(
2351 land,
2352 index,
2353 &mut rng,
2354 DESERT_CITY_PLAZA_RADIUS,
2355 DESERT_CITY_PLAZA_SEARCH_INNER,
2356 DESERT_CITY_PLAZA_SEARCH_WIDTH,
2357 generator_stats,
2358 &name,
2359 road_kind,
2360 );
2361
2362 let size = 17.0 as i32;
2363 let aabr = Aabr {
2364 min: Vec2::broadcast(-size),
2365 max: Vec2::broadcast(size),
2366 };
2367
2368 let desert_city_arena =
2369 plot::DesertCityArena::generate(land, &mut reseed(&mut rng), &site, aabr);
2370
2371 let desert_city_arena_alt = desert_city_arena.alt;
2372 let plot = site.create_plot(Plot {
2373 kind: PlotKind::DesertCityArena(desert_city_arena),
2374 root_tile: aabr.center(),
2375 tiles: aabr_tiles(aabr).collect(),
2376 });
2377
2378 site.blit_aabr(aabr, Tile {
2379 kind: TileKind::Building,
2380 plot: Some(plot),
2381 hard_alt: Some(desert_city_arena_alt),
2382 });
2383
2384 let build_chance =
2385 Lottery::from(vec![(20.0, 1), (10.0, 2), (15.0, 3), (10.0, 4), (0.0, 5)]);
2386
2387 let mut temples = 0;
2388 let mut airship_docks = 0;
2389 let mut campfires = 0;
2390
2391 for _ in 0..35 {
2392 match *build_chance.choose_seeded(rng.random()) {
2393 1 => {
2395 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2396 generator_stats.attempt(site.name(), GenStatPlotKind::MultiPlot);
2397 let campfire = campfires < 4;
2398 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2399 site.find_roadside_aabr(
2400 &mut rng,
2401 8..(size + 1).pow(2),
2402 Extent2::broadcast(size),
2403 )
2404 }) {
2405 let desert_city_multi_plot = plot::DesertCityMultiPlot::generate(
2406 land,
2407 &mut reseed(&mut rng),
2408 &site,
2409 door_tile,
2410 door_dir,
2411 aabr,
2412 campfire,
2413 alt,
2414 );
2415 let desert_city_multi_plot_alt = desert_city_multi_plot.alt;
2416 let plot = site.create_plot(Plot {
2417 kind: PlotKind::DesertCityMultiPlot(desert_city_multi_plot),
2418 root_tile: aabr.center(),
2419 tiles: aabr_tiles(aabr).collect(),
2420 });
2421
2422 site.blit_aabr(aabr, Tile {
2423 kind: TileKind::Building,
2424 plot: Some(plot),
2425 hard_alt: Some(desert_city_multi_plot_alt),
2426 });
2427 campfires += 1;
2428 generator_stats.success(site.name(), GenStatPlotKind::MultiPlot);
2429 } else {
2430 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2431 }
2432 },
2433 2 if temples < 1 => {
2435 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2436 generator_stats.attempt(site.name(), GenStatPlotKind::Temple);
2437 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2438 site.find_roadside_aabr(
2439 &mut rng,
2440 8..(size + 1).pow(2),
2441 Extent2::broadcast(size),
2442 )
2443 }) {
2444 let desert_city_temple = plot::DesertCityTemple::generate(
2445 land,
2446 &mut reseed(&mut rng),
2447 &site,
2448 door_tile,
2449 door_dir,
2450 aabr,
2451 alt,
2452 );
2453 let desert_city_temple_alt = desert_city_temple.alt;
2454 let plot = site.create_plot(Plot {
2455 kind: PlotKind::DesertCityTemple(desert_city_temple),
2456 root_tile: aabr.center(),
2457 tiles: aabr_tiles(aabr).collect(),
2458 });
2459
2460 site.blit_aabr(aabr, Tile {
2461 kind: TileKind::Building,
2462 plot: Some(plot),
2463 hard_alt: Some(desert_city_temple_alt),
2464 });
2465 temples += 1;
2466 generator_stats.success(site.name(), GenStatPlotKind::Temple);
2467 }
2468 },
2469 3 if airship_docks < 1 => {
2470 let size = 9u32;
2477 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
2478 if let Some((aabr, door_tile, door_dir, alt)) = attempt(100, || {
2479 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
2480 }) {
2481 let desert_city_airship_dock = plot::DesertCityAirshipDock::generate(
2482 land,
2483 &mut reseed(&mut rng),
2484 &site,
2485 door_tile,
2486 door_dir,
2487 aabr,
2488 alt,
2489 );
2490 let desert_city_airship_dock_alt = desert_city_airship_dock.alt;
2491 let plot = site.create_plot(Plot {
2492 kind: PlotKind::DesertCityAirshipDock(desert_city_airship_dock),
2493 root_tile: aabr.center(),
2494 tiles: aabr_tiles(aabr).collect(),
2495 });
2496
2497 site.blit_aabr(aabr, Tile {
2498 kind: TileKind::Building,
2499 plot: Some(plot),
2500 hard_alt: Some(desert_city_airship_dock_alt),
2501 });
2502 airship_docks += 1;
2503 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
2504 } else {
2505 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2506 }
2507 },
2508 4 => {
2510 Self::generate_farm(true, &mut rng, &mut site, land);
2511 },
2512 5 => {
2515 Self::generate_barn(true, &mut rng, &mut site, land, index);
2516 },
2517 _ => {},
2518 }
2519 }
2520 site
2521 }
2522
2523 pub fn generate_farm(
2524 is_desert: bool,
2525 mut rng: &mut impl Rng,
2526 site: &mut Site,
2527 land: &Land,
2528 ) -> bool {
2529 let size = (3.0 + rng.random::<f32>().powf(5.0) * 6.0).round() as u32;
2530 if let Some((aabr, door_tile, door_dir, _alt)) = attempt(32, || {
2531 site.find_rural_aabr(&mut rng, 6..(size + 1).pow(2), Extent2::broadcast(size))
2532 }) {
2533 let field = plot::FarmField::generate(
2534 land,
2535 &mut reseed(&mut rng),
2536 site,
2537 door_tile,
2538 door_dir,
2539 aabr,
2540 is_desert,
2541 );
2542
2543 let field_alt = field.alt;
2544 let plot = site.create_plot(Plot {
2545 kind: PlotKind::FarmField(field),
2546 root_tile: aabr.center(),
2547 tiles: aabr_tiles(aabr).collect(),
2548 });
2549
2550 site.blit_aabr(aabr, Tile {
2551 kind: TileKind::Field,
2552 plot: Some(plot),
2553 hard_alt: Some(field_alt),
2554 });
2555 true
2556 } else {
2557 false
2558 }
2559 }
2560
2561 pub fn generate_barn(
2562 is_desert: bool,
2563 mut rng: &mut impl Rng,
2564 site: &mut Site,
2565 land: &Land,
2566 index: IndexRef,
2567 ) -> bool {
2568 let size = (7.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2569 if let Some((aabr, door_tile, door_dir, _alt)) = attempt(32, || {
2570 site.find_rural_aabr(&mut rng, 7..(size + 1).pow(2), Extent2::broadcast(size))
2571 }) {
2572 let bounds = Aabr {
2573 min: site.tile_wpos(aabr.min),
2574 max: site.tile_wpos(aabr.max),
2575 };
2576
2577 let gradient_avg = get_gradient_average(bounds, land);
2580
2581 if gradient_avg > 0.5 {
2582 false
2583 } else {
2584 let barn = plot::Barn::generate(
2585 land,
2586 index,
2587 &mut reseed(&mut rng),
2588 site,
2589 door_tile,
2590 door_dir,
2591 aabr,
2592 is_desert,
2593 );
2594 let barn_alt = barn.alt;
2595 let plot = site.create_plot(Plot {
2596 kind: PlotKind::Barn(barn),
2597 root_tile: aabr.center(),
2598 tiles: aabr_tiles(aabr).collect(),
2599 });
2600
2601 site.blit_aabr(aabr, Tile {
2602 kind: TileKind::Building,
2603 plot: Some(plot),
2604 hard_alt: Some(barn_alt),
2605 });
2606
2607 true
2608 }
2609 } else {
2610 false
2611 }
2612 }
2613
2614 pub fn generate_haniwa(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2615 let mut rng = reseed(rng);
2616 let mut site = Site {
2617 origin,
2618 name: Some(format!(
2619 "{} {}",
2620 NameGen::location(&mut rng).generate_haniwa(),
2621 [
2622 "Catacombs",
2623 "Crypt",
2624 "Tomb",
2625 "Gravemound",
2626 "Tunnels",
2627 "Vault",
2628 "Chambers",
2629 "Halls",
2630 "Tumulus",
2631 "Barrow",
2632 ]
2633 .choose(&mut rng)
2634 .unwrap()
2635 )),
2636 kind: Some(SiteKind::Haniwa),
2637 ..Site::default()
2638 };
2639 let size = 24.0 as i32;
2640 let aabr = Aabr {
2641 min: Vec2::broadcast(-size),
2642 max: Vec2::broadcast(size),
2643 };
2644 {
2645 let haniwa = plot::Haniwa::generate(land, &mut reseed(&mut rng), &site, aabr);
2646 let haniwa_alt = haniwa.alt;
2647 let plot = site.create_plot(Plot {
2648 kind: PlotKind::Haniwa(haniwa),
2649 root_tile: aabr.center(),
2650 tiles: aabr_tiles(aabr).collect(),
2651 });
2652
2653 site.blit_aabr(aabr, Tile {
2654 kind: TileKind::Building,
2655 plot: Some(plot),
2656 hard_alt: Some(haniwa_alt),
2657 });
2658 }
2659 site
2660 }
2661
2662 pub fn generate_chapel_site(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2663 let mut rng = reseed(rng);
2664 let mut site = Site {
2665 origin,
2666 name: Some(NameGen::location(&mut rng).generate_danari()),
2667 kind: Some(SiteKind::ChapelSite),
2668 ..Site::default()
2669 };
2670
2671 let size = 10.0 as i32;
2673 let aabr = Aabr {
2674 min: Vec2::broadcast(-size),
2675 max: Vec2::broadcast(size),
2676 };
2677 {
2678 let sea_chapel = plot::SeaChapel::generate(land, &mut reseed(&mut rng), &site, aabr);
2679 let sea_chapel_alt = sea_chapel.alt;
2680 let plot = site.create_plot(Plot {
2681 kind: PlotKind::SeaChapel(sea_chapel),
2682 root_tile: aabr.center(),
2683 tiles: aabr_tiles(aabr).collect(),
2684 });
2685
2686 site.blit_aabr(aabr, Tile {
2687 kind: TileKind::Building,
2688 plot: Some(plot),
2689 hard_alt: Some(sea_chapel_alt),
2690 });
2691 }
2692 site
2693 }
2694
2695 pub fn generate_pirate_hideout(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2696 let mut rng = reseed(rng);
2697 let mut site = Site {
2698 origin,
2699 name: None,
2700 kind: Some(SiteKind::PirateHideout),
2701 ..Site::default()
2702 };
2703
2704 let size = 8.0 as i32;
2705 let aabr = Aabr {
2706 min: Vec2::broadcast(-size),
2707 max: Vec2::broadcast(size),
2708 };
2709 {
2710 let pirate_hideout =
2711 plot::PirateHideout::generate(land, &mut reseed(&mut rng), &site, aabr);
2712 let pirate_hideout_alt = pirate_hideout.alt;
2713 let plot = site.create_plot(Plot {
2714 kind: PlotKind::PirateHideout(pirate_hideout),
2715 root_tile: aabr.center(),
2716 tiles: aabr_tiles(aabr).collect(),
2717 });
2718
2719 site.blit_aabr(aabr, Tile {
2720 kind: TileKind::Building,
2721 plot: Some(plot),
2722 hard_alt: Some(pirate_hideout_alt),
2723 });
2724 }
2725 site
2726 }
2727
2728 pub fn generate_jungle_ruin(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2729 let mut rng = reseed(rng);
2730 let mut site = Site {
2731 origin,
2732 name: None,
2733 kind: Some(SiteKind::JungleRuin),
2734 ..Site::default()
2735 };
2736 let size = 8.0 as i32;
2737 let aabr = Aabr {
2738 min: Vec2::broadcast(-size),
2739 max: Vec2::broadcast(size),
2740 };
2741 {
2742 let jungle_ruin = plot::JungleRuin::generate(land, &mut reseed(&mut rng), &site, aabr);
2743 let jungle_ruin_alt = jungle_ruin.alt;
2744 let plot = site.create_plot(Plot {
2745 kind: PlotKind::JungleRuin(jungle_ruin),
2746 root_tile: aabr.center(),
2747 tiles: aabr_tiles(aabr).collect(),
2748 });
2749
2750 site.blit_aabr(aabr, Tile {
2751 kind: TileKind::Building,
2752 plot: Some(plot),
2753 hard_alt: Some(jungle_ruin_alt),
2754 });
2755 }
2756 site
2757 }
2758
2759 pub fn generate_rock_circle(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2760 let mut rng = reseed(rng);
2761 let mut site = Site {
2762 origin,
2763 kind: Some(SiteKind::RockCircle),
2764 ..Site::default()
2765 };
2766 let size = 8.0 as i32;
2767 let aabr = Aabr {
2768 min: Vec2::broadcast(-size),
2769 max: Vec2::broadcast(size),
2770 };
2771 {
2772 let rock_circle = plot::RockCircle::generate(land, &mut reseed(&mut rng), &site, aabr);
2773 let rock_circle_alt = rock_circle.alt;
2774 let plot = site.create_plot(Plot {
2775 kind: PlotKind::RockCircle(rock_circle),
2776 root_tile: aabr.center(),
2777 tiles: aabr_tiles(aabr).collect(),
2778 });
2779
2780 site.blit_aabr(aabr, Tile {
2781 kind: TileKind::Building,
2782 plot: Some(plot),
2783 hard_alt: Some(rock_circle_alt),
2784 });
2785 }
2786 site
2787 }
2788
2789 pub fn generate_troll_cave(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2790 let mut rng = reseed(rng);
2791 let mut site = Site {
2792 origin,
2793 name: None,
2794 kind: Some(SiteKind::TrollCave),
2795 ..Site::default()
2796 };
2797 let size = 2.0 as i32;
2798 let aabr = Aabr {
2799 min: Vec2::broadcast(-size),
2800 max: Vec2::broadcast(size),
2801 };
2802 let site_temp = temp_at_wpos(land, origin);
2803 {
2804 let troll_cave =
2805 plot::TrollCave::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp);
2806 let troll_cave_alt = troll_cave.alt;
2807 let plot = site.create_plot(Plot {
2808 kind: PlotKind::TrollCave(troll_cave),
2809 root_tile: aabr.center(),
2810 tiles: aabr_tiles(aabr).collect(),
2811 });
2812
2813 site.blit_aabr(aabr, Tile {
2814 kind: TileKind::Building,
2815 plot: Some(plot),
2816 hard_alt: Some(troll_cave_alt),
2817 });
2818 }
2819 site
2820 }
2821
2822 pub fn generate_camp(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2823 let mut rng = reseed(rng);
2824 let mut site = Site {
2825 origin,
2826 name: None,
2827 kind: Some(SiteKind::Camp),
2828 ..Site::default()
2829 };
2830 let size = 2.0 as i32;
2831 let aabr = Aabr {
2832 min: Vec2::broadcast(-size),
2833 max: Vec2::broadcast(size),
2834 };
2835 let site_temp = temp_at_wpos(land, origin);
2836 {
2837 let camp = plot::Camp::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp);
2838 let camp_alt = camp.alt;
2839 let plot = site.create_plot(Plot {
2840 kind: PlotKind::Camp(camp),
2841 root_tile: aabr.center(),
2842 tiles: aabr_tiles(aabr).collect(),
2843 });
2844
2845 site.blit_aabr(aabr, Tile {
2846 kind: TileKind::Building,
2847 plot: Some(plot),
2848 hard_alt: Some(camp_alt),
2849 });
2850 }
2851 site
2852 }
2853
2854 pub fn generate_cultist(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2855 let mut rng = reseed(rng);
2856 let mut site = Site {
2857 origin,
2858 name: Some({
2859 let name = NameGen::location(&mut rng).generate();
2860 match rng.random_range(0..5) {
2861 0 => format!("{} Dungeon", name),
2862 1 => format!("{} Lair", name),
2863 2 => format!("{} Crib", name),
2864 3 => format!("{} Catacombs", name),
2865 _ => format!("{} Pit", name),
2866 }
2867 }),
2868 kind: Some(SiteKind::Cultist),
2869 ..Site::default()
2870 };
2871 let size = 22.0 as i32;
2872 let aabr = Aabr {
2873 min: Vec2::broadcast(-size),
2874 max: Vec2::broadcast(size),
2875 };
2876 {
2877 let cultist = plot::Cultist::generate(land, &mut reseed(&mut rng), &site, aabr);
2878 let cultist_alt = cultist.alt;
2879 let plot = site.create_plot(Plot {
2880 kind: PlotKind::Cultist(cultist),
2881 root_tile: aabr.center(),
2882 tiles: aabr_tiles(aabr).collect(),
2883 });
2884
2885 site.blit_aabr(aabr, Tile {
2886 kind: TileKind::Building,
2887 plot: Some(plot),
2888 hard_alt: Some(cultist_alt),
2889 });
2890 }
2891 site
2892 }
2893
2894 pub fn generate_sahagin(
2895 land: &Land,
2896 index: IndexRef,
2897 rng: &mut impl Rng,
2898 origin: Vec2<i32>,
2899 ) -> Self {
2900 let mut rng = reseed(rng);
2901 let mut site = Site {
2902 origin,
2903 name: Some({
2904 let name = NameGen::location(&mut rng).generate();
2905 match rng.random_range(0..5) {
2906 0 => format!("{} Isle", name),
2907 1 => format!("{} Islet", name),
2908 2 => format!("{} Key", name),
2909 3 => format!("{} Cay", name),
2910 _ => format!("{} Rock", name),
2911 }
2912 }),
2913 kind: Some(SiteKind::Sahagin),
2914 ..Site::default()
2915 };
2916 let size = 16.0 as i32;
2917 let aabr = Aabr {
2918 min: Vec2::broadcast(-size),
2919 max: Vec2::broadcast(size),
2920 };
2921 {
2922 let sahagin = plot::Sahagin::generate(land, index, &mut reseed(&mut rng), &site, aabr);
2923 let sahagin_alt = sahagin.alt;
2924 let plot = site.create_plot(Plot {
2925 kind: PlotKind::Sahagin(sahagin),
2926 root_tile: aabr.center(),
2927 tiles: aabr_tiles(aabr).collect(),
2928 });
2929
2930 site.blit_aabr(aabr, Tile {
2931 kind: TileKind::Building,
2932 plot: Some(plot),
2933 hard_alt: Some(sahagin_alt),
2934 });
2935 }
2936 site
2937 }
2938
2939 pub fn generate_vampire_castle(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2940 let mut rng = reseed(rng);
2941 let mut site = Site {
2942 origin,
2943 name: Some({
2944 let name = NameGen::location(&mut rng).generate_vampire();
2945 match rng.random_range(0..4) {
2946 0 => format!("{} Keep", name),
2947 1 => format!("{} Chateau", name),
2948 2 => format!("{} Manor", name),
2949 _ => format!("{} Palace", name),
2950 }
2951 }),
2952 kind: Some(SiteKind::VampireCastle),
2953 ..Site::default()
2954 };
2955 let size = 22.0 as i32;
2956 let aabr = Aabr {
2957 min: Vec2::broadcast(-size),
2958 max: Vec2::broadcast(size),
2959 };
2960 {
2961 let vampire_castle =
2962 plot::VampireCastle::generate(land, &mut reseed(&mut rng), &site, aabr);
2963 let vampire_castle_alt = vampire_castle.alt;
2964 let plot = site.create_plot(Plot {
2965 kind: PlotKind::VampireCastle(vampire_castle),
2966 root_tile: aabr.center(),
2967 tiles: aabr_tiles(aabr).collect(),
2968 });
2969
2970 site.blit_aabr(aabr, Tile {
2971 kind: TileKind::Building,
2972 plot: Some(plot),
2973 hard_alt: Some(vampire_castle_alt),
2974 });
2975 }
2976 site
2977 }
2978
2979 pub fn generate_bridge(
2980 land: &Land,
2981 index: IndexRef,
2982 rng: &mut impl Rng,
2983 start_chunk: Vec2<i32>,
2984 end_chunk: Vec2<i32>,
2985 ) -> Self {
2986 let mut rng = reseed(rng);
2987 let start = TerrainChunkSize::center_wpos(start_chunk);
2988 let end = TerrainChunkSize::center_wpos(end_chunk);
2989 let origin = (start + end) / 2;
2990
2991 let mut site = Site {
2992 origin,
2993 name: Some(format!(
2994 "Bridge of {}",
2995 NameGen::location(&mut rng).generate_town()
2996 )),
2997 kind: Some(SiteKind::Bridge(start_chunk, end_chunk)),
2998 ..Site::default()
2999 };
3000
3001 let start_tile = site.wpos_tile_pos(start);
3002 let end_tile = site.wpos_tile_pos(end);
3003
3004 let width = 1;
3005
3006 let orth = (start_tile - end_tile).yx().map(|dir| dir.signum().abs());
3007
3008 let start_aabr = Aabr {
3009 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
3010 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
3011 };
3012
3013 let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
3014
3015 let start_tile = site.wpos_tile_pos(bridge.start.xy());
3016 let end_tile = site.wpos_tile_pos(bridge.end.xy());
3017
3018 let width = (bridge.width() + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
3019 let aabr = Aabr {
3020 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
3021 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
3022 };
3023
3024 let line = LineSegment2 {
3025 start: site.tile_wpos(bridge.dir.select_aabr_with(start_aabr, start_aabr.center())),
3026 end: site.tile_wpos(
3027 bridge
3028 .dir
3029 .opposite()
3030 .select_aabr_with(start_aabr, start_aabr.center()),
3031 ),
3032 }
3033 .as_();
3034
3035 for y in start_aabr.min.y..start_aabr.max.y {
3036 for x in start_aabr.min.x..start_aabr.max.x {
3037 let tpos = Vec2::new(x, y);
3038 let tile_aabr = Aabr {
3039 min: site.tile_wpos(tpos),
3040 max: site.tile_wpos(tpos + 1) - 1,
3041 };
3042 if let Some(tile) = site.tiles.get_mut(tpos) {
3043 let closest_point = line.projected_point(tile_aabr.center().as_());
3044 let w = TILE_SIZE as f32;
3045 if tile_aabr
3046 .as_()
3047 .projected_point(closest_point)
3048 .distance_squared(closest_point)
3049 < w.powi(2)
3050 {
3051 tile.kind = TileKind::Path {
3052 closest_pos: closest_point,
3053 path: Path { width: w },
3054 };
3055 }
3056 }
3057 }
3058 }
3059
3060 let plot = site.create_plot(Plot {
3061 kind: PlotKind::Bridge(bridge),
3062 root_tile: start_tile,
3063 tiles: aabr_tiles(aabr).collect(),
3064 });
3065
3066 site.blit_aabr(aabr, Tile {
3067 kind: TileKind::Bridge,
3068 plot: Some(plot),
3069 hard_alt: None,
3070 });
3071
3072 site
3073 }
3074
3075 pub fn wpos_tile_pos(&self, wpos2d: Vec2<i32>) -> Vec2<i32> {
3076 (wpos2d - self.origin).map(|e| e.div_euclid(TILE_SIZE as i32))
3077 }
3078
3079 pub fn wpos_tile(&self, wpos2d: Vec2<i32>) -> &Tile {
3080 self.tiles.get(self.wpos_tile_pos(wpos2d))
3081 }
3082
3083 pub fn tile_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> { self.origin + tile * TILE_SIZE as i32 }
3084
3085 pub fn tile_center_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> {
3086 self.origin + tile * TILE_SIZE as i32 + TILE_SIZE as i32 / 2
3087 }
3088
3089 pub fn render_tile(&self, canvas: &mut Canvas, tpos: Vec2<i32>) {
3090 let tile = self.tiles.get(tpos);
3091 let twpos = self.tile_wpos(tpos);
3092 let border = TILE_SIZE as i32;
3093 let cols = (-border..TILE_SIZE as i32 + border).flat_map(|y| {
3094 (-border..TILE_SIZE as i32 + border)
3095 .map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))
3096 });
3097 if let TileKind::Path { closest_pos, path } = &tile.kind {
3098 let near_connections = CARDINALS.iter().filter_map(|rpos| {
3099 let npos = tpos + rpos;
3100 let tile = self.tiles.get(npos);
3101 let tile_aabr = Aabr {
3102 min: self.tile_wpos(tpos).map(|e| e as f32),
3103 max: self.tile_wpos(tpos + 1).map(|e| e as f32) - 1.0,
3104 };
3105 match tile.kind {
3106 TileKind::Road { a, b, w, .. } => {
3107 if let Some(PlotKind::Road(road)) = tile.plot.map(|p| &self.plot(p).kind) {
3108 let start = road.path.nodes[a as usize];
3109 let end = road.path.nodes[b as usize];
3110 let dir = Dir2::from_vec2(end - start);
3111 let orth = dir.orthogonal();
3112 let aabr = Aabr {
3113 min: self.tile_center_wpos(start)
3114 - w as i32 * 2 * orth.to_vec2()
3115 - dir.to_vec2() * TILE_SIZE as i32 / 2,
3116 max: self.tile_center_wpos(end)
3117 + w as i32 * 2 * orth.to_vec2()
3118 + dir.to_vec2() * TILE_SIZE as i32 / 2,
3119 }
3120 .made_valid()
3121 .as_();
3122 Some(aabr)
3123 } else {
3124 Some(tile_aabr)
3125 }
3126 },
3127 TileKind::Bridge | TileKind::Plaza => Some(tile_aabr),
3128 _ => tile
3129 .plot
3130 .and_then(|plot| self.plot(plot).door_tile())
3131 .is_some_and(|door_tile| door_tile == npos)
3132 .then_some(tile_aabr),
3133 }
3134 });
3135 cols.for_each(|(wpos2d, _offs)| {
3136 let wpos2df = wpos2d.map(|e| e as f32);
3137
3138 if closest_pos.distance_squared(wpos2d.as_()) < path.width.powi(2)
3139 || near_connections
3140 .clone()
3141 .map(|aabr| aabr.distance_to_point(wpos2df))
3142 .min_by_key(|d| (*d * 100.0) as i32)
3143 .is_some_and(|d| d <= 1.5)
3144 {
3145 let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
3146 let sub_surface_color = canvas
3147 .col(wpos2d)
3148 .map_or(Rgb::zero(), |col| col.sub_surface_color);
3149 for z in -8..6 {
3150 let wpos = Vec3::new(wpos2d.x, wpos2d.y, alt + z);
3151 canvas.map(wpos, |b| {
3152 if b.kind() == BlockKind::Snow {
3153 b.into_vacant()
3154 } else if b.is_filled() {
3155 if b.is_terrain() {
3156 Block::new(
3157 BlockKind::Earth,
3158 path.surface_color((sub_surface_color * 255.0).as_(), wpos),
3159 )
3160 } else {
3161 b
3162 }
3163 } else {
3164 b.into_vacant()
3165 }
3166 })
3167 }
3168 }
3169 });
3170 }
3171 }
3172
3173 pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut ChaCha8Rng) {
3174 let tile_aabr = Aabr {
3175 min: self.wpos_tile_pos(canvas.wpos()) - 1,
3176 max: self
3177 .wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2)
3178 + 3, };
3180
3181 let mut plots = DHashSet::default();
3183
3184 for y in tile_aabr.min.y..tile_aabr.max.y {
3185 for x in tile_aabr.min.x..tile_aabr.max.x {
3186 self.render_tile(canvas, Vec2::new(x, y));
3187
3188 if let Some(plot) = self.tiles.get(Vec2::new(x, y)).plot {
3189 plots.insert(plot);
3190 }
3191 }
3192 }
3193
3194 canvas.foreach_col(|canvas, wpos2d, col| {
3195 let tile = self.wpos_tile(wpos2d);
3196 for z_off in (-2..4).rev() {
3197 if let Some(plot) = tile.plot.map(|p| self.plot(p)) {
3198 canvas.map_resource(
3199 wpos2d.with_z(plot.rel_terrain_offset(col) + z_off),
3200 |block| {
3201 plot.terrain_surface_at(wpos2d, block, dynamic_rng, col, z_off, self)
3202 .unwrap_or(block)
3203 },
3204 );
3205 }
3206 }
3207 });
3208
3209 for (id, plot) in self.plots.iter() {
3211 if matches!(&plot.kind, PlotKind::GiantTree(_)) {
3212 plots.insert(id);
3213 }
3214 }
3215
3216 let mut plots_to_render = plots.into_iter().collect::<Vec<_>>();
3217 plots_to_render.sort_unstable_by_key(|plot| (self.plot(*plot).render_ordering(), *plot));
3219
3220 let wpos2d = canvas.info().wpos();
3221 let chunk_aabr = Aabr {
3222 min: wpos2d,
3223 max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
3224 };
3225
3226 let info = canvas.info();
3227
3228 for plot in plots_to_render {
3229 let (prim_tree, fills, mut entities) = self.plot(plot).render_collect(self, canvas);
3230
3231 let mut spawn = |pos, last_block| {
3232 if let Some(entity) = match &self.plots[plot].kind {
3233 PlotKind::GiantTree(tree) => tree.entity_at(pos, &last_block, dynamic_rng),
3234 _ => None,
3235 } {
3236 entities.push(entity);
3237 }
3238 };
3239
3240 let mut entities_from_structure_blocks = Vec::<EntityInfo>::new();
3241
3242 for (prim, fill) in fills {
3243 for mut aabb in Fill::get_bounds_disjoint(&prim_tree, prim) {
3244 aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z);
3245 aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z);
3246
3247 for x in aabb.min.x..aabb.max.x {
3248 for y in aabb.min.y..aabb.max.y {
3249 let wpos = Vec2::new(x, y);
3250 let col_tile = self.wpos_tile(wpos);
3251 if
3252 col_tile
3254 .plot
3255 .and_then(|p| self.plots[p].z_range())
3256 .zip(self.plots[plot].z_range())
3257 .is_some_and(|(a, b)| a.end > b.end)
3258 {
3259 continue;
3260 }
3261 let mut last_block = None;
3262
3263 let col = canvas
3264 .col(wpos)
3265 .map(|col| col.get_info())
3266 .unwrap_or_default();
3267
3268 for z in aabb.min.z..aabb.max.z {
3269 let pos = Vec3::new(x, y, z);
3270
3271 let mut sprite_cfg = None;
3272
3273 let map = |block| {
3274 let (current_block, _sb, entity_path) = fill.sample_at(
3275 &prim_tree,
3276 prim,
3277 pos,
3278 &info,
3279 block,
3280 &mut sprite_cfg,
3281 &col,
3282 );
3283
3284 if let Some(spec) = entity_path {
3285 let entity = EntityInfo::at(pos.as_());
3286 let mut loadout_rng = rand::rng();
3287 entities_from_structure_blocks.push(
3288 entity.with_asset_expect(&spec, &mut loadout_rng, None),
3289 );
3290 };
3291
3292 if let (Some(last_block), None) = (last_block, current_block) {
3293 spawn(pos, last_block);
3294 }
3295 last_block = current_block;
3296 current_block.unwrap_or(block)
3297 };
3298
3299 match fill {
3300 Fill::ResourceSprite { .. } | Fill::Prefab(..) => {
3301 canvas.map_resource(pos, map)
3302 },
3303 _ => canvas.map(pos, map),
3304 };
3305
3306 if let Some(sprite_cfg) = sprite_cfg {
3307 canvas.set_sprite_cfg(pos, sprite_cfg);
3308 }
3309 }
3310 if let Some(block) = last_block {
3311 spawn(Vec3::new(x, y, aabb.max.z), block);
3312 }
3313 }
3314 }
3315 }
3316 }
3317
3318 for entity in entities {
3319 canvas.spawn(EntitySpawn::Entity(Box::new(entity)));
3320 }
3321
3322 for entity in entities_from_structure_blocks {
3323 canvas.spawn(EntitySpawn::Entity(Box::new(entity)));
3324 }
3325 }
3326 }
3327
3328 pub fn apply_supplement(
3329 &self,
3330 dynamic_rng: &mut impl Rng,
3331 wpos2d: Vec2<i32>,
3332 supplement: &mut crate::ChunkSupplement,
3333 ) {
3334 for (_, plot) in self.plots.iter() {
3335 match &plot.kind {
3336 PlotKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement),
3337 PlotKind::Adlet(a) => a.apply_supplement(dynamic_rng, wpos2d, supplement),
3338 _ => {},
3339 }
3340 }
3341 }
3342}
3343
3344pub fn test_site() -> Site {
3345 let index = crate::index::Index::new(0);
3346 let index_ref = IndexRef {
3347 colors: &index.colors(),
3348 features: &index.features(),
3349 index: &index,
3350 };
3351 let mut gen_meta = SitesGenMeta::new(0);
3352 Site::generate_city(
3353 &Land::empty(),
3354 index_ref,
3355 &mut rand::rng(),
3356 Vec2::zero(),
3357 0.5,
3358 None,
3359 &mut gen_meta,
3360 )
3361}
3362
3363fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
3364 if land
3365 .get_chunk_wpos(wpos)
3366 .is_none_or(|c| c.river.near_water())
3367 {
3368 Some(HazardKind::Water)
3369 } else {
3370 Some(land.get_gradient_approx(wpos))
3371 .filter(|g| *g > 0.8)
3372 .map(|gradient| HazardKind::Hill { gradient })
3373 }
3374}
3375
3376fn temp_at_wpos(land: &Land, wpos: Vec2<i32>) -> f32 {
3377 land.get_chunk_wpos(wpos)
3378 .map(|c| c.temp)
3379 .unwrap_or(CONFIG.temperate_temp)
3380}
3381
3382pub fn aabr_tiles(aabr: Aabr<i32>) -> impl Iterator<Item = Vec2<i32>> {
3383 (0..aabr.size().h)
3384 .flat_map(move |y| (0..aabr.size().w).map(move |x| aabr.min + Vec2::new(x, y)))
3385}
3386
3387fn get_gradient_average(aabr: Aabr<i32>, land: &Land) -> f32 {
3391 let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as i32;
3392
3393 let mut gradient_sum = 0.0;
3394 let mut gradient_sample_count = 0;
3395
3396 let aabr_center = aabr.center();
3397 let range_x_min = aabr_center.x - chunk_size;
3398 let range_x_max = aabr_center.x + chunk_size;
3399 let range_y_min = aabr_center.y - chunk_size;
3400 let range_y_max = aabr_center.y + chunk_size;
3401
3402 for x_pos in (range_x_min..=range_x_max).step_by(chunk_size as usize) {
3403 for y_pos in (range_y_min..=range_y_max).step_by(chunk_size as usize) {
3404 let gradient_at_pos = land.get_gradient_approx(Vec2::new(x_pos, y_pos));
3405 gradient_sum += gradient_at_pos;
3406 gradient_sample_count += 1;
3407 }
3408 }
3409
3410 gradient_sum / (gradient_sample_count as f32)
3411}