1pub mod economy;
2mod gen;
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 gen::{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 spiral::Spiral2d,
31 store::{Id, Store},
32 terrain::{
33 Block, BlockKind, SiteKindMeta, SpriteKind, TerrainChunkSize,
34 site::{DungeonKindMeta, SettlementKindMeta},
35 },
36 vol::RectVolSize,
37};
38use common_net::msg::world_msg;
39use hashbrown::DefaultHashBuilder;
40use namegen::NameGen;
41use rand::prelude::*;
42use rand_chacha::ChaChaRng;
43use std::ops::Range;
44use vek::*;
45
46fn reseed<R: Rng>(rng: &mut R) -> impl Rng + use<R> { ChaChaRng::from_seed(rng.gen::<[u8; 32]>()) }
54
55pub struct SpawnRules {
56 pub trees: bool,
57 pub max_warp: f32,
58 pub paths: bool,
59 pub waypoints: bool,
60}
61
62impl SpawnRules {
63 #[must_use]
64 pub fn combine(self, other: Self) -> Self {
65 Self {
67 trees: self.trees && other.trees,
68 max_warp: self.max_warp.min(other.max_warp),
69 paths: self.paths && other.paths,
70 waypoints: self.waypoints && other.waypoints,
71 }
72 }
73}
74
75impl Default for SpawnRules {
76 fn default() -> Self {
77 Self {
78 trees: true,
79 max_warp: 1.0,
80 paths: true,
81 waypoints: true,
82 }
83 }
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum SiteKind {
88 Refactor,
89 CliffTown,
90 SavannahTown,
91 DesertCity,
92 ChapelSite,
93 DwarvenMine,
94 CoastalTown,
95 Citadel,
96 Terracotta,
97 GiantTree,
98 Gnarling,
99 Bridge(Vec2<i32>, Vec2<i32>),
100 Adlet,
101 Haniwa,
102 PirateHideout,
103 JungleRuin,
104 RockCircle,
105 TrollCave,
106 Camp,
107 Cultist,
108 Sahagin,
109 VampireCastle,
110 GliderCourse,
111 Myrmidon,
112}
113
114impl SiteKind {
115 pub fn meta(&self) -> Option<SiteKindMeta> {
116 match self {
117 SiteKind::Refactor => Some(SiteKindMeta::Settlement(SettlementKindMeta::Default)),
118 SiteKind::CliffTown => Some(SiteKindMeta::Settlement(SettlementKindMeta::CliffTown)),
119 SiteKind::SavannahTown => {
120 Some(SiteKindMeta::Settlement(SettlementKindMeta::SavannahTown))
121 },
122 SiteKind::CoastalTown => {
123 Some(SiteKindMeta::Settlement(SettlementKindMeta::CoastalTown))
124 },
125 SiteKind::DesertCity => Some(SiteKindMeta::Settlement(SettlementKindMeta::DesertCity)),
126 SiteKind::Gnarling => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Gnarling)),
127 SiteKind::Adlet => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Adlet)),
128 SiteKind::Terracotta => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Terracotta)),
129 SiteKind::Haniwa => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Haniwa)),
130 SiteKind::Myrmidon => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Myrmidon)),
131 SiteKind::DwarvenMine => Some(SiteKindMeta::Dungeon(DungeonKindMeta::DwarvenMine)),
132 SiteKind::ChapelSite => Some(SiteKindMeta::Dungeon(DungeonKindMeta::SeaChapel)),
133 SiteKind::Cultist => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Cultist)),
134 SiteKind::Sahagin => Some(SiteKindMeta::Dungeon(DungeonKindMeta::Sahagin)),
135 SiteKind::VampireCastle => Some(SiteKindMeta::Dungeon(DungeonKindMeta::VampireCastle)),
136
137 _ => None,
138 }
139 }
140
141 pub fn should_do_economic_simulation(&self) -> bool {
142 matches!(
143 self,
144 SiteKind::Refactor
145 | SiteKind::CliffTown
146 | SiteKind::SavannahTown
147 | SiteKind::CoastalTown
148 | SiteKind::DesertCity
149 )
150 }
151
152 pub fn marker(&self) -> Option<world_msg::MarkerKind> {
153 match self {
154 SiteKind::Refactor
155 | SiteKind::CliffTown
156 | SiteKind::SavannahTown
157 | SiteKind::CoastalTown
158 | SiteKind::DesertCity => Some(world_msg::MarkerKind::Town),
159 SiteKind::Citadel => Some(world_msg::MarkerKind::Castle),
160 SiteKind::Bridge(_, _) => Some(world_msg::MarkerKind::Bridge),
161 SiteKind::GiantTree => Some(world_msg::MarkerKind::Tree),
162 SiteKind::Gnarling => Some(world_msg::MarkerKind::Gnarling),
163 SiteKind::DwarvenMine => Some(world_msg::MarkerKind::DwarvenMine),
164 SiteKind::ChapelSite => Some(world_msg::MarkerKind::ChapelSite),
165 SiteKind::Terracotta => Some(world_msg::MarkerKind::Terracotta),
166 SiteKind::GliderCourse => Some(world_msg::MarkerKind::GliderCourse),
167 SiteKind::Cultist => Some(world_msg::MarkerKind::Cultist),
168 SiteKind::Sahagin => Some(world_msg::MarkerKind::Sahagin),
169 SiteKind::Myrmidon => Some(world_msg::MarkerKind::Myrmidon),
170 SiteKind::Adlet => Some(world_msg::MarkerKind::Adlet),
171 SiteKind::Haniwa => Some(world_msg::MarkerKind::Haniwa),
172 SiteKind::VampireCastle => Some(world_msg::MarkerKind::VampireCastle),
173
174 SiteKind::PirateHideout
175 | SiteKind::JungleRuin
176 | SiteKind::RockCircle
177 | SiteKind::TrollCave
178 | SiteKind::Camp => None,
179 }
180 }
181}
182
183#[derive(Default)]
184pub struct Site {
185 pub origin: Vec2<i32>,
186 name: String,
187 pub tiles: TileGrid,
189 pub plots: Store<Plot>,
190 pub plazas: Vec<Id<Plot>>,
191 pub roads: Vec<Id<Plot>>,
192 pub economy: Option<Box<Economy>>,
193 pub kind: Option<SiteKind>,
194}
195
196impl Site {
197 pub fn filter_plots<'a, F: FnMut(&'a Plot) -> bool>(
198 &'a self,
199 mut f: F,
200 ) -> std::iter::Filter<impl ExactSizeIterator<Item = &'a Plot>, impl FnMut(&&'a Plot) -> bool>
201 {
202 self.plots.values().filter(move |p| f(p))
203 }
204
205 pub fn any_plot<F: FnMut(&Plot) -> bool>(&self, f: F) -> bool { self.plots.values().any(f) }
206
207 pub fn meta(&self) -> Option<SiteKindMeta> { self.kind.and_then(|s| s.meta()) }
208
209 pub fn economy_mut(&mut self) -> &mut Economy { self.economy.get_or_insert_default() }
210
211 pub fn do_economic_simulation(&self) -> bool {
212 self.kind.is_some_and(|s| s.should_do_economic_simulation())
213 }
214
215 pub fn trade_information(&self, id: Id<Site>) -> Option<common::trade::SiteInformation> {
216 self.economy
217 .as_ref()
218 .map(|econ| common::trade::SiteInformation {
219 id: id.id(),
220 unconsumed_stock: econ.get_available_stock(),
221 })
222 }
223
224 pub fn radius(&self) -> f32 {
225 ((self
226 .tiles
227 .bounds
228 .min
229 .map(|e| e.abs())
230 .reduce_max()
231 .max(self.tiles.bounds.max.map(|e| e.abs()).reduce_max())
232 + if self
236 .plots
237 .values()
238 .any(|p| matches!(&p.kind, PlotKind::GiantTree(_)))
239 {
240 25
242 } else {
243 5
244 })
245 * TILE_SIZE as i32) as f32
246 }
247
248 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
249 let tile_pos = self.wpos_tile_pos(wpos);
250 let max_warp = SQUARE_9
251 .iter()
252 .filter_map(|rpos| {
253 let tile_pos = tile_pos + rpos;
254 if self.tiles.get(tile_pos).is_natural() {
255 None
256 } else {
257 let clamped =
258 wpos.clamped(self.tile_wpos(tile_pos), self.tile_wpos(tile_pos + 1) - 1);
259 Some(clamped.as_::<f32>().distance_squared(wpos.as_::<f32>()))
260 }
261 })
262 .min_by_key(|d2| *d2 as i32)
263 .map(|d2| d2.sqrt() / TILE_SIZE as f32)
264 .unwrap_or(1.0);
265 let base_spawn_rules = SpawnRules {
266 trees: max_warp == 1.0,
267 max_warp,
268 paths: max_warp > f32::EPSILON,
269 waypoints: true,
270 };
271 self.plots
272 .values()
273 .filter_map(|plot| match &plot.kind {
274 PlotKind::Gnarling(g) => Some(g.spawn_rules(wpos)),
275 PlotKind::Adlet(ad) => Some(ad.spawn_rules(wpos)),
276 PlotKind::SeaChapel(p) => Some(p.spawn_rules(wpos)),
277 PlotKind::Haniwa(ha) => Some(ha.spawn_rules(wpos)),
278 PlotKind::TerracottaPalace(tp) => Some(tp.spawn_rules(wpos)),
279 PlotKind::TerracottaHouse(th) => Some(th.spawn_rules(wpos)),
280 PlotKind::TerracottaYard(ty) => Some(ty.spawn_rules(wpos)),
281 PlotKind::Cultist(cl) => Some(cl.spawn_rules(wpos)),
282 PlotKind::Sahagin(sg) => Some(sg.spawn_rules(wpos)),
283 PlotKind::DwarvenMine(dm) => Some(dm.spawn_rules(wpos)),
284 PlotKind::VampireCastle(vc) => Some(vc.spawn_rules(wpos)),
285 PlotKind::MyrmidonArena(ma) => Some(ma.spawn_rules(wpos)),
286 PlotKind::MyrmidonHouse(mh) => Some(mh.spawn_rules(wpos)),
287 PlotKind::AirshipDock(ad) => Some(ad.spawn_rules(wpos)),
288 PlotKind::CoastalAirshipDock(cad) => Some(cad.spawn_rules(wpos)),
289 PlotKind::CliffTownAirshipDock(clad) => Some(clad.spawn_rules(wpos)),
290 PlotKind::DesertCityAirshipDock(dcad) => Some(dcad.spawn_rules(wpos)),
291 PlotKind::SavannahAirshipDock(sad) => Some(sad.spawn_rules(wpos)),
292 _ => None,
293 })
294 .fold(base_spawn_rules, |a, b| a.combine(b))
295 }
296
297 pub fn bounds(&self) -> Aabr<i32> {
298 let border = 1;
299 Aabr {
300 min: self.tile_wpos(self.tiles.bounds.min - border),
301 max: self.tile_wpos(self.tiles.bounds.max + 1 + border),
302 }
303 }
304
305 pub fn plot(&self, id: Id<Plot>) -> &Plot { &self.plots[id] }
306
307 pub fn plots(&self) -> impl ExactSizeIterator<Item = &Plot> + '_ { self.plots.values() }
308
309 pub fn plazas(&self) -> impl ExactSizeIterator<Item = Id<Plot>> + '_ {
310 self.plazas.iter().copied()
311 }
312
313 pub fn create_plot(&mut self, plot: Plot) -> Id<Plot> { self.plots.insert(plot) }
314
315 pub fn blit_aabr(&mut self, aabr: Aabr<i32>, tile: Tile) {
316 for y in 0..aabr.size().h {
317 for x in 0..aabr.size().w {
318 self.tiles.set(aabr.min + Vec2::new(x, y), tile.clone());
319 }
320 }
321 }
322
323 pub fn create_road(
324 &mut self,
325 land: &Land,
326 a: Vec2<i32>,
327 b: Vec2<i32>,
328 w: u16,
329 kind: plot::RoadKind,
330 ) -> Option<Id<Plot>> {
331 const MAX_ITERS: usize = 4096;
332 let range = &(-(w as i32) / 2..w as i32 - (w as i32 + 1) / 2);
333 let heuristic =
335 |(tile, _): &(Vec2<i32>, Vec2<i32>)| (tile - b).map(|e| e.abs()).sum() as f32;
336 let (path, _cost) = Astar::new(MAX_ITERS, (a, Vec2::zero()), DefaultHashBuilder::default())
337 .poll(
338 MAX_ITERS,
339 &heuristic,
340 |(tile, prev_dir)| {
341 let tile = *tile;
342 let prev_dir = *prev_dir;
343 let this = &self;
344 CARDINALS.iter().map(move |dir| {
345 let neighbor = (tile + *dir, *dir);
346
347 let alt_a = land.get_alt_approx(this.tile_center_wpos(tile));
349 let alt_b = land.get_alt_approx(this.tile_center_wpos(neighbor.0));
350 let mut cost = 1.0
351 + (alt_a - alt_b).abs() / TILE_SIZE as f32
352 + (prev_dir != *dir) as i32 as f32;
353
354 for i in range.clone() {
355 let orth = dir.yx() * i;
356 let tile = this.tiles.get(neighbor.0 + orth);
357 if tile.is_obstacle() {
358 cost += 1000.0;
359 } else if !tile.is_empty() && !tile.is_road() {
360 cost += 25.0;
361 }
362 }
363
364 (neighbor, cost)
365 })
366 },
367 |(tile, _)| *tile == b,
368 )
369 .into_path()?;
370
371 let plot = self.create_plot(Plot {
372 kind: PlotKind::Road(plot::Road {
373 path: path.iter().map(|(tile, _)| *tile).collect(),
374 kind,
375 }),
376 root_tile: a,
377 tiles: path.iter().map(|(tile, _)| *tile).collect(),
378 });
379
380 self.roads.push(plot);
381
382 for (i, (tile, _)) in path.iter().enumerate() {
383 for y in range.clone() {
384 for x in range.clone() {
385 let tile = tile + Vec2::new(x, y);
386 if matches!(
387 self.tiles.get(tile).kind,
388 TileKind::Empty | TileKind::Path { .. }
389 ) {
390 self.tiles.set(tile, Tile {
391 kind: TileKind::Road {
392 a: i.saturating_sub(1) as u16,
393 b: (i + 1).min(path.len() - 1) as u16,
394 w,
395 },
396 plot: Some(plot),
397 hard_alt: Some(land.get_alt_approx(self.tile_center_wpos(tile)) as i32),
398 });
399 }
400 }
401 }
402 }
403
404 Some(plot)
405 }
406
407 pub fn find_aabr(
408 &mut self,
409 search_pos: Vec2<i32>,
410 area_range: Range<u32>,
411 min_dims: Extent2<u32>,
412 ) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>, Option<i32>)> {
413 let ((aabr, (door_dir, hard_alt)), door_pos) =
414 self.tiles.find_near(search_pos, |center, _| {
415 let dir = CARDINALS
416 .iter()
417 .find(|dir| self.tiles.get(center + *dir).is_road())?;
418 let hard_alt = self.tiles.get(center + *dir).plot.and_then(|plot| {
419 if let PlotKind::Plaza(p) = self.plots.get(plot).kind() {
420 p.hard_alt
421 } else {
422 None
423 }
424 });
425 self.tiles
426 .grow_aabr(center, area_range.clone(), min_dims)
427 .ok()
428 .zip(Some((*dir, hard_alt)))
429 })?;
430 Some((aabr, door_pos, door_dir, hard_alt))
431 }
432
433 pub fn find_roadside_aabr(
434 &mut self,
435 rng: &mut impl Rng,
436 area_range: Range<u32>,
437 min_dims: Extent2<u32>,
438 ) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>, Option<i32>)> {
439 let dir = Vec2::<f32>::zero()
440 .map(|_| rng.gen_range(-1.0..1.0))
441 .normalized();
442 let search_pos = if rng.gen() {
443 let plaza = self.plot(*self.plazas.choose(rng)?);
444 let sz = plaza.find_bounds().size();
445 plaza.root_tile + dir.map(|e: f32| e.round() as i32) * (sz + 1)
446 } else if let PlotKind::Road(plot::Road { path, .. }) =
447 &self.plot(*self.roads.choose(rng)?).kind
448 {
449 *path.nodes().choose(rng)? + (dir * 1.0).map(|e: f32| e.round() as i32)
450 } else {
451 unreachable!()
452 };
453
454 self.find_aabr(search_pos, area_range, min_dims)
455 }
456
457 pub fn find_rural_aabr(
458 &mut self,
459 rng: &mut impl Rng,
460 area_range: Range<u32>,
461 min_dims: Extent2<u32>,
462 ) -> Option<(Aabr<i32>, Vec2<i32>, Vec2<i32>, Option<i32>)> {
463 let search_center = self
465 .plazas
466 .choose(rng)
467 .map(|&p| self.plot(p).root_tile)
468 .unwrap_or_default();
469
470 let dir = Vec2::<f32>::zero()
472 .map(|_| rng.gen_range(-1.0..1.0))
473 .normalized();
474 let search_offset = dir.map2(min_dims.into(), |e: f32, sz: u32| {
475 (e * sz as f32 * 0.75 + 10.0).round() as i32
476 });
477
478 self.find_aabr(search_center + search_offset, area_range, min_dims)
479 }
480
481 pub fn make_plaza_at(
482 &mut self,
483 land: &Land,
484 index: IndexRef,
485 tile_aabr: Aabr<i32>,
486 rng: &mut impl Rng,
487 road_kind: plot::RoadKind,
488 ) -> Option<Id<Plot>> {
489 let tpos = tile_aabr.center();
490 let plaza_alt = land.get_alt_approx(self.tile_center_wpos(tpos)) as i32;
491
492 let plaza = self.create_plot(Plot {
493 kind: PlotKind::Plaza(plot::Plaza::generate(
494 tile_aabr, road_kind, self, land, index,
495 )),
496 root_tile: tpos,
497 tiles: aabr_tiles(tile_aabr).collect(),
498 });
499 self.plazas.push(plaza);
500 self.blit_aabr(tile_aabr, Tile {
501 kind: TileKind::Plaza,
502 plot: Some(plaza),
503 hard_alt: Some(plaza_alt),
504 });
505
506 let mut already_pathed = vec![];
507 for _ in (0..rng.gen_range(1.25..2.25) as u16).rev() {
509 if let Some(&p) = self
510 .plazas
511 .iter()
512 .filter(|&&p| {
513 !already_pathed.contains(&p)
514 && p != plaza
515 && already_pathed.iter().all(|&ap| {
516 (self.plot(ap).root_tile - tpos)
517 .map(|e| e as f32)
518 .normalized()
519 .dot(
520 (self.plot(p).root_tile - tpos)
521 .map(|e| e as f32)
522 .normalized(),
523 )
524 < 0.0
525 })
526 })
527 .min_by_key(|&&p| self.plot(p).root_tile.distance_squared(tpos))
528 {
529 self.create_road(
530 land,
531 self.plot(p).root_tile,
532 tpos,
533 2, road_kind,
535 );
536 already_pathed.push(p);
537 } else {
538 break;
539 }
540 }
541
542 Some(plaza)
543 }
544
545 pub fn make_plaza(
546 &mut self,
547 land: &Land,
548 index: IndexRef,
549 rng: &mut impl Rng,
550 generator_stats: &mut SitesGenMeta,
551 site_name: &String,
552 road_kind: plot::RoadKind,
553 ) -> Option<Id<Plot>> {
554 generator_stats.attempt(site_name, GenStatPlotKind::Plaza);
555 let plaza_radius = rng.gen_range(1..4);
556 let plaza_dist = 6.5 + plaza_radius as f32 * 3.0;
557 let aabr = attempt(32, || {
558 self.plazas
559 .choose(rng)
560 .map(|&p| {
561 self.plot(p).root_tile
562 + (Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0))
563 .normalized()
564 * plaza_dist)
565 .map(|e| e as i32)
566 })
567 .or_else(|| Some(Vec2::zero()))
568 .map(|center_tile| Aabr {
569 min: center_tile + Vec2::broadcast(-plaza_radius),
570 max: center_tile + Vec2::broadcast(plaza_radius + 1),
571 })
572 .filter(|&aabr| {
573 rng.gen_range(0..48) > aabr.center().map(|e| e.abs()).reduce_max()
574 && aabr_tiles(aabr).all(|tile| !self.tiles.get(tile).is_obstacle())
575 })
576 .filter(|&aabr| {
577 self.plazas.iter().all(|&p| {
578 let dist_sqr = if let PlotKind::Plaza(plaza) = &self.plot(p).kind {
579 let intersection = plaza.aabr.intersection(aabr);
580 intersection
583 .size()
584 .map(|e| e.min(0) as f32)
585 .magnitude_squared()
586 } else {
587 let r = self.plot(p).root_tile();
588 let closest_point = aabr.projected_point(r);
589 closest_point.as_::<f32>().distance_squared(r.as_::<f32>())
590 };
591 dist_sqr > (plaza_dist * 0.85).powi(2)
592 })
593 })
594 })?;
595 generator_stats.success(site_name, GenStatPlotKind::Plaza);
596 self.make_plaza_at(land, index, aabr, rng, road_kind)
597 }
598
599 pub fn demarcate_obstacles(&mut self, land: &Land) {
600 const SEARCH_RADIUS: u32 = 96;
601
602 Spiral2d::new()
603 .take((SEARCH_RADIUS * 2 + 1).pow(2) as usize)
604 .for_each(|tile| {
605 let wpos = self.tile_center_wpos(tile);
606 if let Some(kind) = Spiral2d::new()
607 .take(9)
608 .find_map(|rpos| wpos_is_hazard(land, wpos + rpos))
609 {
610 for &rpos in &SQUARE_4 {
611 self.tiles
613 .get_mut(tile - rpos - 1)
614 .filter(|tile| tile.is_natural())
615 .map(|tile| tile.kind = TileKind::Hazard(kind));
616 }
617 }
618 if let Some((_, path_wpos, path, _)) = land.get_nearest_path(wpos) {
619 let tile_aabr = Aabr {
620 min: self.tile_wpos(tile),
621 max: self.tile_wpos(tile + 1) - 1,
622 };
623
624 if tile_aabr
625 .projected_point(path_wpos.as_())
626 .as_()
627 .distance_squared(path_wpos)
628 < path.width.powi(2)
629 {
630 self.tiles
631 .get_mut(tile)
632 .filter(|tile| tile.is_natural())
633 .map(|tile| {
634 tile.kind = TileKind::Path {
635 closest_pos: path_wpos,
636 path,
637 }
638 });
639 }
640 }
641 });
642 }
643
644 pub fn make_initial_plaza(
661 &mut self,
662 land: &Land,
663 index: IndexRef,
664 rng: &mut impl Rng,
665 plaza_radius: u32,
666 search_inner_radius: u32,
667 search_width: u32,
668 generator_stats: &mut SitesGenMeta,
669 site_name: &String,
670 road_kind: plot::RoadKind,
671 ) -> Option<Id<Plot>> {
672 generator_stats.attempt(site_name, GenStatPlotKind::InitialPlaza);
673 let mut plaza_locations = vec![];
675 Spiral2d::with_ring(search_inner_radius, search_width).for_each(|tpos| {
677 let aabr = Aabr {
678 min: tpos - Vec2::broadcast(plaza_radius as i32),
679 max: tpos + Vec2::broadcast(plaza_radius as i32 + 1),
680 };
681 if aabr_tiles(aabr).all(|tpos| self.tiles.get(tpos).is_empty()) {
684 plaza_locations.push(aabr);
685 }
686 });
687 if plaza_locations.is_empty() {
688 self.make_plaza(land, index, rng, generator_stats, site_name, road_kind)
692 } else {
693 plaza_locations.sort_by_key(|&aabr| {
695 aabr.min
696 .map2(aabr.max, |a, b| a.abs().min(b.abs()))
697 .magnitude_squared()
698 });
699 let aabr = plaza_locations.first()?;
701 generator_stats.success(site_name, GenStatPlotKind::InitialPlaza);
702 self.make_plaza_at(land, index, *aabr, rng, road_kind)
703 }
704 }
705
706 pub fn make_initial_plaza_default(
726 &mut self,
727 land: &Land,
728 index: IndexRef,
729 rng: &mut impl Rng,
730 generator_stats: &mut SitesGenMeta,
731 site_name: &String,
732 road_kind: plot::RoadKind,
733 ) -> Option<Id<Plot>> {
734 let plaza_radius = rng.gen_range(1..4);
736 let search_inner_radius = 7 + plaza_radius;
740 const PLAZA_MAX_SEARCH_RADIUS: u32 = 24;
741 self.make_initial_plaza(
742 land,
743 index,
744 rng,
745 plaza_radius,
746 search_inner_radius,
747 PLAZA_MAX_SEARCH_RADIUS - search_inner_radius,
748 generator_stats,
749 site_name,
750 road_kind,
751 )
752 }
753
754 pub fn name(&self) -> &str { &self.name }
755
756 pub fn generate_mine(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
757 let mut rng = reseed(rng);
758 let mut site = Site {
759 origin,
760 kind: Some(SiteKind::DwarvenMine),
761 ..Site::default()
762 };
763
764 let size = 60.0;
765
766 let aabr = Aabr {
767 min: Vec2::broadcast(-size as i32),
768 max: Vec2::broadcast(size as i32),
769 };
770
771 let wpos: Vec2<i32> = [1, 2].into();
772
773 let dwarven_mine =
774 plot::DwarvenMine::generate(land, &mut reseed(&mut rng), &site, wpos, aabr);
775 site.name = dwarven_mine.name().to_string();
776 let plot = site.create_plot(Plot {
777 kind: PlotKind::DwarvenMine(dwarven_mine),
778 root_tile: aabr.center(),
779 tiles: aabr_tiles(aabr).collect(),
780 });
781
782 site.blit_aabr(aabr, Tile {
783 kind: TileKind::Empty,
784 plot: Some(plot),
785 hard_alt: Some(1_i32),
786 });
787
788 site
789 }
790
791 pub fn generate_citadel(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
792 let mut rng = reseed(rng);
793 let mut site = Site {
794 origin,
795 kind: Some(SiteKind::Citadel),
796 ..Site::default()
797 };
798 site.demarcate_obstacles(land);
799 let citadel = plot::Citadel::generate(origin, land, &mut rng);
800 site.name = citadel.name().to_string();
801 let size = citadel.radius() / tile::TILE_SIZE as i32;
802 let aabr = Aabr {
803 min: Vec2::broadcast(-size),
804 max: Vec2::broadcast(size),
805 };
806 let plot = site.create_plot(Plot {
807 kind: PlotKind::Citadel(citadel),
808 root_tile: aabr.center(),
809 tiles: aabr_tiles(aabr).collect(),
810 });
811 site.blit_aabr(aabr, Tile {
812 kind: TileKind::Building,
813 plot: Some(plot),
814 hard_alt: None,
815 });
816 site
817 }
818
819 pub fn generate_gnarling(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
820 let mut rng = reseed(rng);
821 let mut site = Site {
822 origin,
823 kind: Some(SiteKind::Gnarling),
824 ..Site::default()
825 };
826 site.demarcate_obstacles(land);
827 let gnarling_fortification = plot::GnarlingFortification::generate(origin, land, &mut rng);
828 site.name = gnarling_fortification.name().to_string();
829 let size = gnarling_fortification.radius() / TILE_SIZE as i32;
830 let aabr = Aabr {
831 min: Vec2::broadcast(-size),
832 max: Vec2::broadcast(size),
833 };
834 let plot = site.create_plot(Plot {
835 kind: PlotKind::Gnarling(gnarling_fortification),
836 root_tile: aabr.center(),
837 tiles: aabr_tiles(aabr).collect(),
838 });
839 site.blit_aabr(aabr, Tile {
840 kind: TileKind::GnarlingFortification,
841 plot: Some(plot),
842 hard_alt: None,
843 });
844 site
845 }
846
847 pub fn generate_adlet(
848 land: &Land,
849 rng: &mut impl Rng,
850 origin: Vec2<i32>,
851 index: IndexRef,
852 ) -> Self {
853 let mut rng = reseed(rng);
854 let mut site = Site {
855 origin,
856 kind: Some(SiteKind::Adlet),
857 ..Site::default()
858 };
859 site.demarcate_obstacles(land);
860 let adlet_stronghold = plot::AdletStronghold::generate(origin, land, &mut rng, index);
861 site.name = adlet_stronghold.name().to_string();
862 let (cavern_aabr, wall_aabr) = adlet_stronghold.plot_tiles(origin);
863 let plot = site.create_plot(Plot {
864 kind: PlotKind::Adlet(adlet_stronghold),
865 root_tile: cavern_aabr.center(),
866 tiles: aabr_tiles(cavern_aabr)
867 .chain(aabr_tiles(wall_aabr))
868 .collect(),
869 });
870 site.blit_aabr(cavern_aabr, Tile {
871 kind: TileKind::AdletStronghold,
872 plot: Some(plot),
873 hard_alt: None,
874 });
875 site.blit_aabr(wall_aabr, Tile {
876 kind: TileKind::AdletStronghold,
877 plot: Some(plot),
878 hard_alt: None,
879 });
880 site
881 }
882
883 pub fn generate_terracotta(
884 land: &Land,
885 index: IndexRef,
886 rng: &mut impl Rng,
887 origin: Vec2<i32>,
888 generator_stats: &mut SitesGenMeta,
889 ) -> Self {
890 let mut rng = reseed(rng);
891 let gen_name = NameGen::location(&mut rng).generate_terracotta();
892 let suffix = [
893 "Tombs",
894 "Necropolis",
895 "Ruins",
896 "Mausoleum",
897 "Cemetery",
898 "Burial Grounds",
899 "Remains",
900 "Temples",
901 "Gardens",
902 ]
903 .choose(&mut rng)
904 .unwrap();
905 let name = match rng.gen_range(0..2) {
906 0 => format!("{} {}", gen_name, suffix),
907 _ => format!("{} of {}", suffix, gen_name),
908 };
909 let mut site = Site {
910 origin,
911 name: name.clone(),
912 kind: Some(SiteKind::Terracotta),
913 ..Site::default()
914 };
915
916 site.demarcate_obstacles(land);
918 const TERRACOTTA_PLAZA_RADIUS: u32 = 3;
921 const TERRACOTTA_PLAZA_SEARCH_INNER: u32 = 17;
922 const TERRACOTTA_PLAZA_SEARCH_WIDTH: u32 = 12;
923 generator_stats.add(&site.name, GenStatSiteKind::Terracotta);
924 let road_kind = plot::RoadKind {
925 lights: plot::RoadLights::Terracotta,
926 material: plot::RoadMaterial::Sandstone,
927 };
928 site.make_initial_plaza(
929 land,
930 index,
931 &mut rng,
932 TERRACOTTA_PLAZA_RADIUS,
933 TERRACOTTA_PLAZA_SEARCH_INNER,
934 TERRACOTTA_PLAZA_SEARCH_WIDTH,
935 generator_stats,
936 &name,
937 road_kind,
938 );
939
940 let size = 15.0 as i32;
941 let aabr = Aabr {
942 min: Vec2::broadcast(-size),
943 max: Vec2::broadcast(size),
944 };
945 {
946 let terracotta_palace =
947 plot::TerracottaPalace::generate(land, &mut reseed(&mut rng), &site, aabr);
948 let terracotta_palace_alt = terracotta_palace.alt;
949 let plot = site.create_plot(Plot {
950 kind: PlotKind::TerracottaPalace(terracotta_palace),
951 root_tile: aabr.center(),
952 tiles: aabr_tiles(aabr).collect(),
953 });
954
955 site.blit_aabr(aabr, Tile {
956 kind: TileKind::Building,
957 plot: Some(plot),
958 hard_alt: Some(terracotta_palace_alt),
959 });
960 }
961 let build_chance = Lottery::from(vec![(12.0, 1), (4.0, 2)]);
962 for _ in 0..16 {
963 match *build_chance.choose_seeded(rng.gen()) {
964 1 => {
965 generator_stats.attempt(&site.name, GenStatPlotKind::House);
967 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
968 if let Some((aabr, _, _, alt)) = attempt(32, || {
969 site.find_roadside_aabr(
970 &mut rng,
971 9..(size + 1).pow(2),
972 Extent2::broadcast(size),
973 )
974 }) {
975 let terracotta_house = plot::TerracottaHouse::generate(
976 land,
977 &mut reseed(&mut rng),
978 &site,
979 aabr,
980 alt,
981 );
982 let terracotta_house_alt = terracotta_house.alt;
983 let plot = site.create_plot(Plot {
984 kind: PlotKind::TerracottaHouse(terracotta_house),
985 root_tile: aabr.center(),
986 tiles: aabr_tiles(aabr).collect(),
987 });
988
989 site.blit_aabr(aabr, Tile {
990 kind: TileKind::Building,
991 plot: Some(plot),
992 hard_alt: Some(terracotta_house_alt),
993 });
994
995 generator_stats.success(&site.name, GenStatPlotKind::House);
996 } else {
997 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
998 }
999 },
1000
1001 2 => {
1002 generator_stats.attempt(&site.name, GenStatPlotKind::Yard);
1004 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
1005 if let Some((aabr, _, _, alt)) = attempt(32, || {
1006 site.find_roadside_aabr(
1007 &mut rng,
1008 9..(size + 1).pow(2),
1009 Extent2::broadcast(size),
1010 )
1011 }) {
1012 let terracotta_yard = plot::TerracottaYard::generate(
1013 land,
1014 &mut reseed(&mut rng),
1015 &site,
1016 aabr,
1017 alt,
1018 );
1019 let terracotta_yard_alt = terracotta_yard.alt;
1020 let plot = site.create_plot(Plot {
1021 kind: PlotKind::TerracottaYard(terracotta_yard),
1022 root_tile: aabr.center(),
1023 tiles: aabr_tiles(aabr).collect(),
1024 });
1025
1026 site.blit_aabr(aabr, Tile {
1027 kind: TileKind::Building,
1028 plot: Some(plot),
1029 hard_alt: Some(terracotta_yard_alt),
1030 });
1031
1032 generator_stats.success(&site.name, GenStatPlotKind::Yard);
1033 } else {
1034 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1035 }
1036 },
1037 _ => {},
1038 }
1039 }
1040 site
1041 }
1042
1043 pub fn generate_myrmidon(
1044 land: &Land,
1045 index: IndexRef,
1046 rng: &mut impl Rng,
1047 origin: Vec2<i32>,
1048 generator_stats: &mut SitesGenMeta,
1049 ) -> Self {
1050 let mut rng = reseed(rng);
1051 let gen_name = NameGen::location(&mut rng).generate_danari();
1052 let suffix = ["City", "Metropolis"].choose(&mut rng).unwrap();
1053 let name = match rng.gen_range(0..2) {
1054 0 => format!("{} {}", gen_name, suffix),
1055 _ => format!("{} of {}", suffix, gen_name),
1056 };
1057 let mut site = Site {
1058 origin,
1059 name: name.clone(),
1060 kind: Some(SiteKind::Myrmidon),
1061 ..Site::default()
1062 };
1063
1064 let road_kind = plot::RoadKind {
1065 lights: plot::RoadLights::Default,
1066 material: plot::RoadMaterial::Dirt,
1067 };
1068
1069 site.demarcate_obstacles(land);
1071 const MYRMIDON_PLAZA_RADIUS: u32 = 3;
1074 const MYRMIDON_PLAZA_SEARCH_INNER: u32 = 18;
1075 const MYRMIDON_PLAZA_SEARCH_WIDTH: u32 = 12;
1076 generator_stats.add(&site.name, GenStatSiteKind::Myrmidon);
1077 generator_stats.attempt(&site.name, GenStatPlotKind::InitialPlaza);
1078 site.make_initial_plaza(
1079 land,
1080 index,
1081 &mut rng,
1082 MYRMIDON_PLAZA_RADIUS,
1083 MYRMIDON_PLAZA_SEARCH_INNER,
1084 MYRMIDON_PLAZA_SEARCH_WIDTH,
1085 generator_stats,
1086 &name,
1087 road_kind,
1088 );
1089
1090 let size = 16.0 as i32;
1091 let aabr = Aabr {
1092 min: Vec2::broadcast(-size),
1093 max: Vec2::broadcast(size),
1094 };
1095 {
1096 let myrmidon_arena =
1097 plot::MyrmidonArena::generate(land, &mut reseed(&mut rng), &site, aabr);
1098 let myrmidon_arena_alt = myrmidon_arena.alt;
1099 let plot = site.create_plot(Plot {
1100 kind: PlotKind::MyrmidonArena(myrmidon_arena),
1101 root_tile: aabr.center(),
1102 tiles: aabr_tiles(aabr).collect(),
1103 });
1104
1105 site.blit_aabr(aabr, Tile {
1106 kind: TileKind::Building,
1107 plot: Some(plot),
1108 hard_alt: Some(myrmidon_arena_alt),
1109 });
1110 }
1111 for _ in 0..30 {
1112 generator_stats.attempt(&site.name, GenStatPlotKind::House);
1114 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
1115 if let Some((aabr, _, _, alt)) = attempt(32, || {
1116 site.find_roadside_aabr(&mut rng, 9..(size + 1).pow(2), Extent2::broadcast(size))
1117 }) {
1118 let myrmidon_house =
1119 plot::MyrmidonHouse::generate(land, &mut reseed(&mut rng), &site, aabr, alt);
1120 let myrmidon_house_alt = myrmidon_house.alt;
1121 let plot = site.create_plot(Plot {
1122 kind: PlotKind::MyrmidonHouse(myrmidon_house),
1123 root_tile: aabr.center(),
1124 tiles: aabr_tiles(aabr).collect(),
1125 });
1126
1127 site.blit_aabr(aabr, Tile {
1128 kind: TileKind::Building,
1129 plot: Some(plot),
1130 hard_alt: Some(myrmidon_house_alt),
1131 });
1132
1133 generator_stats.success(&site.name, GenStatPlotKind::House);
1134 } else {
1135 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1136 }
1137 }
1138
1139 site
1140 }
1141
1142 pub fn generate_giant_tree(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
1143 let mut rng = reseed(rng);
1144 let mut site = Site {
1145 origin,
1146 kind: Some(SiteKind::GiantTree),
1147 ..Site::default()
1148 };
1149 site.demarcate_obstacles(land);
1150 let giant_tree = plot::GiantTree::generate(&site, Vec2::zero(), land, &mut rng);
1151 site.name = giant_tree.name().to_string();
1152 let size = (giant_tree.radius() / TILE_SIZE as f32).ceil() as i32;
1153 let aabr = Aabr {
1154 min: Vec2::broadcast(-size),
1155 max: Vec2::broadcast(size) + 1,
1156 };
1157 let plot = site.create_plot(Plot {
1158 kind: PlotKind::GiantTree(giant_tree),
1159 root_tile: aabr.center(),
1160 tiles: aabr_tiles(aabr).collect(),
1161 });
1162 site.blit_aabr(aabr, Tile {
1163 kind: TileKind::Building,
1164 plot: Some(plot),
1165 hard_alt: None,
1166 });
1167 site
1168 }
1169
1170 pub fn generate_city(
1172 land: &Land,
1173 index: IndexRef,
1174 rng: &mut impl Rng,
1175 origin: Vec2<i32>,
1176 size: f32,
1177 calendar: Option<&Calendar>,
1178 generator_stats: &mut SitesGenMeta,
1179 ) -> Self {
1180 let mut rng = reseed(rng);
1181 let name = NameGen::location(&mut rng).generate_town();
1182 let mut site = Site {
1183 origin,
1184 name: name.clone(),
1185 kind: Some(SiteKind::Refactor),
1186 ..Site::default()
1187 };
1188 let road_kind = plot::RoadKind {
1189 lights: plot::RoadLights::Default,
1190 material: plot::RoadMaterial::Cobblestone,
1191 };
1192
1193 site.demarcate_obstacles(land);
1195 generator_stats.add(&site.name, GenStatSiteKind::City);
1196 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1197
1198 let build_chance = Lottery::from(vec![
1199 (64.0, 1), (5.0, 2), (25.0, 3), (5.0, 5), (15.0, 6), (15.0, 7), (5.0, 8), ]);
1208
1209 let mut workshops = 0;
1211 let mut castles = 0;
1212 let mut taverns = 0;
1213 let mut airship_docks = 0;
1214
1215 for _ in 0..(size * 200.0) as i32 {
1216 match *build_chance.choose_seeded(rng.gen()) {
1217 n if (n == 5 && workshops < (size * 5.0) as i32) || workshops == 0 => {
1219 generator_stats.attempt(&site.name, GenStatPlotKind::Workshop);
1220 let size = (3.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
1221 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1222 site.find_roadside_aabr(
1223 &mut rng,
1224 4..(size + 1).pow(2),
1225 Extent2::broadcast(size),
1226 )
1227 }) {
1228 let workshop = plot::Workshop::generate(
1229 land,
1230 &mut reseed(&mut rng),
1231 &site,
1232 door_tile,
1233 door_dir,
1234 aabr,
1235 alt,
1236 );
1237 let workshop_alt = workshop.alt;
1238 let plot = site.create_plot(Plot {
1239 kind: PlotKind::Workshop(workshop),
1240 root_tile: aabr.center(),
1241 tiles: aabr_tiles(aabr).collect(),
1242 });
1243
1244 site.blit_aabr(aabr, Tile {
1245 kind: TileKind::Building,
1246 plot: Some(plot),
1247 hard_alt: Some(workshop_alt),
1248 });
1249 workshops += 1;
1250 generator_stats.success(&site.name, GenStatPlotKind::Workshop);
1251 } else {
1252 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1253 }
1254 },
1255 1 => {
1257 let size = (1.5 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
1258 generator_stats.attempt(&site.name, GenStatPlotKind::House);
1259 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1260 site.find_roadside_aabr(
1261 &mut rng,
1262 4..(size + 1).pow(2),
1263 Extent2::broadcast(size),
1264 )
1265 }) {
1266 let house = plot::House::generate(
1267 land,
1268 &mut reseed(&mut rng),
1269 &site,
1270 door_tile,
1271 door_dir,
1272 aabr,
1273 calendar,
1274 alt,
1275 );
1276 let house_alt = house.alt;
1277 let plot = site.create_plot(Plot {
1278 kind: PlotKind::House(house),
1279 root_tile: aabr.center(),
1280 tiles: aabr_tiles(aabr).collect(),
1281 });
1282
1283 site.blit_aabr(aabr, Tile {
1284 kind: TileKind::Building,
1285 plot: Some(plot),
1286 hard_alt: Some(house_alt),
1287 });
1288 generator_stats.success(&site.name, GenStatPlotKind::House);
1289 } else {
1290 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1291 }
1292 },
1293 2 => {
1295 generator_stats.attempt(&site.name, GenStatPlotKind::GuardTower);
1296 if let Some((_aabr, _, _door_dir, _)) = attempt(10, || {
1297 site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
1298 }) {
1299 }
1316 },
1317 3 => {
1319 Self::generate_farm(false, &mut rng, &mut site, land);
1320 },
1321 4 if size > 0.2 && castles < 1 => {
1323 generator_stats.attempt(&site.name, GenStatPlotKind::Castle);
1324 if let Some((aabr, _entrance_tile, _door_dir, _alt)) = attempt(64, || {
1325 site.find_rural_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))
1326 }) {
1327 let offset = rng.gen_range(5..(aabr.size().w.min(aabr.size().h) - 4));
1328 let gate_aabr = Aabr {
1329 min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y),
1330 max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1),
1331 };
1332 let castle = plot::Castle::generate(land, &mut rng, &site, aabr, gate_aabr);
1333 let castle_alt = castle.alt;
1334 let plot = site.create_plot(Plot {
1335 kind: PlotKind::Castle(castle),
1336 root_tile: aabr.center(),
1337 tiles: aabr_tiles(aabr).collect(),
1338 });
1339
1340 let wall_north = Tile {
1341 kind: TileKind::Wall(Dir::Y),
1342 plot: Some(plot),
1343 hard_alt: Some(castle_alt),
1344 };
1345
1346 let wall_east = Tile {
1347 kind: TileKind::Wall(Dir::X),
1348 plot: Some(plot),
1349 hard_alt: Some(castle_alt),
1350 };
1351 for x in 0..aabr.size().w {
1352 site.tiles
1353 .set(aabr.min + Vec2::new(x, 0), wall_east.clone());
1354 site.tiles.set(
1355 aabr.min + Vec2::new(x, aabr.size().h - 1),
1356 wall_east.clone(),
1357 );
1358 }
1359 for y in 0..aabr.size().h {
1360 site.tiles
1361 .set(aabr.min + Vec2::new(0, y), wall_north.clone());
1362 site.tiles.set(
1363 aabr.min + Vec2::new(aabr.size().w - 1, y),
1364 wall_north.clone(),
1365 );
1366 }
1367
1368 let gate = Tile {
1369 kind: TileKind::Gate,
1370 plot: Some(plot),
1371 hard_alt: Some(castle_alt),
1372 };
1373 let tower_parapet = Tile {
1374 kind: TileKind::Tower(RoofKind::Parapet),
1375 plot: Some(plot),
1376 hard_alt: Some(castle_alt),
1377 };
1378 let tower_pyramid = Tile {
1379 kind: TileKind::Tower(RoofKind::Pyramid),
1380 plot: Some(plot),
1381 hard_alt: Some(castle_alt),
1382 };
1383
1384 site.tiles.set(
1385 Vec2::new(aabr.min.x + offset - 2, aabr.min.y),
1386 tower_parapet.clone(),
1387 );
1388 site.tiles
1389 .set(Vec2::new(aabr.min.x + offset - 1, aabr.min.y), gate.clone());
1390 site.tiles
1391 .set(Vec2::new(aabr.min.x + offset, aabr.min.y), gate.clone());
1392 site.tiles
1393 .set(Vec2::new(aabr.min.x + offset + 1, aabr.min.y), gate.clone());
1394 site.tiles.set(
1395 Vec2::new(aabr.min.x + offset + 2, aabr.min.y),
1396 tower_parapet.clone(),
1397 );
1398
1399 site.tiles
1400 .set(Vec2::new(aabr.min.x, aabr.min.y), tower_parapet.clone());
1401 site.tiles
1402 .set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower_parapet.clone());
1403 site.tiles
1404 .set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower_parapet.clone());
1405 site.tiles.set(
1406 Vec2::new(aabr.max.x - 1, aabr.max.y - 1),
1407 tower_parapet.clone(),
1408 );
1409
1410 site.blit_aabr(
1412 Aabr {
1413 min: aabr.min + 1,
1414 max: aabr.max - 1,
1415 },
1416 Tile {
1417 kind: TileKind::Road { a: 0, b: 0, w: 0 },
1418 plot: Some(plot),
1419 hard_alt: Some(castle_alt),
1420 },
1421 );
1422
1423 site.blit_aabr(
1425 Aabr {
1426 min: aabr.center() - 3,
1427 max: aabr.center() + 3,
1428 },
1429 Tile {
1430 kind: TileKind::Wall(Dir::Y),
1431 plot: Some(plot),
1432 hard_alt: Some(castle_alt),
1433 },
1434 );
1435 site.tiles.set(
1436 Vec2::new(aabr.center().x + 2, aabr.center().y + 2),
1437 tower_pyramid.clone(),
1438 );
1439 site.tiles.set(
1440 Vec2::new(aabr.center().x + 2, aabr.center().y - 3),
1441 tower_pyramid.clone(),
1442 );
1443 site.tiles.set(
1444 Vec2::new(aabr.center().x - 3, aabr.center().y + 2),
1445 tower_pyramid.clone(),
1446 );
1447 site.tiles.set(
1448 Vec2::new(aabr.center().x - 3, aabr.center().y - 3),
1449 tower_pyramid.clone(),
1450 );
1451
1452 site.blit_aabr(
1453 Aabr {
1454 min: aabr.center() - 2,
1455 max: aabr.center() + 2,
1456 },
1457 Tile {
1458 kind: TileKind::Keep(KeepKind::Middle),
1459 plot: Some(plot),
1460 hard_alt: Some(castle_alt),
1461 },
1462 );
1463
1464 castles += 1;
1465 generator_stats.success(&site.name, GenStatPlotKind::Castle);
1466 }
1467 },
1468 6 if (size > 0.125 && airship_docks == 0) => {
1470 generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
1471 let size = 9u32;
1477 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1478 site.find_roadside_aabr(&mut rng, 81..82, Extent2::broadcast(size))
1479 }) {
1480 let airship_dock = plot::AirshipDock::generate(
1481 land,
1482 &mut reseed(&mut rng),
1483 &site,
1484 door_tile,
1485 door_dir,
1486 aabr,
1487 alt,
1488 );
1489 let airship_dock_alt = airship_dock.alt;
1490 let plot = site.create_plot(Plot {
1491 kind: PlotKind::AirshipDock(airship_dock),
1492 root_tile: aabr.center(),
1493 tiles: aabr_tiles(aabr).collect(),
1494 });
1495
1496 site.blit_aabr(aabr, Tile {
1497 kind: TileKind::Building,
1498 plot: Some(plot),
1499 hard_alt: Some(airship_dock_alt),
1500 });
1501 airship_docks += 1;
1502 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
1503 } else {
1504 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1505 }
1506 },
1507 7 if (size > 0.125 && taverns < 2) => {
1508 generator_stats.attempt(&site.name, GenStatPlotKind::Tavern);
1509 let size = (4.5 + rng.gen::<f32>().powf(5.0) * 2.0).round() as u32;
1510 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1511 site.find_roadside_aabr(
1512 &mut rng,
1513 8..(size + 1).pow(2),
1514 Extent2::broadcast(size),
1515 )
1516 }) {
1517 let tavern = plot::Tavern::generate(
1518 land,
1519 index,
1520 &mut reseed(&mut rng),
1521 &site,
1522 door_tile,
1523 Dir::from_vec2(door_dir),
1524 aabr,
1525 alt,
1526 );
1527 let tavern_alt = tavern.door_wpos.z;
1528 let plot = site.create_plot(Plot {
1529 kind: PlotKind::Tavern(tavern),
1530 root_tile: aabr.center(),
1531 tiles: aabr_tiles(aabr).collect(),
1532 });
1533
1534 site.blit_aabr(aabr, Tile {
1535 kind: TileKind::Building,
1536 plot: Some(plot),
1537 hard_alt: Some(tavern_alt),
1538 });
1539
1540 taverns += 1;
1541 generator_stats.success(&site.name, GenStatPlotKind::Tavern);
1542 } else {
1543 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1544 }
1545 },
1546 8 => {
1547 Self::generate_barn(false, &mut rng, &mut site, land, index);
1548 },
1549 _ => {},
1550 }
1551 }
1552
1553 site
1554 }
1555
1556 pub fn generate_glider_course(
1557 land: &Land,
1558 _index: IndexRef,
1559 rng: &mut impl Rng,
1560 origin: Vec2<i32>,
1561 ) -> Self {
1562 let mut rng = reseed(rng);
1563 let mut site = Site {
1564 origin,
1565 kind: Some(SiteKind::GliderCourse),
1566 ..Site::default()
1567 };
1568
1569 site.name = NameGen::location(&mut rng).generate_town() + " Glider Course";
1572
1573 let origin_alt = land.get_alt_approx(origin);
1576 let alt_drops: Vec<f32> = CARDINALS
1577 .iter()
1578 .map(|c| {
1579 origin_alt
1580 - 0.5
1581 * (land.get_alt_approx(origin + *c * TerrainChunkSize::RECT_SIZE.x as i32)
1582 + land.get_alt_approx(
1583 origin + 2 * *c * TerrainChunkSize::RECT_SIZE.x as i32,
1584 ))
1585 })
1586 .collect();
1587 let mut cardinal = 0;
1588 let mut max_drop = 0.0;
1589 for (i, drop) in alt_drops.iter().enumerate() {
1590 if *drop > max_drop {
1591 max_drop = *drop;
1592 cardinal = i;
1593 }
1594 }
1595 let dir = match cardinal {
1596 0 => Dir::X,
1597 1 => Dir::Y,
1598 2 => Dir::NegX,
1599 3 => Dir::NegY,
1600 _ => Dir::X,
1601 };
1602 let size = 2.0;
1603
1604 let mut valid_course = true;
1605 let mut positions = Vec::new();
1606
1607 let pos = origin;
1609 let tile_pos: Vec2<i32> = Vec2::zero();
1610 positions.push((pos, tile_pos));
1611
1612 const CHUNK_OFFSET: usize = 5;
1616 let offset = CHUNK_OFFSET as i32 * TerrainChunkSize::RECT_SIZE.x as i32;
1618 let tile_offset = offset / TILE_SIZE as i32;
1621 let pos_offset = tile_offset * TILE_SIZE as i32;
1622
1623 let pos = origin + pos_offset * dir.to_vec2();
1625 let tile_pos = tile_offset * dir.to_vec2();
1626 positions.push((pos, tile_pos));
1627
1628 let mut last_pos = pos;
1632 let mut last_tile_pos = tile_pos;
1633 for j in 1..(CHUNK_OFFSET * 9 + 1) {
1634 let c_downhill = land.get_chunk_wpos(last_pos).and_then(|c| c.downhill);
1635 if let Some(downhill) = c_downhill {
1636 let downhill_chunk =
1637 downhill.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / (sz as i32));
1638 let downhill_chunk_pos = TerrainChunkSize::center_wpos(downhill_chunk);
1639 let downhill_vec = downhill_chunk_pos - last_pos;
1640 let tile_offset = downhill_vec / (TILE_SIZE as i32);
1643 let pos_offset = tile_offset * TILE_SIZE as i32;
1644 let pos = last_pos + pos_offset;
1645 let tile_pos = last_tile_pos + tile_offset;
1646 last_pos = pos;
1647 last_tile_pos = tile_pos;
1648 if j % CHUNK_OFFSET == 0 {
1651 positions.push((pos, tile_pos));
1652 }
1653 } else {
1654 valid_course = false;
1655 }
1656 }
1657 if valid_course && positions.len() > 1 {
1664 for (i, window) in positions.windows(2).enumerate() {
1665 if !window.is_empty() {
1666 let [(pos, tile_pos), (next_pos, next_tile_pos)] = window else {
1667 panic!(
1668 "previous condition required positions Vec to have at least two \
1669 elements"
1670 );
1671 };
1672 if i == 0 {
1673 let aabr = Aabr {
1675 min: Vec2::broadcast(-size as i32),
1676 max: Vec2::broadcast(size as i32),
1677 };
1678 let glider_platform = plot::GliderPlatform::generate(
1679 land,
1680 &mut reseed(&mut rng),
1681 &site,
1682 *pos,
1683 dir,
1684 );
1685 let alt = glider_platform.alt - 5;
1686 let plot = site.create_plot(Plot {
1687 kind: PlotKind::GliderPlatform(glider_platform),
1688 root_tile: aabr.center(),
1689 tiles: aabr_tiles(aabr).collect(),
1690 });
1691 site.blit_aabr(aabr, Tile {
1692 kind: TileKind::Building,
1693 plot: Some(plot),
1694 hard_alt: Some(alt),
1695 });
1696 } else if i < 9 {
1697 let dir = if i > 1 {
1700 Dir::from_vec2(next_pos - pos)
1701 } else {
1702 dir
1703 };
1704 let aabr = Aabr {
1705 min: Vec2::broadcast(-size as i32) + tile_pos,
1706 max: Vec2::broadcast(size as i32) + tile_pos,
1707 };
1708 let glider_ring = plot::GliderRing::generate(
1709 land,
1710 &mut reseed(&mut rng),
1711 &site,
1712 pos,
1713 i,
1714 dir,
1715 );
1716 let plot = site.create_plot(Plot {
1717 kind: PlotKind::GliderRing(glider_ring),
1718 root_tile: aabr.center(),
1719 tiles: aabr_tiles(aabr).collect(),
1720 });
1721 site.blit_aabr(aabr, Tile {
1722 kind: TileKind::Building,
1723 plot: Some(plot),
1724 hard_alt: None,
1725 });
1726 } else if i == 9 {
1727 let dir = Dir::from_vec2(next_pos - pos);
1731 let aabr = Aabr {
1732 min: Vec2::broadcast(-size as i32) + tile_pos,
1733 max: Vec2::broadcast(size as i32) + tile_pos,
1734 };
1735 let glider_ring = plot::GliderRing::generate(
1736 land,
1737 &mut reseed(&mut rng),
1738 &site,
1739 pos,
1740 i,
1741 dir,
1742 );
1743 let plot = site.create_plot(Plot {
1744 kind: PlotKind::GliderRing(glider_ring),
1745 root_tile: aabr.center(),
1746 tiles: aabr_tiles(aabr).collect(),
1747 });
1748 site.blit_aabr(aabr, Tile {
1749 kind: TileKind::Building,
1750 plot: Some(plot),
1751 hard_alt: None,
1752 });
1753 let size = 10.0;
1755 let aabr = Aabr {
1756 min: Vec2::broadcast(-size as i32) + next_tile_pos,
1757 max: Vec2::broadcast(size as i32) + next_tile_pos,
1758 };
1759 let glider_finish = plot::GliderFinish::generate(
1760 land,
1761 &mut reseed(&mut rng),
1762 &site,
1763 *next_pos,
1764 );
1765 let plot = site.create_plot(Plot {
1766 kind: PlotKind::GliderFinish(glider_finish),
1767 root_tile: aabr.center(),
1768 tiles: aabr_tiles(aabr).collect(),
1769 });
1770 site.blit_aabr(aabr, Tile {
1771 kind: TileKind::Building,
1772 plot: Some(plot),
1773 hard_alt: None,
1774 });
1775 }
1776 }
1777 }
1778 }
1779
1780 site
1781 }
1782
1783 pub fn generate_cliff_town(
1784 land: &Land,
1785 index: IndexRef,
1786 rng: &mut impl Rng,
1787 origin: Vec2<i32>,
1788 generator_stats: &mut SitesGenMeta,
1789 ) -> Self {
1790 let mut rng = reseed(rng);
1791 let name = NameGen::location(&mut rng).generate_arabic();
1792 let mut site = Site {
1793 origin,
1794 name: name.clone(),
1795 kind: Some(SiteKind::CliffTown),
1796 ..Site::default()
1797 };
1798 let mut campfires = 0;
1799 let road_kind = plot::RoadKind {
1800 lights: plot::RoadLights::Default,
1801 material: plot::RoadMaterial::Sandstone,
1802 };
1803
1804 generator_stats.add(&site.name, GenStatSiteKind::CliffTown);
1806 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1807
1808 let build_chance = Lottery::from(vec![(30.0, 1), (50.0, 2)]);
1809 let mut airship_docks = 0;
1810 for _ in 0..80 {
1811 match *build_chance.choose_seeded(rng.gen()) {
1812 1 => {
1813 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
1815 generator_stats.attempt(&site.name, GenStatPlotKind::House);
1816 let campfire = campfires < 4;
1817 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1818 site.find_roadside_aabr(
1819 &mut rng,
1820 8..(size + 1).pow(2),
1821 Extent2::broadcast(size),
1822 )
1823 }) {
1824 let cliff_tower = plot::CliffTower::generate(
1825 land,
1826 index,
1827 &mut reseed(&mut rng),
1828 &site,
1829 door_tile,
1830 door_dir,
1831 aabr,
1832 campfire,
1833 alt,
1834 );
1835 let cliff_tower_alt = cliff_tower.alt;
1836 let plot = site.create_plot(Plot {
1837 kind: PlotKind::CliffTower(cliff_tower),
1838 root_tile: aabr.center(),
1839 tiles: aabr_tiles(aabr).collect(),
1840 });
1841 site.blit_aabr(aabr, Tile {
1842 kind: TileKind::Building,
1843 plot: Some(plot),
1844 hard_alt: Some(cliff_tower_alt),
1845 });
1846 campfires += 1;
1847 generator_stats.success(&site.name, GenStatPlotKind::House);
1848 } else {
1849 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1850 }
1851 },
1852 2 if airship_docks < 1 => {
1853 let size = 25u32;
1855 generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
1856 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1857 site.find_roadside_aabr(&mut rng, 625..626, Extent2::broadcast(size))
1858 }) {
1859 let cliff_town_airship_dock = plot::CliffTownAirshipDock::generate(
1860 land,
1861 index,
1862 &mut reseed(&mut rng),
1863 &site,
1864 door_tile,
1865 door_dir,
1866 aabr,
1867 alt,
1868 );
1869 let cliff_town_airship_dock_alt = cliff_town_airship_dock.alt;
1870 let plot = site.create_plot(Plot {
1871 kind: PlotKind::CliffTownAirshipDock(cliff_town_airship_dock),
1872 root_tile: aabr.center(),
1873 tiles: aabr_tiles(aabr).collect(),
1874 });
1875
1876 site.blit_aabr(aabr, Tile {
1877 kind: TileKind::Building,
1878 plot: Some(plot),
1879 hard_alt: Some(cliff_town_airship_dock_alt),
1880 });
1881 airship_docks += 1;
1882 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
1883 } else {
1884 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1885 }
1886 },
1887 _ => {},
1888 }
1889 }
1890
1891 site.demarcate_obstacles(land);
1892 site
1893 }
1894
1895 pub fn generate_savannah_town(
1896 land: &Land,
1897 index: IndexRef,
1898 rng: &mut impl Rng,
1899 origin: Vec2<i32>,
1900 generator_stats: &mut SitesGenMeta,
1901 ) -> Self {
1902 let mut rng = reseed(rng);
1903 let name = NameGen::location(&mut rng).generate_savannah_custom();
1904 let mut site = Site {
1905 origin,
1906 name: name.clone(),
1907 kind: Some(SiteKind::SavannahTown),
1908 ..Site::default()
1909 };
1910 let road_kind = plot::RoadKind {
1911 lights: plot::RoadLights::Default,
1912 material: plot::RoadMaterial::Dirt,
1913 };
1914
1915 site.demarcate_obstacles(land);
1917 generator_stats.add(&site.name, GenStatSiteKind::SavannahTown);
1918 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1919
1920 let mut workshops = 0;
1921 let mut airship_dock = 0;
1922 let build_chance = Lottery::from(vec![(25.0, 1), (5.0, 2), (5.0, 3), (15.0, 4), (5.0, 5)]);
1923
1924 for _ in 0..50 {
1925 match *build_chance.choose_seeded(rng.gen()) {
1926 n if (n == 2 && workshops < 3) || workshops == 0 => {
1927 let size = (4.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
1929 generator_stats.attempt(&site.name, GenStatPlotKind::Workshop);
1930 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1931 site.find_roadside_aabr(
1932 &mut rng,
1933 4..(size + 1).pow(2),
1934 Extent2::broadcast(size),
1935 )
1936 }) {
1937 let savannah_workshop = plot::SavannahWorkshop::generate(
1938 land,
1939 &mut reseed(&mut rng),
1940 &site,
1941 door_tile,
1942 door_dir,
1943 aabr,
1944 alt,
1945 );
1946 let savannah_workshop_alt = savannah_workshop.alt;
1947 let plot = site.create_plot(Plot {
1948 kind: PlotKind::SavannahWorkshop(savannah_workshop),
1949 root_tile: aabr.center(),
1950 tiles: aabr_tiles(aabr).collect(),
1951 });
1952
1953 site.blit_aabr(aabr, Tile {
1954 kind: TileKind::Building,
1955 plot: Some(plot),
1956 hard_alt: Some(savannah_workshop_alt),
1957 });
1958 workshops += 1;
1959 generator_stats.success(&site.name, GenStatPlotKind::Workshop);
1960 } else {
1961 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1962 }
1963 },
1964 1 => {
1965 let size = (4.0 + rng.gen::<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, door_dir, alt)) = 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 door_dir,
2015 aabr,
2016 alt,
2017 );
2018 let savannah_airship_dock_alt = savannah_airship_dock.alt;
2019 let plot = site.create_plot(Plot {
2020 kind: PlotKind::SavannahAirshipDock(savannah_airship_dock),
2021 root_tile: aabr.center(),
2022 tiles: aabr_tiles(aabr).collect(),
2023 });
2024
2025 site.blit_aabr(aabr, Tile {
2026 kind: TileKind::Building,
2027 plot: Some(plot),
2028 hard_alt: Some(savannah_airship_dock_alt),
2029 });
2030 airship_dock += 1;
2031 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
2032 } else {
2033 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2034 }
2035 },
2036 4 => {
2038 Self::generate_farm(false, &mut rng, &mut site, land);
2039 },
2040 5 => {
2041 Self::generate_barn(false, &mut rng, &mut site, land, index);
2042 },
2043 _ => {},
2044 }
2045 }
2046 site
2047 }
2048
2049 pub fn generate_coastal_town(
2050 land: &Land,
2051 index: IndexRef,
2052 rng: &mut impl Rng,
2053 origin: Vec2<i32>,
2054 generator_stats: &mut SitesGenMeta,
2055 ) -> Self {
2056 let mut rng = reseed(rng);
2057 let name = NameGen::location(&mut rng).generate_danari();
2058 let mut site = Site {
2059 origin,
2060 name: name.clone(),
2061 kind: Some(SiteKind::CoastalTown),
2062 ..Site::default()
2063 };
2064 let road_kind = plot::RoadKind {
2065 lights: plot::RoadLights::Default,
2066 material: plot::RoadMaterial::Marble,
2067 };
2068
2069 site.demarcate_obstacles(land);
2071 generator_stats.add(&site.name, GenStatSiteKind::CoastalTown);
2072 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
2073
2074 let mut workshops = 0;
2075 let build_chance = Lottery::from(vec![(38.0, 1), (5.0, 2), (15.0, 3), (15.0, 4), (5.0, 5)]);
2076 let mut airship_docks = 0;
2077 for _ in 0..55 {
2078 match *build_chance.choose_seeded(rng.gen()) {
2079 n if (n == 2 && workshops < 3) || workshops == 0 => {
2080 let size = (7.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2082 generator_stats.attempt(&site.name, GenStatPlotKind::Workshop);
2083 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2084 site.find_roadside_aabr(
2085 &mut rng,
2086 7..(size + 1).pow(2),
2087 Extent2::broadcast(size),
2088 )
2089 }) {
2090 let coastal_workshop = plot::CoastalWorkshop::generate(
2091 land,
2092 &mut reseed(&mut rng),
2093 &site,
2094 door_tile,
2095 door_dir,
2096 aabr,
2097 alt,
2098 );
2099 let coastal_workshop_alt = coastal_workshop.alt;
2100 let plot = site.create_plot(Plot {
2101 kind: PlotKind::CoastalWorkshop(coastal_workshop),
2102 root_tile: aabr.center(),
2103 tiles: aabr_tiles(aabr).collect(),
2104 });
2105
2106 site.blit_aabr(aabr, Tile {
2107 kind: TileKind::Building,
2108 plot: Some(plot),
2109 hard_alt: Some(coastal_workshop_alt),
2110 });
2111 workshops += 1;
2112 generator_stats.success(&site.name, GenStatPlotKind::Workshop);
2113 } else {
2114 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2115 }
2116 },
2117 1 => {
2118 let size = (7.0 + rng.gen::<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: 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.gen()) {
2280 1 => {
2282 let size = (9.0 + rng.gen::<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.gen::<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.gen::<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.gen::<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: 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: 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: "".to_string(),
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: "".to_string(),
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: "".to_string(),
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: "".to_string(),
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: {
2746 let name = NameGen::location(&mut rng).generate();
2747 match rng.gen_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: {
2791 let name = NameGen::location(&mut rng).generate();
2792 match rng.gen_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: {
2831 let name = NameGen::location(&mut rng).generate_vampire();
2832 match rng.gen_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: format!("Bridge of {}", NameGen::location(&mut rng).generate_town()),
2881 kind: Some(SiteKind::Bridge(start_chunk, end_chunk)),
2882 ..Site::default()
2883 };
2884
2885 let start_tile = site.wpos_tile_pos(start);
2886 let end_tile = site.wpos_tile_pos(end);
2887
2888 let width = 1;
2889
2890 let orth = (start_tile - end_tile).yx().map(|dir| dir.signum().abs());
2891
2892 let start_aabr = Aabr {
2893 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
2894 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
2895 };
2896
2897 let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
2898
2899 let start_tile = site.wpos_tile_pos(bridge.start.xy());
2900 let end_tile = site.wpos_tile_pos(bridge.end.xy());
2901
2902 let width = (bridge.width() + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
2903 let aabr = Aabr {
2904 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
2905 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
2906 };
2907
2908 let line = LineSegment2 {
2909 start: site.tile_wpos(bridge.dir.select_aabr_with(start_aabr, start_aabr.center())),
2910 end: site.tile_wpos(
2911 bridge
2912 .dir
2913 .opposite()
2914 .select_aabr_with(start_aabr, start_aabr.center()),
2915 ),
2916 }
2917 .as_();
2918
2919 for y in start_aabr.min.y..start_aabr.max.y {
2920 for x in start_aabr.min.x..start_aabr.max.x {
2921 let tpos = Vec2::new(x, y);
2922 let tile_aabr = Aabr {
2923 min: site.tile_wpos(tpos),
2924 max: site.tile_wpos(tpos + 1) - 1,
2925 };
2926 if let Some(tile) = site.tiles.get_mut(tpos) {
2927 let closest_point = line.projected_point(tile_aabr.center().as_());
2928 let w = TILE_SIZE as f32;
2929 if tile_aabr
2930 .as_()
2931 .projected_point(closest_point)
2932 .distance_squared(closest_point)
2933 < w.powi(2)
2934 {
2935 tile.kind = TileKind::Path {
2936 closest_pos: closest_point,
2937 path: Path { width: w },
2938 };
2939 }
2940 }
2941 }
2942 }
2943
2944 let plot = site.create_plot(Plot {
2945 kind: PlotKind::Bridge(bridge),
2946 root_tile: start_tile,
2947 tiles: aabr_tiles(aabr).collect(),
2948 });
2949
2950 site.blit_aabr(aabr, Tile {
2951 kind: TileKind::Bridge,
2952 plot: Some(plot),
2953 hard_alt: None,
2954 });
2955
2956 site
2957 }
2958
2959 pub fn wpos_tile_pos(&self, wpos2d: Vec2<i32>) -> Vec2<i32> {
2960 (wpos2d - self.origin).map(|e| e.div_euclid(TILE_SIZE as i32))
2961 }
2962
2963 pub fn wpos_tile(&self, wpos2d: Vec2<i32>) -> &Tile {
2964 self.tiles.get(self.wpos_tile_pos(wpos2d))
2965 }
2966
2967 pub fn tile_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> { self.origin + tile * TILE_SIZE as i32 }
2968
2969 pub fn tile_center_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> {
2970 self.origin + tile * TILE_SIZE as i32 + TILE_SIZE as i32 / 2
2971 }
2972
2973 pub fn render_tile(&self, canvas: &mut Canvas, tpos: Vec2<i32>) {
2974 let tile = self.tiles.get(tpos);
2975 let twpos = self.tile_wpos(tpos);
2976 let border = TILE_SIZE as i32;
2977 let cols = (-border..TILE_SIZE as i32 + border).flat_map(|y| {
2978 (-border..TILE_SIZE as i32 + border)
2979 .map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))
2980 });
2981 if let TileKind::Path { closest_pos, path } = &tile.kind {
2982 let near_connections = CARDINALS.iter().filter_map(|rpos| {
2983 let npos = tpos + rpos;
2984 let tile = self.tiles.get(npos);
2985 let tile_aabr = Aabr {
2986 min: self.tile_wpos(tpos).map(|e| e as f32),
2987 max: self.tile_wpos(tpos + 1).map(|e| e as f32) - 1.0,
2988 };
2989 match tile.kind {
2990 TileKind::Road { a, b, w } => {
2991 if let Some(PlotKind::Road(road)) = tile.plot.map(|p| &self.plot(p).kind) {
2992 let start = road.path.nodes[a as usize];
2993 let end = road.path.nodes[b as usize];
2994 let dir = Dir::from_vec2(end - start);
2995 let orth = dir.orthogonal();
2996 let aabr = Aabr {
2997 min: self.tile_center_wpos(start)
2998 - w as i32 * 2 * orth.to_vec2()
2999 - dir.to_vec2() * TILE_SIZE as i32 / 2,
3000 max: self.tile_center_wpos(end)
3001 + w as i32 * 2 * orth.to_vec2()
3002 + dir.to_vec2() * TILE_SIZE as i32 / 2,
3003 }
3004 .made_valid()
3005 .as_();
3006 Some(aabr)
3007 } else {
3008 None
3009 }
3010 },
3011 TileKind::Bridge | TileKind::Plaza => Some(tile_aabr),
3012 _ => tile
3013 .plot
3014 .and_then(|plot| self.plot(plot).kind().meta())
3015 .and_then(|meta| meta.door_tile())
3016 .is_some_and(|door_tile| door_tile == npos)
3017 .then_some(tile_aabr),
3018 }
3019 });
3020 cols.for_each(|(wpos2d, _offs)| {
3021 let wpos2df = wpos2d.map(|e| e as f32);
3022
3023 if closest_pos.distance_squared(wpos2d.as_()) < path.width.powi(2)
3024 || near_connections
3025 .clone()
3026 .map(|aabr| aabr.distance_to_point(wpos2df))
3027 .min_by_key(|d| (*d * 100.0) as i32)
3028 .is_some_and(|d| d <= 1.5)
3029 {
3030 let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
3031 let sub_surface_color = canvas
3032 .col(wpos2d)
3033 .map_or(Rgb::zero(), |col| col.sub_surface_color);
3034 for z in -8..6 {
3035 let wpos = Vec3::new(wpos2d.x, wpos2d.y, alt + z);
3036 canvas.map(wpos, |b| {
3037 if b.kind() == BlockKind::Snow {
3038 b.into_vacant()
3039 } else if b.is_filled() {
3040 if b.is_terrain() {
3041 Block::new(
3042 BlockKind::Earth,
3043 path.surface_color((sub_surface_color * 255.0).as_(), wpos),
3044 )
3045 } else {
3046 b
3047 }
3048 } else {
3049 b.into_vacant()
3050 }
3051 })
3052 }
3053 }
3054 });
3055 }
3056 }
3057
3058 pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
3059 let tile_aabr = Aabr {
3060 min: self.wpos_tile_pos(canvas.wpos()) - 1,
3061 max: self
3062 .wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2)
3063 + 3, };
3065
3066 let mut plots = DHashSet::default();
3068
3069 for y in tile_aabr.min.y..tile_aabr.max.y {
3070 for x in tile_aabr.min.x..tile_aabr.max.x {
3071 self.render_tile(canvas, Vec2::new(x, y));
3072
3073 if let Some(plot) = self.tiles.get(Vec2::new(x, y)).plot {
3074 plots.insert(plot);
3075 }
3076 }
3077 }
3078
3079 canvas.foreach_col(|canvas, wpos2d, col| {
3080 let tile = self.wpos_tile(wpos2d);
3081 for z_off in (-2..4).rev() {
3082 if let Some(plot) = tile.plot.map(|p| &self.plots[p]) {
3083 canvas.map_resource(
3084 Vec3::new(
3085 wpos2d.x,
3086 wpos2d.y,
3087 foreach_plot!(&plot.kind, plot => plot.rel_terrain_offset(col)) + z_off,
3088 ),
3089 |block| {
3090 foreach_plot!(
3091 &plot.kind,
3092 plot => plot.terrain_surface_at(
3093 wpos2d,
3094 block,
3095 dynamic_rng,
3096 col,
3097 z_off,
3098 self,
3099 ).unwrap_or(block),
3100 )
3101 },
3102 );
3103 }
3104 }
3105 });
3106
3107 for (id, plot) in self.plots.iter() {
3109 if matches!(&plot.kind, PlotKind::GiantTree(_)) {
3110 plots.insert(id);
3111 }
3112 }
3113
3114 let mut plots_to_render = plots.into_iter().collect::<Vec<_>>();
3115 plots_to_render
3117 .sort_unstable_by_key(|plot| (self.plots[*plot].kind.render_ordering(), *plot));
3118
3119 let wpos2d = canvas.info().wpos();
3120 let chunk_aabr = Aabr {
3121 min: wpos2d,
3122 max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
3123 };
3124
3125 let info = canvas.info();
3126
3127 for plot in plots_to_render {
3128 let (prim_tree, fills, mut entities) =
3129 foreach_plot!(&self.plots[plot].kind, plot => plot.render_collect(self, canvas));
3130
3131 let mut spawn = |pos, last_block| {
3132 if let Some(entity) = match &self.plots[plot].kind {
3133 PlotKind::GiantTree(tree) => tree.entity_at(pos, &last_block, dynamic_rng),
3134 _ => None,
3135 } {
3136 entities.push(entity);
3137 }
3138 };
3139
3140 let mut entities_from_structure_blocks = Vec::<EntityInfo>::new();
3141
3142 for (prim, fill) in fills {
3143 for mut aabb in Fill::get_bounds_disjoint(&prim_tree, prim) {
3144 aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z);
3145 aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z);
3146
3147 for x in aabb.min.x..aabb.max.x {
3148 for y in aabb.min.y..aabb.max.y {
3149 let wpos = Vec2::new(x, y);
3150 let col_tile = self.wpos_tile(wpos);
3151 if
3152 col_tile
3154 .plot
3155 .and_then(|p| self.plots[p].z_range())
3156 .zip(self.plots[plot].z_range())
3157 .is_some_and(|(a, b)| a.end > b.end)
3158 {
3159 continue;
3160 }
3161 let mut last_block = None;
3162
3163 let col = canvas
3164 .col(wpos)
3165 .map(|col| col.get_info())
3166 .unwrap_or_default();
3167
3168 for z in aabb.min.z..aabb.max.z {
3169 let pos = Vec3::new(x, y, z);
3170
3171 let mut sprite_cfg = None;
3172
3173 let map = |block| {
3174 let (current_block, _sb, entity_path) = fill.sample_at(
3175 &prim_tree,
3176 prim,
3177 pos,
3178 &info,
3179 block,
3180 &mut sprite_cfg,
3181 &col,
3182 );
3183
3184 if let Some(spec) = entity_path {
3185 let entity = EntityInfo::at(pos.as_());
3186 let mut loadout_rng = rand::thread_rng();
3187 entities_from_structure_blocks.push(
3188 entity.with_asset_expect(&spec, &mut loadout_rng, None),
3189 );
3190 };
3191
3192 if let (Some(last_block), None) = (last_block, current_block) {
3193 spawn(pos, last_block);
3194 }
3195 last_block = current_block;
3196 current_block.unwrap_or(block)
3197 };
3198
3199 match fill {
3200 Fill::ResourceSprite { .. } | Fill::Prefab(..) => {
3201 canvas.map_resource(pos, map)
3202 },
3203 _ => canvas.map(pos, map),
3204 };
3205
3206 if let Some(sprite_cfg) = sprite_cfg {
3207 canvas.set_sprite_cfg(pos, sprite_cfg);
3208 }
3209 }
3210 if let Some(block) = last_block {
3211 spawn(Vec3::new(x, y, aabb.max.z), block);
3212 }
3213 }
3214 }
3215 }
3216 }
3217
3218 for entity in entities {
3219 canvas.spawn(entity);
3220 }
3221
3222 for entity in entities_from_structure_blocks {
3223 canvas.spawn(entity);
3224 }
3225 }
3226 }
3227
3228 pub fn apply_supplement(
3229 &self,
3230 dynamic_rng: &mut impl Rng,
3231 wpos2d: Vec2<i32>,
3232 supplement: &mut crate::ChunkSupplement,
3233 ) {
3234 for (_, plot) in self.plots.iter() {
3235 match &plot.kind {
3236 PlotKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement),
3237 PlotKind::Adlet(a) => a.apply_supplement(dynamic_rng, wpos2d, supplement),
3238 _ => {},
3239 }
3240 }
3241 }
3242}
3243
3244pub fn test_site() -> Site {
3245 let index = crate::index::Index::new(0);
3246 let index_ref = IndexRef {
3247 colors: &index.colors(),
3248 features: &index.features(),
3249 index: &index,
3250 };
3251 let mut gen_meta = SitesGenMeta::new(0);
3252 Site::generate_city(
3253 &Land::empty(),
3254 index_ref,
3255 &mut thread_rng(),
3256 Vec2::zero(),
3257 0.5,
3258 None,
3259 &mut gen_meta,
3260 )
3261}
3262
3263fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
3264 if land
3265 .get_chunk_wpos(wpos)
3266 .is_none_or(|c| c.river.near_water())
3267 {
3268 Some(HazardKind::Water)
3269 } else {
3270 Some(land.get_gradient_approx(wpos))
3271 .filter(|g| *g > 0.8)
3272 .map(|gradient| HazardKind::Hill { gradient })
3273 }
3274}
3275
3276fn temp_at_wpos(land: &Land, wpos: Vec2<i32>) -> f32 {
3277 land.get_chunk_wpos(wpos)
3278 .map(|c| c.temp)
3279 .unwrap_or(CONFIG.temperate_temp)
3280}
3281
3282pub fn aabr_tiles(aabr: Aabr<i32>) -> impl Iterator<Item = Vec2<i32>> {
3283 (0..aabr.size().h)
3284 .flat_map(move |y| (0..aabr.size().w).map(move |x| aabr.min + Vec2::new(x, y)))
3285}
3286
3287fn get_gradient_average(aabr: Aabr<i32>, land: &Land) -> f32 {
3291 let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as i32;
3292
3293 let mut gradient_sum = 0.0;
3294 let mut gradient_sample_count = 0;
3295
3296 let aabr_center = aabr.center();
3297 let range_x_min = aabr_center.x - chunk_size;
3298 let range_x_max = aabr_center.x + chunk_size;
3299 let range_y_min = aabr_center.y - chunk_size;
3300 let range_y_max = aabr_center.y + chunk_size;
3301
3302 for x_pos in (range_x_min..=range_x_max).step_by(chunk_size as usize) {
3303 for y_pos in (range_y_min..=range_y_max).step_by(chunk_size as usize) {
3304 let gradient_at_pos = land.get_gradient_approx(Vec2::new(x_pos, y_pos));
3305 gradient_sum += gradient_at_pos;
3306 gradient_sample_count += 1;
3307 }
3308 }
3309
3310 gradient_sum / (gradient_sample_count as f32)
3311}