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, EntitySpawn},
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, _)) = 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 index,
1485 &mut reseed(&mut rng),
1486 &site,
1487 door_tile,
1488 door_dir,
1489 aabr,
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, _)) = 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 );
1870 let cliff_town_airship_dock_alt = cliff_town_airship_dock.alt;
1871 let plot = site.create_plot(Plot {
1872 kind: PlotKind::CliffTownAirshipDock(cliff_town_airship_dock),
1873 root_tile: aabr.center(),
1874 tiles: aabr_tiles(aabr).collect(),
1875 });
1876
1877 site.blit_aabr(aabr, Tile {
1878 kind: TileKind::Building,
1879 plot: Some(plot),
1880 hard_alt: Some(cliff_town_airship_dock_alt),
1881 });
1882 airship_docks += 1;
1883 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
1884 } else {
1885 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1886 }
1887 },
1888 _ => {},
1889 }
1890 }
1891
1892 site.demarcate_obstacles(land);
1893 site
1894 }
1895
1896 pub fn generate_savannah_town(
1897 land: &Land,
1898 index: IndexRef,
1899 rng: &mut impl Rng,
1900 origin: Vec2<i32>,
1901 generator_stats: &mut SitesGenMeta,
1902 ) -> Self {
1903 let mut rng = reseed(rng);
1904 let name = NameGen::location(&mut rng).generate_savannah_custom();
1905 let mut site = Site {
1906 origin,
1907 name: Some(name.clone()),
1908 kind: Some(SiteKind::SavannahTown),
1909 ..Site::default()
1910 };
1911 let road_kind = plot::RoadKind {
1912 lights: plot::RoadLights::Default,
1913 material: plot::RoadMaterial::Dirt,
1914 };
1915
1916 site.demarcate_obstacles(land);
1918 generator_stats.add(site.name(), GenStatSiteKind::SavannahTown);
1919 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1920
1921 let mut workshops = 0;
1922 let mut airship_dock = 0;
1923 let build_chance = Lottery::from(vec![(25.0, 1), (5.0, 2), (5.0, 3), (15.0, 4), (5.0, 5)]);
1924
1925 for _ in 0..50 {
1926 match *build_chance.choose_seeded(rng.random()) {
1927 n if (n == 2 && workshops < 3) || workshops == 0 => {
1928 let size = (4.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
1930 generator_stats.attempt(site.name(), GenStatPlotKind::Workshop);
1931 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1932 site.find_roadside_aabr(
1933 &mut rng,
1934 4..(size + 1).pow(2),
1935 Extent2::broadcast(size),
1936 )
1937 }) {
1938 let savannah_workshop = plot::SavannahWorkshop::generate(
1939 land,
1940 &mut reseed(&mut rng),
1941 &site,
1942 door_tile,
1943 door_dir,
1944 aabr,
1945 alt,
1946 );
1947 let savannah_workshop_alt = savannah_workshop.alt;
1948 let plot = site.create_plot(Plot {
1949 kind: PlotKind::SavannahWorkshop(savannah_workshop),
1950 root_tile: aabr.center(),
1951 tiles: aabr_tiles(aabr).collect(),
1952 });
1953
1954 site.blit_aabr(aabr, Tile {
1955 kind: TileKind::Building,
1956 plot: Some(plot),
1957 hard_alt: Some(savannah_workshop_alt),
1958 });
1959 workshops += 1;
1960 generator_stats.success(site.name(), GenStatPlotKind::Workshop);
1961 } else {
1962 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1963 }
1964 },
1965 1 => {
1966 let size = (4.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
1968 generator_stats.attempt(site.name(), GenStatPlotKind::House);
1969 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1970 site.find_roadside_aabr(
1971 &mut rng,
1972 4..(size + 1).pow(2),
1973 Extent2::broadcast(size),
1974 )
1975 }) {
1976 let savannah_hut = plot::SavannahHut::generate(
1977 land,
1978 &mut reseed(&mut rng),
1979 &site,
1980 door_tile,
1981 door_dir,
1982 aabr,
1983 alt,
1984 );
1985 let savannah_hut_alt = savannah_hut.alt;
1986 let plot = site.create_plot(Plot {
1987 kind: PlotKind::SavannahHut(savannah_hut),
1988 root_tile: aabr.center(),
1989 tiles: aabr_tiles(aabr).collect(),
1990 });
1991
1992 site.blit_aabr(aabr, Tile {
1993 kind: TileKind::Building,
1994 plot: Some(plot),
1995 hard_alt: Some(savannah_hut_alt),
1996 });
1997 generator_stats.success(site.name(), GenStatPlotKind::House);
1998 } else {
1999 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2000 }
2001 },
2002 3 if airship_dock < 1 => {
2003 let size = 9u32;
2005 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
2006 if let Some((aabr, door_tile, _, _)) = attempt(48, || {
2007 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
2008 }) {
2009 let savannah_airship_dock = plot::SavannahAirshipDock::generate(
2010 land,
2011 &mut reseed(&mut rng),
2012 &site,
2013 door_tile,
2014 aabr,
2015 );
2016 let savannah_airship_dock_alt = savannah_airship_dock.alt;
2017 let plot = site.create_plot(Plot {
2018 kind: PlotKind::SavannahAirshipDock(savannah_airship_dock),
2019 root_tile: aabr.center(),
2020 tiles: aabr_tiles(aabr).collect(),
2021 });
2022
2023 site.blit_aabr(aabr, Tile {
2024 kind: TileKind::Building,
2025 plot: Some(plot),
2026 hard_alt: Some(savannah_airship_dock_alt),
2027 });
2028 airship_dock += 1;
2029 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
2030 } else {
2031 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2032 }
2033 },
2034 4 => {
2036 Self::generate_farm(false, &mut rng, &mut site, land);
2037 },
2038 5 => {
2039 Self::generate_barn(false, &mut rng, &mut site, land, index);
2040 },
2041 _ => {},
2042 }
2043 }
2044 site
2045 }
2046
2047 pub fn generate_coastal_town(
2048 land: &Land,
2049 index: IndexRef,
2050 rng: &mut impl Rng,
2051 origin: Vec2<i32>,
2052 generator_stats: &mut SitesGenMeta,
2053 ) -> Self {
2054 let mut rng = reseed(rng);
2055 let name = NameGen::location(&mut rng).generate_danari();
2056 let mut site = Site {
2057 origin,
2058 name: Some(name.clone()),
2059 kind: Some(SiteKind::CoastalTown),
2060 ..Site::default()
2061 };
2062 let road_kind = plot::RoadKind {
2063 lights: plot::RoadLights::Default,
2064 material: plot::RoadMaterial::Marble,
2065 };
2066
2067 site.demarcate_obstacles(land);
2069 generator_stats.add(site.name(), GenStatSiteKind::CoastalTown);
2070 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
2071
2072 let mut workshops = 0;
2073 let build_chance = Lottery::from(vec![(38.0, 1), (5.0, 2), (15.0, 3), (15.0, 4), (5.0, 5)]);
2074 let mut airship_docks = 0;
2075 for _ in 0..55 {
2076 match *build_chance.choose_seeded(rng.random()) {
2077 n if (n == 2 && workshops < 3) || workshops == 0 => {
2078 let size = (7.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2080 generator_stats.attempt(site.name(), GenStatPlotKind::Workshop);
2081 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2082 site.find_roadside_aabr(
2083 &mut rng,
2084 7..(size + 1).pow(2),
2085 Extent2::broadcast(size),
2086 )
2087 }) {
2088 let coastal_workshop = plot::CoastalWorkshop::generate(
2089 land,
2090 &mut reseed(&mut rng),
2091 &site,
2092 door_tile,
2093 door_dir,
2094 aabr,
2095 alt,
2096 );
2097 let coastal_workshop_alt = coastal_workshop.alt;
2098 let plot = site.create_plot(Plot {
2099 kind: PlotKind::CoastalWorkshop(coastal_workshop),
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(coastal_workshop_alt),
2108 });
2109 workshops += 1;
2110 generator_stats.success(site.name(), GenStatPlotKind::Workshop);
2111 } else {
2112 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2113 }
2114 },
2115 1 => {
2116 let size = (7.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2118 generator_stats.attempt(site.name(), GenStatPlotKind::House);
2119 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2120 site.find_roadside_aabr(
2121 &mut rng,
2122 7..(size + 1).pow(2),
2123 Extent2::broadcast(size),
2124 )
2125 }) {
2126 let coastal_house = plot::CoastalHouse::generate(
2127 land,
2128 &mut reseed(&mut rng),
2129 &site,
2130 door_tile,
2131 door_dir,
2132 aabr,
2133 alt,
2134 );
2135 let coastal_house_alt = coastal_house.alt;
2136 let plot = site.create_plot(Plot {
2137 kind: PlotKind::CoastalHouse(coastal_house),
2138 root_tile: aabr.center(),
2139 tiles: aabr_tiles(aabr).collect(),
2140 });
2141
2142 site.blit_aabr(aabr, Tile {
2143 kind: TileKind::Building,
2144 plot: Some(plot),
2145 hard_alt: Some(coastal_house_alt),
2146 });
2147
2148 generator_stats.success(site.name(), GenStatPlotKind::House);
2149 } else {
2150 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2151 }
2152 },
2153 3 if airship_docks < 1 => {
2154 let size = 9u32;
2161 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
2162 if let Some((aabr, door_tile, _, _)) = attempt(32, || {
2163 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
2164 }) {
2165 let coastal_airship_dock = plot::CoastalAirshipDock::generate(
2166 land,
2167 &mut reseed(&mut rng),
2168 &site,
2169 door_tile,
2170 aabr,
2171 );
2172 let coastal_airship_dock_alt = coastal_airship_dock.alt;
2173 let plot = site.create_plot(Plot {
2174 kind: PlotKind::CoastalAirshipDock(coastal_airship_dock),
2175 root_tile: aabr.center(),
2176 tiles: aabr_tiles(aabr).collect(),
2177 });
2178
2179 site.blit_aabr(aabr, Tile {
2180 kind: TileKind::Building,
2181 plot: Some(plot),
2182 hard_alt: Some(coastal_airship_dock_alt),
2183 });
2184 airship_docks += 1;
2185 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
2186 } else {
2187 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2188 }
2189 },
2190 4 => {
2192 Self::generate_farm(false, &mut rng, &mut site, land);
2193 },
2194 5 => {
2195 Self::generate_barn(false, &mut rng, &mut site, land, index);
2196 },
2197 _ => {},
2198 }
2199 }
2200 site
2201 }
2202
2203 pub fn generate_desert_city(
2204 land: &Land,
2205 index: IndexRef,
2206 rng: &mut impl Rng,
2207 origin: Vec2<i32>,
2208 generator_stats: &mut SitesGenMeta,
2209 ) -> Self {
2210 let mut rng = reseed(rng);
2211
2212 let name = NameGen::location(&mut rng).generate_arabic();
2213 let mut site = Site {
2214 origin,
2215 name: Some(name.clone()),
2216 kind: Some(SiteKind::DesertCity),
2217 ..Site::default()
2218 };
2219 let road_kind = plot::RoadKind {
2220 lights: plot::RoadLights::Default,
2221 material: plot::RoadMaterial::Sandstone,
2222 };
2223
2224 site.demarcate_obstacles(land);
2226 const DESERT_CITY_PLAZA_RADIUS: u32 = 3;
2229 const DESERT_CITY_PLAZA_SEARCH_INNER: u32 = 19;
2230 const DESERT_CITY_PLAZA_SEARCH_WIDTH: u32 = 12;
2231 generator_stats.add(site.name(), GenStatSiteKind::DesertCity);
2232 site.make_initial_plaza(
2233 land,
2234 index,
2235 &mut rng,
2236 DESERT_CITY_PLAZA_RADIUS,
2237 DESERT_CITY_PLAZA_SEARCH_INNER,
2238 DESERT_CITY_PLAZA_SEARCH_WIDTH,
2239 generator_stats,
2240 &name,
2241 road_kind,
2242 );
2243
2244 let size = 17.0 as i32;
2245 let aabr = Aabr {
2246 min: Vec2::broadcast(-size),
2247 max: Vec2::broadcast(size),
2248 };
2249
2250 let desert_city_arena =
2251 plot::DesertCityArena::generate(land, &mut reseed(&mut rng), &site, aabr);
2252
2253 let desert_city_arena_alt = desert_city_arena.alt;
2254 let plot = site.create_plot(Plot {
2255 kind: PlotKind::DesertCityArena(desert_city_arena),
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(desert_city_arena_alt),
2264 });
2265
2266 let build_chance =
2267 Lottery::from(vec![(20.0, 1), (10.0, 2), (15.0, 3), (10.0, 4), (0.0, 5)]);
2268
2269 let mut temples = 0;
2270 let mut airship_docks = 0;
2271 let mut campfires = 0;
2272
2273 for _ in 0..35 {
2274 match *build_chance.choose_seeded(rng.random()) {
2275 1 => {
2277 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2278 generator_stats.attempt(site.name(), GenStatPlotKind::MultiPlot);
2279 let campfire = campfires < 4;
2280 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2281 site.find_roadside_aabr(
2282 &mut rng,
2283 8..(size + 1).pow(2),
2284 Extent2::broadcast(size),
2285 )
2286 }) {
2287 let desert_city_multi_plot = plot::DesertCityMultiPlot::generate(
2288 land,
2289 &mut reseed(&mut rng),
2290 &site,
2291 door_tile,
2292 door_dir,
2293 aabr,
2294 campfire,
2295 alt,
2296 );
2297 let desert_city_multi_plot_alt = desert_city_multi_plot.alt;
2298 let plot = site.create_plot(Plot {
2299 kind: PlotKind::DesertCityMultiPlot(desert_city_multi_plot),
2300 root_tile: aabr.center(),
2301 tiles: aabr_tiles(aabr).collect(),
2302 });
2303
2304 site.blit_aabr(aabr, Tile {
2305 kind: TileKind::Building,
2306 plot: Some(plot),
2307 hard_alt: Some(desert_city_multi_plot_alt),
2308 });
2309 campfires += 1;
2310 generator_stats.success(site.name(), GenStatPlotKind::MultiPlot);
2311 } else {
2312 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2313 }
2314 },
2315 2 if temples < 1 => {
2317 let size = (9.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2318 generator_stats.attempt(site.name(), GenStatPlotKind::Temple);
2319 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2320 site.find_roadside_aabr(
2321 &mut rng,
2322 8..(size + 1).pow(2),
2323 Extent2::broadcast(size),
2324 )
2325 }) {
2326 let desert_city_temple = plot::DesertCityTemple::generate(
2327 land,
2328 &mut reseed(&mut rng),
2329 &site,
2330 door_tile,
2331 door_dir,
2332 aabr,
2333 alt,
2334 );
2335 let desert_city_temple_alt = desert_city_temple.alt;
2336 let plot = site.create_plot(Plot {
2337 kind: PlotKind::DesertCityTemple(desert_city_temple),
2338 root_tile: aabr.center(),
2339 tiles: aabr_tiles(aabr).collect(),
2340 });
2341
2342 site.blit_aabr(aabr, Tile {
2343 kind: TileKind::Building,
2344 plot: Some(plot),
2345 hard_alt: Some(desert_city_temple_alt),
2346 });
2347 temples += 1;
2348 generator_stats.success(site.name(), GenStatPlotKind::Temple);
2349 }
2350 },
2351 3 if airship_docks < 1 => {
2352 let size = 9u32;
2359 generator_stats.attempt(site.name(), GenStatPlotKind::AirshipDock);
2360 if let Some((aabr, door_tile, door_dir, alt)) = attempt(100, || {
2361 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
2362 }) {
2363 let desert_city_airship_dock = plot::DesertCityAirshipDock::generate(
2364 land,
2365 &mut reseed(&mut rng),
2366 &site,
2367 door_tile,
2368 door_dir,
2369 aabr,
2370 alt,
2371 );
2372 let desert_city_airship_dock_alt = desert_city_airship_dock.alt;
2373 let plot = site.create_plot(Plot {
2374 kind: PlotKind::DesertCityAirshipDock(desert_city_airship_dock),
2375 root_tile: aabr.center(),
2376 tiles: aabr_tiles(aabr).collect(),
2377 });
2378
2379 site.blit_aabr(aabr, Tile {
2380 kind: TileKind::Building,
2381 plot: Some(plot),
2382 hard_alt: Some(desert_city_airship_dock_alt),
2383 });
2384 airship_docks += 1;
2385 generator_stats.success(site.name(), GenStatPlotKind::AirshipDock);
2386 } else {
2387 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2388 }
2389 },
2390 4 => {
2392 Self::generate_farm(true, &mut rng, &mut site, land);
2393 },
2394 5 => {
2397 Self::generate_barn(true, &mut rng, &mut site, land, index);
2398 },
2399 _ => {},
2400 }
2401 }
2402 site
2403 }
2404
2405 pub fn generate_farm(
2406 is_desert: bool,
2407 mut rng: &mut impl Rng,
2408 site: &mut Site,
2409 land: &Land,
2410 ) -> bool {
2411 let size = (3.0 + rng.random::<f32>().powf(5.0) * 6.0).round() as u32;
2412 if let Some((aabr, door_tile, door_dir, _alt)) = attempt(32, || {
2413 site.find_rural_aabr(&mut rng, 6..(size + 1).pow(2), Extent2::broadcast(size))
2414 }) {
2415 let field = plot::FarmField::generate(
2416 land,
2417 &mut reseed(&mut rng),
2418 site,
2419 door_tile,
2420 door_dir,
2421 aabr,
2422 is_desert,
2423 );
2424
2425 let field_alt = field.alt;
2426 let plot = site.create_plot(Plot {
2427 kind: PlotKind::FarmField(field),
2428 root_tile: aabr.center(),
2429 tiles: aabr_tiles(aabr).collect(),
2430 });
2431
2432 site.blit_aabr(aabr, Tile {
2433 kind: TileKind::Field,
2434 plot: Some(plot),
2435 hard_alt: Some(field_alt),
2436 });
2437 true
2438 } else {
2439 false
2440 }
2441 }
2442
2443 pub fn generate_barn(
2444 is_desert: bool,
2445 mut rng: &mut impl Rng,
2446 site: &mut Site,
2447 land: &Land,
2448 index: IndexRef,
2449 ) -> bool {
2450 let size = (7.0 + rng.random::<f32>().powf(5.0) * 1.5).round() as u32;
2451 if let Some((aabr, door_tile, door_dir, _alt)) = attempt(32, || {
2452 site.find_rural_aabr(&mut rng, 7..(size + 1).pow(2), Extent2::broadcast(size))
2453 }) {
2454 let bounds = Aabr {
2455 min: site.tile_wpos(aabr.min),
2456 max: site.tile_wpos(aabr.max),
2457 };
2458
2459 let gradient_avg = get_gradient_average(bounds, land);
2462
2463 if gradient_avg > 0.05 {
2464 false
2465 } else {
2466 let barn = plot::Barn::generate(
2467 land,
2468 index,
2469 &mut reseed(&mut rng),
2470 site,
2471 door_tile,
2472 door_dir,
2473 aabr,
2474 is_desert,
2475 );
2476 let barn_alt = barn.alt;
2477 let plot = site.create_plot(Plot {
2478 kind: PlotKind::Barn(barn),
2479 root_tile: aabr.center(),
2480 tiles: aabr_tiles(aabr).collect(),
2481 });
2482
2483 site.blit_aabr(aabr, Tile {
2484 kind: TileKind::Building,
2485 plot: Some(plot),
2486 hard_alt: Some(barn_alt),
2487 });
2488
2489 true
2490 }
2491 } else {
2492 false
2493 }
2494 }
2495
2496 pub fn generate_haniwa(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2497 let mut rng = reseed(rng);
2498 let mut site = Site {
2499 origin,
2500 name: Some(format!(
2501 "{} {}",
2502 NameGen::location(&mut rng).generate_haniwa(),
2503 [
2504 "Catacombs",
2505 "Crypt",
2506 "Tomb",
2507 "Gravemound",
2508 "Tunnels",
2509 "Vault",
2510 "Chambers",
2511 "Halls",
2512 "Tumulus",
2513 "Barrow",
2514 ]
2515 .choose(&mut rng)
2516 .unwrap()
2517 )),
2518 kind: Some(SiteKind::Haniwa),
2519 ..Site::default()
2520 };
2521 let size = 24.0 as i32;
2522 let aabr = Aabr {
2523 min: Vec2::broadcast(-size),
2524 max: Vec2::broadcast(size),
2525 };
2526 {
2527 let haniwa = plot::Haniwa::generate(land, &mut reseed(&mut rng), &site, aabr);
2528 let haniwa_alt = haniwa.alt;
2529 let plot = site.create_plot(Plot {
2530 kind: PlotKind::Haniwa(haniwa),
2531 root_tile: aabr.center(),
2532 tiles: aabr_tiles(aabr).collect(),
2533 });
2534
2535 site.blit_aabr(aabr, Tile {
2536 kind: TileKind::Building,
2537 plot: Some(plot),
2538 hard_alt: Some(haniwa_alt),
2539 });
2540 }
2541 site
2542 }
2543
2544 pub fn generate_chapel_site(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2545 let mut rng = reseed(rng);
2546 let mut site = Site {
2547 origin,
2548 name: Some(NameGen::location(&mut rng).generate_danari()),
2549 kind: Some(SiteKind::ChapelSite),
2550 ..Site::default()
2551 };
2552
2553 let size = 10.0 as i32;
2555 let aabr = Aabr {
2556 min: Vec2::broadcast(-size),
2557 max: Vec2::broadcast(size),
2558 };
2559 {
2560 let sea_chapel = plot::SeaChapel::generate(land, &mut reseed(&mut rng), &site, aabr);
2561 let sea_chapel_alt = sea_chapel.alt;
2562 let plot = site.create_plot(Plot {
2563 kind: PlotKind::SeaChapel(sea_chapel),
2564 root_tile: aabr.center(),
2565 tiles: aabr_tiles(aabr).collect(),
2566 });
2567
2568 site.blit_aabr(aabr, Tile {
2569 kind: TileKind::Building,
2570 plot: Some(plot),
2571 hard_alt: Some(sea_chapel_alt),
2572 });
2573 }
2574 site
2575 }
2576
2577 pub fn generate_pirate_hideout(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2578 let mut rng = reseed(rng);
2579 let mut site = Site {
2580 origin,
2581 name: None,
2582 kind: Some(SiteKind::PirateHideout),
2583 ..Site::default()
2584 };
2585
2586 let size = 8.0 as i32;
2587 let aabr = Aabr {
2588 min: Vec2::broadcast(-size),
2589 max: Vec2::broadcast(size),
2590 };
2591 {
2592 let pirate_hideout =
2593 plot::PirateHideout::generate(land, &mut reseed(&mut rng), &site, aabr);
2594 let pirate_hideout_alt = pirate_hideout.alt;
2595 let plot = site.create_plot(Plot {
2596 kind: PlotKind::PirateHideout(pirate_hideout),
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(pirate_hideout_alt),
2605 });
2606 }
2607 site
2608 }
2609
2610 pub fn generate_jungle_ruin(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2611 let mut rng = reseed(rng);
2612 let mut site = Site {
2613 origin,
2614 name: None,
2615 kind: Some(SiteKind::JungleRuin),
2616 ..Site::default()
2617 };
2618 let size = 8.0 as i32;
2619 let aabr = Aabr {
2620 min: Vec2::broadcast(-size),
2621 max: Vec2::broadcast(size),
2622 };
2623 {
2624 let jungle_ruin = plot::JungleRuin::generate(land, &mut reseed(&mut rng), &site, aabr);
2625 let jungle_ruin_alt = jungle_ruin.alt;
2626 let plot = site.create_plot(Plot {
2627 kind: PlotKind::JungleRuin(jungle_ruin),
2628 root_tile: aabr.center(),
2629 tiles: aabr_tiles(aabr).collect(),
2630 });
2631
2632 site.blit_aabr(aabr, Tile {
2633 kind: TileKind::Building,
2634 plot: Some(plot),
2635 hard_alt: Some(jungle_ruin_alt),
2636 });
2637 }
2638 site
2639 }
2640
2641 pub fn generate_rock_circle(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2642 let mut rng = reseed(rng);
2643 let mut site = Site {
2644 origin,
2645 kind: Some(SiteKind::RockCircle),
2646 ..Site::default()
2647 };
2648 let size = 8.0 as i32;
2649 let aabr = Aabr {
2650 min: Vec2::broadcast(-size),
2651 max: Vec2::broadcast(size),
2652 };
2653 {
2654 let rock_circle = plot::RockCircle::generate(land, &mut reseed(&mut rng), &site, aabr);
2655 let rock_circle_alt = rock_circle.alt;
2656 let plot = site.create_plot(Plot {
2657 kind: PlotKind::RockCircle(rock_circle),
2658 root_tile: aabr.center(),
2659 tiles: aabr_tiles(aabr).collect(),
2660 });
2661
2662 site.blit_aabr(aabr, Tile {
2663 kind: TileKind::Building,
2664 plot: Some(plot),
2665 hard_alt: Some(rock_circle_alt),
2666 });
2667 }
2668 site
2669 }
2670
2671 pub fn generate_troll_cave(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2672 let mut rng = reseed(rng);
2673 let mut site = Site {
2674 origin,
2675 name: None,
2676 kind: Some(SiteKind::TrollCave),
2677 ..Site::default()
2678 };
2679 let size = 2.0 as i32;
2680 let aabr = Aabr {
2681 min: Vec2::broadcast(-size),
2682 max: Vec2::broadcast(size),
2683 };
2684 let site_temp = temp_at_wpos(land, origin);
2685 {
2686 let troll_cave =
2687 plot::TrollCave::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp);
2688 let troll_cave_alt = troll_cave.alt;
2689 let plot = site.create_plot(Plot {
2690 kind: PlotKind::TrollCave(troll_cave),
2691 root_tile: aabr.center(),
2692 tiles: aabr_tiles(aabr).collect(),
2693 });
2694
2695 site.blit_aabr(aabr, Tile {
2696 kind: TileKind::Building,
2697 plot: Some(plot),
2698 hard_alt: Some(troll_cave_alt),
2699 });
2700 }
2701 site
2702 }
2703
2704 pub fn generate_camp(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2705 let mut rng = reseed(rng);
2706 let mut site = Site {
2707 origin,
2708 name: None,
2709 kind: Some(SiteKind::Camp),
2710 ..Site::default()
2711 };
2712 let size = 2.0 as i32;
2713 let aabr = Aabr {
2714 min: Vec2::broadcast(-size),
2715 max: Vec2::broadcast(size),
2716 };
2717 let site_temp = temp_at_wpos(land, origin);
2718 {
2719 let camp = plot::Camp::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp);
2720 let camp_alt = camp.alt;
2721 let plot = site.create_plot(Plot {
2722 kind: PlotKind::Camp(camp),
2723 root_tile: aabr.center(),
2724 tiles: aabr_tiles(aabr).collect(),
2725 });
2726
2727 site.blit_aabr(aabr, Tile {
2728 kind: TileKind::Building,
2729 plot: Some(plot),
2730 hard_alt: Some(camp_alt),
2731 });
2732 }
2733 site
2734 }
2735
2736 pub fn generate_cultist(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2737 let mut rng = reseed(rng);
2738 let mut site = Site {
2739 origin,
2740 name: Some({
2741 let name = NameGen::location(&mut rng).generate();
2742 match rng.random_range(0..5) {
2743 0 => format!("{} Dungeon", name),
2744 1 => format!("{} Lair", name),
2745 2 => format!("{} Crib", name),
2746 3 => format!("{} Catacombs", name),
2747 _ => format!("{} Pit", name),
2748 }
2749 }),
2750 kind: Some(SiteKind::Cultist),
2751 ..Site::default()
2752 };
2753 let size = 22.0 as i32;
2754 let aabr = Aabr {
2755 min: Vec2::broadcast(-size),
2756 max: Vec2::broadcast(size),
2757 };
2758 {
2759 let cultist = plot::Cultist::generate(land, &mut reseed(&mut rng), &site, aabr);
2760 let cultist_alt = cultist.alt;
2761 let plot = site.create_plot(Plot {
2762 kind: PlotKind::Cultist(cultist),
2763 root_tile: aabr.center(),
2764 tiles: aabr_tiles(aabr).collect(),
2765 });
2766
2767 site.blit_aabr(aabr, Tile {
2768 kind: TileKind::Building,
2769 plot: Some(plot),
2770 hard_alt: Some(cultist_alt),
2771 });
2772 }
2773 site
2774 }
2775
2776 pub fn generate_sahagin(
2777 land: &Land,
2778 index: IndexRef,
2779 rng: &mut impl Rng,
2780 origin: Vec2<i32>,
2781 ) -> Self {
2782 let mut rng = reseed(rng);
2783 let mut site = Site {
2784 origin,
2785 name: Some({
2786 let name = NameGen::location(&mut rng).generate();
2787 match rng.random_range(0..5) {
2788 0 => format!("{} Isle", name),
2789 1 => format!("{} Islet", name),
2790 2 => format!("{} Key", name),
2791 3 => format!("{} Cay", name),
2792 _ => format!("{} Rock", name),
2793 }
2794 }),
2795 kind: Some(SiteKind::Sahagin),
2796 ..Site::default()
2797 };
2798 let size = 16.0 as i32;
2799 let aabr = Aabr {
2800 min: Vec2::broadcast(-size),
2801 max: Vec2::broadcast(size),
2802 };
2803 {
2804 let sahagin = plot::Sahagin::generate(land, index, &mut reseed(&mut rng), &site, aabr);
2805 let sahagin_alt = sahagin.alt;
2806 let plot = site.create_plot(Plot {
2807 kind: PlotKind::Sahagin(sahagin),
2808 root_tile: aabr.center(),
2809 tiles: aabr_tiles(aabr).collect(),
2810 });
2811
2812 site.blit_aabr(aabr, Tile {
2813 kind: TileKind::Building,
2814 plot: Some(plot),
2815 hard_alt: Some(sahagin_alt),
2816 });
2817 }
2818 site
2819 }
2820
2821 pub fn generate_vampire_castle(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2822 let mut rng = reseed(rng);
2823 let mut site = Site {
2824 origin,
2825 name: Some({
2826 let name = NameGen::location(&mut rng).generate_vampire();
2827 match rng.random_range(0..4) {
2828 0 => format!("{} Keep", name),
2829 1 => format!("{} Chateau", name),
2830 2 => format!("{} Manor", name),
2831 _ => format!("{} Palace", name),
2832 }
2833 }),
2834 kind: Some(SiteKind::VampireCastle),
2835 ..Site::default()
2836 };
2837 let size = 22.0 as i32;
2838 let aabr = Aabr {
2839 min: Vec2::broadcast(-size),
2840 max: Vec2::broadcast(size),
2841 };
2842 {
2843 let vampire_castle =
2844 plot::VampireCastle::generate(land, &mut reseed(&mut rng), &site, aabr);
2845 let vampire_castle_alt = vampire_castle.alt;
2846 let plot = site.create_plot(Plot {
2847 kind: PlotKind::VampireCastle(vampire_castle),
2848 root_tile: aabr.center(),
2849 tiles: aabr_tiles(aabr).collect(),
2850 });
2851
2852 site.blit_aabr(aabr, Tile {
2853 kind: TileKind::Building,
2854 plot: Some(plot),
2855 hard_alt: Some(vampire_castle_alt),
2856 });
2857 }
2858 site
2859 }
2860
2861 pub fn generate_bridge(
2862 land: &Land,
2863 index: IndexRef,
2864 rng: &mut impl Rng,
2865 start_chunk: Vec2<i32>,
2866 end_chunk: Vec2<i32>,
2867 ) -> Self {
2868 let mut rng = reseed(rng);
2869 let start = TerrainChunkSize::center_wpos(start_chunk);
2870 let end = TerrainChunkSize::center_wpos(end_chunk);
2871 let origin = (start + end) / 2;
2872
2873 let mut site = Site {
2874 origin,
2875 name: Some(format!(
2876 "Bridge of {}",
2877 NameGen::location(&mut rng).generate_town()
2878 )),
2879 kind: Some(SiteKind::Bridge(start_chunk, end_chunk)),
2880 ..Site::default()
2881 };
2882
2883 let start_tile = site.wpos_tile_pos(start);
2884 let end_tile = site.wpos_tile_pos(end);
2885
2886 let width = 1;
2887
2888 let orth = (start_tile - end_tile).yx().map(|dir| dir.signum().abs());
2889
2890 let start_aabr = Aabr {
2891 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
2892 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
2893 };
2894
2895 let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
2896
2897 let start_tile = site.wpos_tile_pos(bridge.start.xy());
2898 let end_tile = site.wpos_tile_pos(bridge.end.xy());
2899
2900 let width = (bridge.width() + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
2901 let aabr = Aabr {
2902 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
2903 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
2904 };
2905
2906 let line = LineSegment2 {
2907 start: site.tile_wpos(bridge.dir.select_aabr_with(start_aabr, start_aabr.center())),
2908 end: site.tile_wpos(
2909 bridge
2910 .dir
2911 .opposite()
2912 .select_aabr_with(start_aabr, start_aabr.center()),
2913 ),
2914 }
2915 .as_();
2916
2917 for y in start_aabr.min.y..start_aabr.max.y {
2918 for x in start_aabr.min.x..start_aabr.max.x {
2919 let tpos = Vec2::new(x, y);
2920 let tile_aabr = Aabr {
2921 min: site.tile_wpos(tpos),
2922 max: site.tile_wpos(tpos + 1) - 1,
2923 };
2924 if let Some(tile) = site.tiles.get_mut(tpos) {
2925 let closest_point = line.projected_point(tile_aabr.center().as_());
2926 let w = TILE_SIZE as f32;
2927 if tile_aabr
2928 .as_()
2929 .projected_point(closest_point)
2930 .distance_squared(closest_point)
2931 < w.powi(2)
2932 {
2933 tile.kind = TileKind::Path {
2934 closest_pos: closest_point,
2935 path: Path { width: w },
2936 };
2937 }
2938 }
2939 }
2940 }
2941
2942 let plot = site.create_plot(Plot {
2943 kind: PlotKind::Bridge(bridge),
2944 root_tile: start_tile,
2945 tiles: aabr_tiles(aabr).collect(),
2946 });
2947
2948 site.blit_aabr(aabr, Tile {
2949 kind: TileKind::Bridge,
2950 plot: Some(plot),
2951 hard_alt: None,
2952 });
2953
2954 site
2955 }
2956
2957 pub fn wpos_tile_pos(&self, wpos2d: Vec2<i32>) -> Vec2<i32> {
2958 (wpos2d - self.origin).map(|e| e.div_euclid(TILE_SIZE as i32))
2959 }
2960
2961 pub fn wpos_tile(&self, wpos2d: Vec2<i32>) -> &Tile {
2962 self.tiles.get(self.wpos_tile_pos(wpos2d))
2963 }
2964
2965 pub fn tile_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> { self.origin + tile * TILE_SIZE as i32 }
2966
2967 pub fn tile_center_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> {
2968 self.origin + tile * TILE_SIZE as i32 + TILE_SIZE as i32 / 2
2969 }
2970
2971 pub fn render_tile(&self, canvas: &mut Canvas, tpos: Vec2<i32>) {
2972 let tile = self.tiles.get(tpos);
2973 let twpos = self.tile_wpos(tpos);
2974 let border = TILE_SIZE as i32;
2975 let cols = (-border..TILE_SIZE as i32 + border).flat_map(|y| {
2976 (-border..TILE_SIZE as i32 + border)
2977 .map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))
2978 });
2979 if let TileKind::Path { closest_pos, path } = &tile.kind {
2980 let near_connections = CARDINALS.iter().filter_map(|rpos| {
2981 let npos = tpos + rpos;
2982 let tile = self.tiles.get(npos);
2983 let tile_aabr = Aabr {
2984 min: self.tile_wpos(tpos).map(|e| e as f32),
2985 max: self.tile_wpos(tpos + 1).map(|e| e as f32) - 1.0,
2986 };
2987 match tile.kind {
2988 TileKind::Road { a, b, w } => {
2989 if let Some(PlotKind::Road(road)) = tile.plot.map(|p| &self.plot(p).kind) {
2990 let start = road.path.nodes[a as usize];
2991 let end = road.path.nodes[b as usize];
2992 let dir = Dir::from_vec2(end - start);
2993 let orth = dir.orthogonal();
2994 let aabr = Aabr {
2995 min: self.tile_center_wpos(start)
2996 - w as i32 * 2 * orth.to_vec2()
2997 - dir.to_vec2() * TILE_SIZE as i32 / 2,
2998 max: self.tile_center_wpos(end)
2999 + w as i32 * 2 * orth.to_vec2()
3000 + dir.to_vec2() * TILE_SIZE as i32 / 2,
3001 }
3002 .made_valid()
3003 .as_();
3004 Some(aabr)
3005 } else {
3006 None
3007 }
3008 },
3009 TileKind::Bridge | TileKind::Plaza => Some(tile_aabr),
3010 _ => tile
3011 .plot
3012 .and_then(|plot| self.plot(plot).kind().meta())
3013 .and_then(|meta| meta.door_tile())
3014 .is_some_and(|door_tile| door_tile == npos)
3015 .then_some(tile_aabr),
3016 }
3017 });
3018 cols.for_each(|(wpos2d, _offs)| {
3019 let wpos2df = wpos2d.map(|e| e as f32);
3020
3021 if closest_pos.distance_squared(wpos2d.as_()) < path.width.powi(2)
3022 || near_connections
3023 .clone()
3024 .map(|aabr| aabr.distance_to_point(wpos2df))
3025 .min_by_key(|d| (*d * 100.0) as i32)
3026 .is_some_and(|d| d <= 1.5)
3027 {
3028 let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
3029 let sub_surface_color = canvas
3030 .col(wpos2d)
3031 .map_or(Rgb::zero(), |col| col.sub_surface_color);
3032 for z in -8..6 {
3033 let wpos = Vec3::new(wpos2d.x, wpos2d.y, alt + z);
3034 canvas.map(wpos, |b| {
3035 if b.kind() == BlockKind::Snow {
3036 b.into_vacant()
3037 } else if b.is_filled() {
3038 if b.is_terrain() {
3039 Block::new(
3040 BlockKind::Earth,
3041 path.surface_color((sub_surface_color * 255.0).as_(), wpos),
3042 )
3043 } else {
3044 b
3045 }
3046 } else {
3047 b.into_vacant()
3048 }
3049 })
3050 }
3051 }
3052 });
3053 }
3054 }
3055
3056 pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
3057 let tile_aabr = Aabr {
3058 min: self.wpos_tile_pos(canvas.wpos()) - 1,
3059 max: self
3060 .wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2)
3061 + 3, };
3063
3064 let mut plots = DHashSet::default();
3066
3067 for y in tile_aabr.min.y..tile_aabr.max.y {
3068 for x in tile_aabr.min.x..tile_aabr.max.x {
3069 self.render_tile(canvas, Vec2::new(x, y));
3070
3071 if let Some(plot) = self.tiles.get(Vec2::new(x, y)).plot {
3072 plots.insert(plot);
3073 }
3074 }
3075 }
3076
3077 canvas.foreach_col(|canvas, wpos2d, col| {
3078 let tile = self.wpos_tile(wpos2d);
3079 for z_off in (-2..4).rev() {
3080 if let Some(plot) = tile.plot.map(|p| &self.plots[p]) {
3081 canvas.map_resource(
3082 Vec3::new(
3083 wpos2d.x,
3084 wpos2d.y,
3085 foreach_plot!(&plot.kind, plot => plot.rel_terrain_offset(col)) + z_off,
3086 ),
3087 |block| {
3088 foreach_plot!(
3089 &plot.kind,
3090 plot => plot.terrain_surface_at(
3091 wpos2d,
3092 block,
3093 dynamic_rng,
3094 col,
3095 z_off,
3096 self,
3097 ).unwrap_or(block),
3098 )
3099 },
3100 );
3101 }
3102 }
3103 });
3104
3105 for (id, plot) in self.plots.iter() {
3107 if matches!(&plot.kind, PlotKind::GiantTree(_)) {
3108 plots.insert(id);
3109 }
3110 }
3111
3112 let mut plots_to_render = plots.into_iter().collect::<Vec<_>>();
3113 plots_to_render
3115 .sort_unstable_by_key(|plot| (self.plots[*plot].kind.render_ordering(), *plot));
3116
3117 let wpos2d = canvas.info().wpos();
3118 let chunk_aabr = Aabr {
3119 min: wpos2d,
3120 max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
3121 };
3122
3123 let info = canvas.info();
3124
3125 for plot in plots_to_render {
3126 let (prim_tree, fills, mut entities) =
3127 foreach_plot!(&self.plots[plot].kind, plot => plot.render_collect(self, canvas));
3128
3129 let mut spawn = |pos, last_block| {
3130 if let Some(entity) = match &self.plots[plot].kind {
3131 PlotKind::GiantTree(tree) => tree.entity_at(pos, &last_block, dynamic_rng),
3132 _ => None,
3133 } {
3134 entities.push(entity);
3135 }
3136 };
3137
3138 let mut entities_from_structure_blocks = Vec::<EntityInfo>::new();
3139
3140 for (prim, fill) in fills {
3141 for mut aabb in Fill::get_bounds_disjoint(&prim_tree, prim) {
3142 aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z);
3143 aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z);
3144
3145 for x in aabb.min.x..aabb.max.x {
3146 for y in aabb.min.y..aabb.max.y {
3147 let wpos = Vec2::new(x, y);
3148 let col_tile = self.wpos_tile(wpos);
3149 if
3150 col_tile
3152 .plot
3153 .and_then(|p| self.plots[p].z_range())
3154 .zip(self.plots[plot].z_range())
3155 .is_some_and(|(a, b)| a.end > b.end)
3156 {
3157 continue;
3158 }
3159 let mut last_block = None;
3160
3161 let col = canvas
3162 .col(wpos)
3163 .map(|col| col.get_info())
3164 .unwrap_or_default();
3165
3166 for z in aabb.min.z..aabb.max.z {
3167 let pos = Vec3::new(x, y, z);
3168
3169 let mut sprite_cfg = None;
3170
3171 let map = |block| {
3172 let (current_block, _sb, entity_path) = fill.sample_at(
3173 &prim_tree,
3174 prim,
3175 pos,
3176 &info,
3177 block,
3178 &mut sprite_cfg,
3179 &col,
3180 );
3181
3182 if let Some(spec) = entity_path {
3183 let entity = EntityInfo::at(pos.as_());
3184 let mut loadout_rng = rand::rng();
3185 entities_from_structure_blocks.push(
3186 entity.with_asset_expect(&spec, &mut loadout_rng, None),
3187 );
3188 };
3189
3190 if let (Some(last_block), None) = (last_block, current_block) {
3191 spawn(pos, last_block);
3192 }
3193 last_block = current_block;
3194 current_block.unwrap_or(block)
3195 };
3196
3197 match fill {
3198 Fill::ResourceSprite { .. } | Fill::Prefab(..) => {
3199 canvas.map_resource(pos, map)
3200 },
3201 _ => canvas.map(pos, map),
3202 };
3203
3204 if let Some(sprite_cfg) = sprite_cfg {
3205 canvas.set_sprite_cfg(pos, sprite_cfg);
3206 }
3207 }
3208 if let Some(block) = last_block {
3209 spawn(Vec3::new(x, y, aabb.max.z), block);
3210 }
3211 }
3212 }
3213 }
3214 }
3215
3216 for entity in entities {
3217 canvas.spawn(EntitySpawn::Entity(Box::new(entity)));
3218 }
3219
3220 for entity in entities_from_structure_blocks {
3221 canvas.spawn(EntitySpawn::Entity(Box::new(entity)));
3222 }
3223 }
3224 }
3225
3226 pub fn apply_supplement(
3227 &self,
3228 dynamic_rng: &mut impl Rng,
3229 wpos2d: Vec2<i32>,
3230 supplement: &mut crate::ChunkSupplement,
3231 ) {
3232 for (_, plot) in self.plots.iter() {
3233 match &plot.kind {
3234 PlotKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement),
3235 PlotKind::Adlet(a) => a.apply_supplement(dynamic_rng, wpos2d, supplement),
3236 _ => {},
3237 }
3238 }
3239 }
3240}
3241
3242pub fn test_site() -> Site {
3243 let index = crate::index::Index::new(0);
3244 let index_ref = IndexRef {
3245 colors: &index.colors(),
3246 features: &index.features(),
3247 index: &index,
3248 };
3249 let mut gen_meta = SitesGenMeta::new(0);
3250 Site::generate_city(
3251 &Land::empty(),
3252 index_ref,
3253 &mut rand::rng(),
3254 Vec2::zero(),
3255 0.5,
3256 None,
3257 &mut gen_meta,
3258 )
3259}
3260
3261fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
3262 if land
3263 .get_chunk_wpos(wpos)
3264 .is_none_or(|c| c.river.near_water())
3265 {
3266 Some(HazardKind::Water)
3267 } else {
3268 Some(land.get_gradient_approx(wpos))
3269 .filter(|g| *g > 0.8)
3270 .map(|gradient| HazardKind::Hill { gradient })
3271 }
3272}
3273
3274fn temp_at_wpos(land: &Land, wpos: Vec2<i32>) -> f32 {
3275 land.get_chunk_wpos(wpos)
3276 .map(|c| c.temp)
3277 .unwrap_or(CONFIG.temperate_temp)
3278}
3279
3280pub fn aabr_tiles(aabr: Aabr<i32>) -> impl Iterator<Item = Vec2<i32>> {
3281 (0..aabr.size().h)
3282 .flat_map(move |y| (0..aabr.size().w).map(move |x| aabr.min + Vec2::new(x, y)))
3283}
3284
3285fn get_gradient_average(aabr: Aabr<i32>, land: &Land) -> f32 {
3289 let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as i32;
3290
3291 let mut gradient_sum = 0.0;
3292 let mut gradient_sample_count = 0;
3293
3294 let aabr_center = aabr.center();
3295 let range_x_min = aabr_center.x - chunk_size;
3296 let range_x_max = aabr_center.x + chunk_size;
3297 let range_y_min = aabr_center.y - chunk_size;
3298 let range_y_max = aabr_center.y + chunk_size;
3299
3300 for x_pos in (range_x_min..=range_x_max).step_by(chunk_size as usize) {
3301 for y_pos in (range_y_min..=range_y_max).step_by(chunk_size as usize) {
3302 let gradient_at_pos = land.get_gradient_approx(Vec2::new(x_pos, y_pos));
3303 gradient_sum += gradient_at_pos;
3304 gradient_sample_count += 1;
3305 }
3306 }
3307
3308 gradient_sum / (gradient_sample_count as f32)
3309}