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