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 dir = Vec2::<f32>::zero()
464 .map(|_| rng.gen_range(-1.0..1.0))
465 .normalized();
466
467 let search_pos = dir.map(|e: f32| e.round() as i32) * ((self.radius() / 6.0) as i32);
470
471 self.find_aabr(search_pos, area_range, min_dims)
472 }
473
474 pub fn make_plaza_at(
475 &mut self,
476 land: &Land,
477 index: IndexRef,
478 tile_aabr: Aabr<i32>,
479 rng: &mut impl Rng,
480 road_kind: plot::RoadKind,
481 ) -> Option<Id<Plot>> {
482 let tpos = tile_aabr.center();
483 let plaza_alt = land.get_alt_approx(self.tile_center_wpos(tpos)) as i32;
484
485 let plaza = self.create_plot(Plot {
486 kind: PlotKind::Plaza(plot::Plaza::generate(
487 tile_aabr, road_kind, self, land, index,
488 )),
489 root_tile: tpos,
490 tiles: aabr_tiles(tile_aabr).collect(),
491 });
492 self.plazas.push(plaza);
493 self.blit_aabr(tile_aabr, Tile {
494 kind: TileKind::Plaza,
495 plot: Some(plaza),
496 hard_alt: Some(plaza_alt),
497 });
498
499 let mut already_pathed = vec![];
500 for _ in (0..rng.gen_range(1.25..2.25) as u16).rev() {
502 if let Some(&p) = self
503 .plazas
504 .iter()
505 .filter(|&&p| {
506 !already_pathed.contains(&p)
507 && p != plaza
508 && already_pathed.iter().all(|&ap| {
509 (self.plot(ap).root_tile - tpos)
510 .map(|e| e as f32)
511 .normalized()
512 .dot(
513 (self.plot(p).root_tile - tpos)
514 .map(|e| e as f32)
515 .normalized(),
516 )
517 < 0.0
518 })
519 })
520 .min_by_key(|&&p| self.plot(p).root_tile.distance_squared(tpos))
521 {
522 self.create_road(
523 land,
524 self.plot(p).root_tile,
525 tpos,
526 2, road_kind,
528 );
529 already_pathed.push(p);
530 } else {
531 break;
532 }
533 }
534
535 Some(plaza)
536 }
537
538 pub fn make_plaza(
539 &mut self,
540 land: &Land,
541 index: IndexRef,
542 rng: &mut impl Rng,
543 generator_stats: &mut SitesGenMeta,
544 site_name: &String,
545 road_kind: plot::RoadKind,
546 ) -> Option<Id<Plot>> {
547 generator_stats.attempt(site_name, GenStatPlotKind::Plaza);
548 let plaza_radius = rng.gen_range(1..4);
549 let plaza_dist = 6.5 + plaza_radius as f32 * 3.0;
550 let aabr = attempt(32, || {
551 self.plazas
552 .choose(rng)
553 .map(|&p| {
554 self.plot(p).root_tile
555 + (Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0))
556 .normalized()
557 * plaza_dist)
558 .map(|e| e as i32)
559 })
560 .or_else(|| Some(Vec2::zero()))
561 .map(|center_tile| Aabr {
562 min: center_tile + Vec2::broadcast(-plaza_radius),
563 max: center_tile + Vec2::broadcast(plaza_radius + 1),
564 })
565 .filter(|&aabr| {
566 rng.gen_range(0..48) > aabr.center().map(|e| e.abs()).reduce_max()
567 && aabr_tiles(aabr).all(|tile| !self.tiles.get(tile).is_obstacle())
568 })
569 .filter(|&aabr| {
570 self.plazas.iter().all(|&p| {
571 let dist_sqr = if let PlotKind::Plaza(plaza) = &self.plot(p).kind {
572 let intersection = plaza.aabr.intersection(aabr);
573 intersection
576 .size()
577 .map(|e| e.min(0) as f32)
578 .magnitude_squared()
579 } else {
580 let r = self.plot(p).root_tile();
581 let closest_point = aabr.projected_point(r);
582 closest_point.as_::<f32>().distance_squared(r.as_::<f32>())
583 };
584 dist_sqr > (plaza_dist * 0.85).powi(2)
585 })
586 })
587 })?;
588 generator_stats.success(site_name, GenStatPlotKind::Plaza);
589 self.make_plaza_at(land, index, aabr, rng, road_kind)
590 }
591
592 pub fn demarcate_obstacles(&mut self, land: &Land) {
593 const SEARCH_RADIUS: u32 = 96;
594
595 Spiral2d::new()
596 .take((SEARCH_RADIUS * 2 + 1).pow(2) as usize)
597 .for_each(|tile| {
598 let wpos = self.tile_center_wpos(tile);
599 if let Some(kind) = Spiral2d::new()
600 .take(9)
601 .find_map(|rpos| wpos_is_hazard(land, wpos + rpos))
602 {
603 for &rpos in &SQUARE_4 {
604 self.tiles
606 .get_mut(tile - rpos - 1)
607 .filter(|tile| tile.is_natural())
608 .map(|tile| tile.kind = TileKind::Hazard(kind));
609 }
610 }
611 if let Some((_, path_wpos, path, _)) = land.get_nearest_path(wpos) {
612 let tile_aabr = Aabr {
613 min: self.tile_wpos(tile),
614 max: self.tile_wpos(tile + 1) - 1,
615 };
616
617 if tile_aabr
618 .projected_point(path_wpos.as_())
619 .as_()
620 .distance_squared(path_wpos)
621 < path.width.powi(2)
622 {
623 self.tiles
624 .get_mut(tile)
625 .filter(|tile| tile.is_natural())
626 .map(|tile| {
627 tile.kind = TileKind::Path {
628 closest_pos: path_wpos,
629 path,
630 }
631 });
632 }
633 }
634 });
635 }
636
637 pub fn make_initial_plaza(
654 &mut self,
655 land: &Land,
656 index: IndexRef,
657 rng: &mut impl Rng,
658 plaza_radius: u32,
659 search_inner_radius: u32,
660 search_width: u32,
661 generator_stats: &mut SitesGenMeta,
662 site_name: &String,
663 road_kind: plot::RoadKind,
664 ) -> Option<Id<Plot>> {
665 generator_stats.attempt(site_name, GenStatPlotKind::InitialPlaza);
666 let mut plaza_locations = vec![];
668 Spiral2d::with_ring(search_inner_radius, search_width).for_each(|tpos| {
670 let aabr = Aabr {
671 min: tpos - Vec2::broadcast(plaza_radius as i32),
672 max: tpos + Vec2::broadcast(plaza_radius as i32 + 1),
673 };
674 if aabr_tiles(aabr).all(|tpos| self.tiles.get(tpos).is_empty()) {
677 plaza_locations.push(aabr);
678 }
679 });
680 if plaza_locations.is_empty() {
681 self.make_plaza(land, index, rng, generator_stats, site_name, road_kind)
685 } else {
686 plaza_locations.sort_by_key(|&aabr| {
688 aabr.min
689 .map2(aabr.max, |a, b| a.abs().min(b.abs()))
690 .magnitude_squared()
691 });
692 let aabr = plaza_locations.first()?;
694 generator_stats.success(site_name, GenStatPlotKind::InitialPlaza);
695 self.make_plaza_at(land, index, *aabr, rng, road_kind)
696 }
697 }
698
699 pub fn make_initial_plaza_default(
719 &mut self,
720 land: &Land,
721 index: IndexRef,
722 rng: &mut impl Rng,
723 generator_stats: &mut SitesGenMeta,
724 site_name: &String,
725 road_kind: plot::RoadKind,
726 ) -> Option<Id<Plot>> {
727 let plaza_radius = rng.gen_range(1..4);
729 let search_inner_radius = 7 + plaza_radius;
733 const PLAZA_MAX_SEARCH_RADIUS: u32 = 24;
734 self.make_initial_plaza(
735 land,
736 index,
737 rng,
738 plaza_radius,
739 search_inner_radius,
740 PLAZA_MAX_SEARCH_RADIUS - search_inner_radius,
741 generator_stats,
742 site_name,
743 road_kind,
744 )
745 }
746
747 pub fn name(&self) -> &str { &self.name }
748
749 pub fn generate_mine(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
750 let mut rng = reseed(rng);
751 let mut site = Site {
752 origin,
753 kind: Some(SiteKind::DwarvenMine),
754 ..Site::default()
755 };
756
757 let size = 60.0;
758
759 let aabr = Aabr {
760 min: Vec2::broadcast(-size as i32),
761 max: Vec2::broadcast(size as i32),
762 };
763
764 let wpos: Vec2<i32> = [1, 2].into();
765
766 let dwarven_mine =
767 plot::DwarvenMine::generate(land, &mut reseed(&mut rng), &site, wpos, aabr);
768 site.name = dwarven_mine.name().to_string();
769 let plot = site.create_plot(Plot {
770 kind: PlotKind::DwarvenMine(dwarven_mine),
771 root_tile: aabr.center(),
772 tiles: aabr_tiles(aabr).collect(),
773 });
774
775 site.blit_aabr(aabr, Tile {
776 kind: TileKind::Empty,
777 plot: Some(plot),
778 hard_alt: Some(1_i32),
779 });
780
781 site
782 }
783
784 pub fn generate_citadel(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
785 let mut rng = reseed(rng);
786 let mut site = Site {
787 origin,
788 kind: Some(SiteKind::Citadel),
789 ..Site::default()
790 };
791 site.demarcate_obstacles(land);
792 let citadel = plot::Citadel::generate(origin, land, &mut rng);
793 site.name = citadel.name().to_string();
794 let size = citadel.radius() / tile::TILE_SIZE as i32;
795 let aabr = Aabr {
796 min: Vec2::broadcast(-size),
797 max: Vec2::broadcast(size),
798 };
799 let plot = site.create_plot(Plot {
800 kind: PlotKind::Citadel(citadel),
801 root_tile: aabr.center(),
802 tiles: aabr_tiles(aabr).collect(),
803 });
804 site.blit_aabr(aabr, Tile {
805 kind: TileKind::Building,
806 plot: Some(plot),
807 hard_alt: None,
808 });
809 site
810 }
811
812 pub fn generate_gnarling(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
813 let mut rng = reseed(rng);
814 let mut site = Site {
815 origin,
816 kind: Some(SiteKind::Gnarling),
817 ..Site::default()
818 };
819 site.demarcate_obstacles(land);
820 let gnarling_fortification = plot::GnarlingFortification::generate(origin, land, &mut rng);
821 site.name = gnarling_fortification.name().to_string();
822 let size = gnarling_fortification.radius() / TILE_SIZE as i32;
823 let aabr = Aabr {
824 min: Vec2::broadcast(-size),
825 max: Vec2::broadcast(size),
826 };
827 let plot = site.create_plot(Plot {
828 kind: PlotKind::Gnarling(gnarling_fortification),
829 root_tile: aabr.center(),
830 tiles: aabr_tiles(aabr).collect(),
831 });
832 site.blit_aabr(aabr, Tile {
833 kind: TileKind::GnarlingFortification,
834 plot: Some(plot),
835 hard_alt: None,
836 });
837 site
838 }
839
840 pub fn generate_adlet(
841 land: &Land,
842 rng: &mut impl Rng,
843 origin: Vec2<i32>,
844 index: IndexRef,
845 ) -> Self {
846 let mut rng = reseed(rng);
847 let mut site = Site {
848 origin,
849 kind: Some(SiteKind::Adlet),
850 ..Site::default()
851 };
852 site.demarcate_obstacles(land);
853 let adlet_stronghold = plot::AdletStronghold::generate(origin, land, &mut rng, index);
854 site.name = adlet_stronghold.name().to_string();
855 let (cavern_aabr, wall_aabr) = adlet_stronghold.plot_tiles(origin);
856 let plot = site.create_plot(Plot {
857 kind: PlotKind::Adlet(adlet_stronghold),
858 root_tile: cavern_aabr.center(),
859 tiles: aabr_tiles(cavern_aabr)
860 .chain(aabr_tiles(wall_aabr))
861 .collect(),
862 });
863 site.blit_aabr(cavern_aabr, Tile {
864 kind: TileKind::AdletStronghold,
865 plot: Some(plot),
866 hard_alt: None,
867 });
868 site.blit_aabr(wall_aabr, Tile {
869 kind: TileKind::AdletStronghold,
870 plot: Some(plot),
871 hard_alt: None,
872 });
873 site
874 }
875
876 pub fn generate_terracotta(
877 land: &Land,
878 index: IndexRef,
879 rng: &mut impl Rng,
880 origin: Vec2<i32>,
881 generator_stats: &mut SitesGenMeta,
882 ) -> Self {
883 let mut rng = reseed(rng);
884 let gen_name = NameGen::location(&mut rng).generate_terracotta();
885 let suffix = [
886 "Tombs",
887 "Necropolis",
888 "Ruins",
889 "Mausoleum",
890 "Cemetery",
891 "Burial Grounds",
892 "Remains",
893 "Temples",
894 "Gardens",
895 ]
896 .choose(&mut rng)
897 .unwrap();
898 let name = match rng.gen_range(0..2) {
899 0 => format!("{} {}", gen_name, suffix),
900 _ => format!("{} of {}", suffix, gen_name),
901 };
902 let mut site = Site {
903 origin,
904 name: name.clone(),
905 kind: Some(SiteKind::Terracotta),
906 ..Site::default()
907 };
908
909 site.demarcate_obstacles(land);
911 const TERRACOTTA_PLAZA_RADIUS: u32 = 3;
914 const TERRACOTTA_PLAZA_SEARCH_INNER: u32 = 17;
915 const TERRACOTTA_PLAZA_SEARCH_WIDTH: u32 = 12;
916 generator_stats.add(&site.name, GenStatSiteKind::Terracotta);
917 let road_kind = plot::RoadKind {
918 lights: plot::RoadLights::Terracotta,
919 material: plot::RoadMaterial::Sandstone,
920 };
921 site.make_initial_plaza(
922 land,
923 index,
924 &mut rng,
925 TERRACOTTA_PLAZA_RADIUS,
926 TERRACOTTA_PLAZA_SEARCH_INNER,
927 TERRACOTTA_PLAZA_SEARCH_WIDTH,
928 generator_stats,
929 &name,
930 road_kind,
931 );
932
933 let size = 15.0 as i32;
934 let aabr = Aabr {
935 min: Vec2::broadcast(-size),
936 max: Vec2::broadcast(size),
937 };
938 {
939 let terracotta_palace =
940 plot::TerracottaPalace::generate(land, &mut reseed(&mut rng), &site, aabr);
941 let terracotta_palace_alt = terracotta_palace.alt;
942 let plot = site.create_plot(Plot {
943 kind: PlotKind::TerracottaPalace(terracotta_palace),
944 root_tile: aabr.center(),
945 tiles: aabr_tiles(aabr).collect(),
946 });
947
948 site.blit_aabr(aabr, Tile {
949 kind: TileKind::Building,
950 plot: Some(plot),
951 hard_alt: Some(terracotta_palace_alt),
952 });
953 }
954 let build_chance = Lottery::from(vec![(12.0, 1), (4.0, 2)]);
955 for _ in 0..16 {
956 match *build_chance.choose_seeded(rng.gen()) {
957 1 => {
958 generator_stats.attempt(&site.name, GenStatPlotKind::House);
960 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
961 if let Some((aabr, _, _, alt)) = attempt(32, || {
962 site.find_roadside_aabr(
963 &mut rng,
964 9..(size + 1).pow(2),
965 Extent2::broadcast(size),
966 )
967 }) {
968 let terracotta_house = plot::TerracottaHouse::generate(
969 land,
970 &mut reseed(&mut rng),
971 &site,
972 aabr,
973 alt,
974 );
975 let terracotta_house_alt = terracotta_house.alt;
976 let plot = site.create_plot(Plot {
977 kind: PlotKind::TerracottaHouse(terracotta_house),
978 root_tile: aabr.center(),
979 tiles: aabr_tiles(aabr).collect(),
980 });
981
982 site.blit_aabr(aabr, Tile {
983 kind: TileKind::Building,
984 plot: Some(plot),
985 hard_alt: Some(terracotta_house_alt),
986 });
987
988 generator_stats.success(&site.name, GenStatPlotKind::House);
989 } else {
990 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
991 }
992 },
993
994 2 => {
995 generator_stats.attempt(&site.name, GenStatPlotKind::Yard);
997 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
998 if let Some((aabr, _, _, alt)) = attempt(32, || {
999 site.find_roadside_aabr(
1000 &mut rng,
1001 9..(size + 1).pow(2),
1002 Extent2::broadcast(size),
1003 )
1004 }) {
1005 let terracotta_yard = plot::TerracottaYard::generate(
1006 land,
1007 &mut reseed(&mut rng),
1008 &site,
1009 aabr,
1010 alt,
1011 );
1012 let terracotta_yard_alt = terracotta_yard.alt;
1013 let plot = site.create_plot(Plot {
1014 kind: PlotKind::TerracottaYard(terracotta_yard),
1015 root_tile: aabr.center(),
1016 tiles: aabr_tiles(aabr).collect(),
1017 });
1018
1019 site.blit_aabr(aabr, Tile {
1020 kind: TileKind::Building,
1021 plot: Some(plot),
1022 hard_alt: Some(terracotta_yard_alt),
1023 });
1024
1025 generator_stats.success(&site.name, GenStatPlotKind::Yard);
1026 } else {
1027 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1028 }
1029 },
1030 _ => {},
1031 }
1032 }
1033 site
1034 }
1035
1036 pub fn generate_myrmidon(
1037 land: &Land,
1038 index: IndexRef,
1039 rng: &mut impl Rng,
1040 origin: Vec2<i32>,
1041 generator_stats: &mut SitesGenMeta,
1042 ) -> Self {
1043 let mut rng = reseed(rng);
1044 let gen_name = NameGen::location(&mut rng).generate_danari();
1045 let suffix = ["City", "Metropolis"].choose(&mut rng).unwrap();
1046 let name = match rng.gen_range(0..2) {
1047 0 => format!("{} {}", gen_name, suffix),
1048 _ => format!("{} of {}", suffix, gen_name),
1049 };
1050 let mut site = Site {
1051 origin,
1052 name: name.clone(),
1053 kind: Some(SiteKind::Myrmidon),
1054 ..Site::default()
1055 };
1056
1057 let road_kind = plot::RoadKind {
1058 lights: plot::RoadLights::Default,
1059 material: plot::RoadMaterial::Dirt,
1060 };
1061
1062 site.demarcate_obstacles(land);
1064 const MYRMIDON_PLAZA_RADIUS: u32 = 3;
1067 const MYRMIDON_PLAZA_SEARCH_INNER: u32 = 18;
1068 const MYRMIDON_PLAZA_SEARCH_WIDTH: u32 = 12;
1069 generator_stats.add(&site.name, GenStatSiteKind::Myrmidon);
1070 generator_stats.attempt(&site.name, GenStatPlotKind::InitialPlaza);
1071 site.make_initial_plaza(
1072 land,
1073 index,
1074 &mut rng,
1075 MYRMIDON_PLAZA_RADIUS,
1076 MYRMIDON_PLAZA_SEARCH_INNER,
1077 MYRMIDON_PLAZA_SEARCH_WIDTH,
1078 generator_stats,
1079 &name,
1080 road_kind,
1081 );
1082
1083 let size = 16.0 as i32;
1084 let aabr = Aabr {
1085 min: Vec2::broadcast(-size),
1086 max: Vec2::broadcast(size),
1087 };
1088 {
1089 let myrmidon_arena =
1090 plot::MyrmidonArena::generate(land, &mut reseed(&mut rng), &site, aabr);
1091 let myrmidon_arena_alt = myrmidon_arena.alt;
1092 let plot = site.create_plot(Plot {
1093 kind: PlotKind::MyrmidonArena(myrmidon_arena),
1094 root_tile: aabr.center(),
1095 tiles: aabr_tiles(aabr).collect(),
1096 });
1097
1098 site.blit_aabr(aabr, Tile {
1099 kind: TileKind::Building,
1100 plot: Some(plot),
1101 hard_alt: Some(myrmidon_arena_alt),
1102 });
1103 }
1104 for _ in 0..30 {
1105 generator_stats.attempt(&site.name, GenStatPlotKind::House);
1107 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
1108 if let Some((aabr, _, _, alt)) = attempt(32, || {
1109 site.find_roadside_aabr(&mut rng, 9..(size + 1).pow(2), Extent2::broadcast(size))
1110 }) {
1111 let myrmidon_house =
1112 plot::MyrmidonHouse::generate(land, &mut reseed(&mut rng), &site, aabr, alt);
1113 let myrmidon_house_alt = myrmidon_house.alt;
1114 let plot = site.create_plot(Plot {
1115 kind: PlotKind::MyrmidonHouse(myrmidon_house),
1116 root_tile: aabr.center(),
1117 tiles: aabr_tiles(aabr).collect(),
1118 });
1119
1120 site.blit_aabr(aabr, Tile {
1121 kind: TileKind::Building,
1122 plot: Some(plot),
1123 hard_alt: Some(myrmidon_house_alt),
1124 });
1125
1126 generator_stats.success(&site.name, GenStatPlotKind::House);
1127 } else {
1128 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1129 }
1130 }
1131
1132 site
1133 }
1134
1135 pub fn generate_giant_tree(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
1136 let mut rng = reseed(rng);
1137 let mut site = Site {
1138 origin,
1139 kind: Some(SiteKind::GiantTree),
1140 ..Site::default()
1141 };
1142 site.demarcate_obstacles(land);
1143 let giant_tree = plot::GiantTree::generate(&site, Vec2::zero(), land, &mut rng);
1144 site.name = giant_tree.name().to_string();
1145 let size = (giant_tree.radius() / TILE_SIZE as f32).ceil() as i32;
1146 let aabr = Aabr {
1147 min: Vec2::broadcast(-size),
1148 max: Vec2::broadcast(size) + 1,
1149 };
1150 let plot = site.create_plot(Plot {
1151 kind: PlotKind::GiantTree(giant_tree),
1152 root_tile: aabr.center(),
1153 tiles: aabr_tiles(aabr).collect(),
1154 });
1155 site.blit_aabr(aabr, Tile {
1156 kind: TileKind::Building,
1157 plot: Some(plot),
1158 hard_alt: None,
1159 });
1160 site
1161 }
1162
1163 pub fn generate_city(
1165 land: &Land,
1166 index: IndexRef,
1167 rng: &mut impl Rng,
1168 origin: Vec2<i32>,
1169 size: f32,
1170 calendar: Option<&Calendar>,
1171 generator_stats: &mut SitesGenMeta,
1172 ) -> Self {
1173 let mut rng = reseed(rng);
1174 let name = NameGen::location(&mut rng).generate_town();
1175 let mut site = Site {
1176 origin,
1177 name: name.clone(),
1178 kind: Some(SiteKind::Refactor),
1179 ..Site::default()
1180 };
1181 let road_kind = plot::RoadKind {
1182 lights: plot::RoadLights::Default,
1183 material: plot::RoadMaterial::Cobblestone,
1184 };
1185
1186 site.demarcate_obstacles(land);
1188 generator_stats.add(&site.name, GenStatSiteKind::City);
1189 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1190
1191 let build_chance = Lottery::from(vec![
1192 (64.0, 1), (5.0, 2), (15.0, 3), (5.0, 4), (5.0, 5), (15.0, 6), (15.0, 7), (5.0, 8), ]);
1201
1202 let mut workshops = 0;
1204 let mut castles = 0;
1205 let mut taverns = 0;
1206 let mut airship_docks = 0;
1207
1208 for _ in 0..(size * 200.0) as i32 {
1209 match *build_chance.choose_seeded(rng.gen()) {
1210 n if (n == 5 && workshops < (size * 5.0) as i32) || workshops == 0 => {
1212 generator_stats.attempt(&site.name, GenStatPlotKind::Workshop);
1213 let size = (3.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
1214 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1215 site.find_roadside_aabr(
1216 &mut rng,
1217 4..(size + 1).pow(2),
1218 Extent2::broadcast(size),
1219 )
1220 }) {
1221 let workshop = plot::Workshop::generate(
1222 land,
1223 &mut reseed(&mut rng),
1224 &site,
1225 door_tile,
1226 door_dir,
1227 aabr,
1228 alt,
1229 );
1230 let workshop_alt = workshop.alt;
1231 let plot = site.create_plot(Plot {
1232 kind: PlotKind::Workshop(workshop),
1233 root_tile: aabr.center(),
1234 tiles: aabr_tiles(aabr).collect(),
1235 });
1236
1237 site.blit_aabr(aabr, Tile {
1238 kind: TileKind::Building,
1239 plot: Some(plot),
1240 hard_alt: Some(workshop_alt),
1241 });
1242 workshops += 1;
1243 generator_stats.success(&site.name, GenStatPlotKind::Workshop);
1244 } else {
1245 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1246 }
1247 },
1248 1 => {
1250 let size = (1.5 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
1251 generator_stats.attempt(&site.name, GenStatPlotKind::House);
1252 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1253 site.find_roadside_aabr(
1254 &mut rng,
1255 4..(size + 1).pow(2),
1256 Extent2::broadcast(size),
1257 )
1258 }) {
1259 let house = plot::House::generate(
1260 land,
1261 &mut reseed(&mut rng),
1262 &site,
1263 door_tile,
1264 door_dir,
1265 aabr,
1266 calendar,
1267 alt,
1268 );
1269 let house_alt = house.alt;
1270 let plot = site.create_plot(Plot {
1271 kind: PlotKind::House(house),
1272 root_tile: aabr.center(),
1273 tiles: aabr_tiles(aabr).collect(),
1274 });
1275
1276 site.blit_aabr(aabr, Tile {
1277 kind: TileKind::Building,
1278 plot: Some(plot),
1279 hard_alt: Some(house_alt),
1280 });
1281 generator_stats.success(&site.name, GenStatPlotKind::House);
1282 } else {
1283 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1284 }
1285 },
1286 2 => {
1288 generator_stats.attempt(&site.name, GenStatPlotKind::GuardTower);
1289 if let Some((_aabr, _, _door_dir, _)) = attempt(10, || {
1290 site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
1291 }) {
1292 }
1309 },
1310 3 => {
1312 Self::generate_farm(false, &mut rng, &mut site, land);
1313 },
1314 4 if castles < 1 => {
1316 generator_stats.attempt(&site.name, GenStatPlotKind::Castle);
1317 if let Some((aabr, _entrance_tile, _door_dir, _alt)) = attempt(32, || {
1318 site.find_roadside_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))
1319 }) {
1320 let offset = rng.gen_range(5..(aabr.size().w.min(aabr.size().h) - 4));
1321 let gate_aabr = Aabr {
1322 min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y),
1323 max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1),
1324 };
1325 let castle = plot::Castle::generate(land, &mut rng, &site, aabr, gate_aabr);
1326 let castle_alt = castle.alt;
1327 let plot = site.create_plot(Plot {
1328 kind: PlotKind::Castle(castle),
1329 root_tile: aabr.center(),
1330 tiles: aabr_tiles(aabr).collect(),
1331 });
1332
1333 let wall_north = Tile {
1334 kind: TileKind::Wall(Dir::Y),
1335 plot: Some(plot),
1336 hard_alt: Some(castle_alt),
1337 };
1338
1339 let wall_east = Tile {
1340 kind: TileKind::Wall(Dir::X),
1341 plot: Some(plot),
1342 hard_alt: Some(castle_alt),
1343 };
1344 for x in 0..aabr.size().w {
1345 site.tiles
1346 .set(aabr.min + Vec2::new(x, 0), wall_east.clone());
1347 site.tiles.set(
1348 aabr.min + Vec2::new(x, aabr.size().h - 1),
1349 wall_east.clone(),
1350 );
1351 }
1352 for y in 0..aabr.size().h {
1353 site.tiles
1354 .set(aabr.min + Vec2::new(0, y), wall_north.clone());
1355 site.tiles.set(
1356 aabr.min + Vec2::new(aabr.size().w - 1, y),
1357 wall_north.clone(),
1358 );
1359 }
1360
1361 let gate = Tile {
1362 kind: TileKind::Gate,
1363 plot: Some(plot),
1364 hard_alt: Some(castle_alt),
1365 };
1366 let tower_parapet = Tile {
1367 kind: TileKind::Tower(RoofKind::Parapet),
1368 plot: Some(plot),
1369 hard_alt: Some(castle_alt),
1370 };
1371 let tower_pyramid = Tile {
1372 kind: TileKind::Tower(RoofKind::Pyramid),
1373 plot: Some(plot),
1374 hard_alt: Some(castle_alt),
1375 };
1376
1377 site.tiles.set(
1378 Vec2::new(aabr.min.x + offset - 2, aabr.min.y),
1379 tower_parapet.clone(),
1380 );
1381 site.tiles
1382 .set(Vec2::new(aabr.min.x + offset - 1, aabr.min.y), gate.clone());
1383 site.tiles
1384 .set(Vec2::new(aabr.min.x + offset, aabr.min.y), gate.clone());
1385 site.tiles
1386 .set(Vec2::new(aabr.min.x + offset + 1, aabr.min.y), gate.clone());
1387 site.tiles.set(
1388 Vec2::new(aabr.min.x + offset + 2, aabr.min.y),
1389 tower_parapet.clone(),
1390 );
1391
1392 site.tiles
1393 .set(Vec2::new(aabr.min.x, aabr.min.y), tower_parapet.clone());
1394 site.tiles
1395 .set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower_parapet.clone());
1396 site.tiles
1397 .set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower_parapet.clone());
1398 site.tiles.set(
1399 Vec2::new(aabr.max.x - 1, aabr.max.y - 1),
1400 tower_parapet.clone(),
1401 );
1402
1403 site.blit_aabr(
1405 Aabr {
1406 min: aabr.min + 1,
1407 max: aabr.max - 1,
1408 },
1409 Tile {
1410 kind: TileKind::Road { a: 0, b: 0, w: 0 },
1411 plot: Some(plot),
1412 hard_alt: Some(castle_alt),
1413 },
1414 );
1415
1416 site.blit_aabr(
1418 Aabr {
1419 min: aabr.center() - 3,
1420 max: aabr.center() + 3,
1421 },
1422 Tile {
1423 kind: TileKind::Wall(Dir::Y),
1424 plot: Some(plot),
1425 hard_alt: Some(castle_alt),
1426 },
1427 );
1428 site.tiles.set(
1429 Vec2::new(aabr.center().x + 2, aabr.center().y + 2),
1430 tower_pyramid.clone(),
1431 );
1432 site.tiles.set(
1433 Vec2::new(aabr.center().x + 2, aabr.center().y - 3),
1434 tower_pyramid.clone(),
1435 );
1436 site.tiles.set(
1437 Vec2::new(aabr.center().x - 3, aabr.center().y + 2),
1438 tower_pyramid.clone(),
1439 );
1440 site.tiles.set(
1441 Vec2::new(aabr.center().x - 3, aabr.center().y - 3),
1442 tower_pyramid.clone(),
1443 );
1444
1445 site.blit_aabr(
1446 Aabr {
1447 min: aabr.center() - 2,
1448 max: aabr.center() + 2,
1449 },
1450 Tile {
1451 kind: TileKind::Keep(KeepKind::Middle),
1452 plot: Some(plot),
1453 hard_alt: Some(castle_alt),
1454 },
1455 );
1456
1457 castles += 1;
1458 generator_stats.success(&site.name, GenStatPlotKind::Castle);
1459 }
1460 },
1461 6 if (size > 0.125 && airship_docks == 0) => {
1463 generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
1464 let size = 3.0 as u32;
1468 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1469 site.find_roadside_aabr(
1470 &mut rng,
1471 4..(size + 1).pow(2),
1472 Extent2::broadcast(size),
1473 )
1474 }) {
1475 let airship_dock = plot::AirshipDock::generate(
1476 land,
1477 &mut reseed(&mut rng),
1478 &site,
1479 door_tile,
1480 door_dir,
1481 aabr,
1482 alt,
1483 );
1484 let airship_dock_alt = airship_dock.alt;
1485 let plot = site.create_plot(Plot {
1486 kind: PlotKind::AirshipDock(airship_dock),
1487 root_tile: aabr.center(),
1488 tiles: aabr_tiles(aabr).collect(),
1489 });
1490
1491 site.blit_aabr(aabr, Tile {
1492 kind: TileKind::Building,
1493 plot: Some(plot),
1494 hard_alt: Some(airship_dock_alt),
1495 });
1496 airship_docks += 1;
1497 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
1498 } else {
1499 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1500 }
1501 },
1503 7 if (size > 0.125 && taverns < 2) => {
1504 generator_stats.attempt(&site.name, GenStatPlotKind::Tavern);
1505 let size = (4.5 + rng.gen::<f32>().powf(5.0) * 2.0).round() as u32;
1506 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1507 site.find_roadside_aabr(
1508 &mut rng,
1509 8..(size + 1).pow(2),
1510 Extent2::broadcast(size),
1511 )
1512 }) {
1513 let tavern = plot::Tavern::generate(
1514 land,
1515 index,
1516 &mut reseed(&mut rng),
1517 &site,
1518 door_tile,
1519 Dir::from_vec2(door_dir),
1520 aabr,
1521 alt,
1522 );
1523 let tavern_alt = tavern.door_wpos.z;
1524 let plot = site.create_plot(Plot {
1525 kind: PlotKind::Tavern(tavern),
1526 root_tile: aabr.center(),
1527 tiles: aabr_tiles(aabr).collect(),
1528 });
1529
1530 site.blit_aabr(aabr, Tile {
1531 kind: TileKind::Building,
1532 plot: Some(plot),
1533 hard_alt: Some(tavern_alt),
1534 });
1535
1536 taverns += 1;
1537 generator_stats.success(&site.name, GenStatPlotKind::Tavern);
1538 } else {
1539 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1540 }
1541 },
1542 8 => {
1543 Self::generate_barn(false, &mut rng, &mut site, land);
1544 },
1545 _ => {},
1546 }
1547 }
1548
1549 site
1550 }
1551
1552 pub fn generate_glider_course(
1553 land: &Land,
1554 _index: IndexRef,
1555 rng: &mut impl Rng,
1556 origin: Vec2<i32>,
1557 ) -> Self {
1558 let mut rng = reseed(rng);
1559 let mut site = Site {
1560 origin,
1561 kind: Some(SiteKind::GliderCourse),
1562 ..Site::default()
1563 };
1564
1565 site.name = NameGen::location(&mut rng).generate_town() + " Glider Course";
1568
1569 let origin_alt = land.get_alt_approx(origin);
1572 let alt_drops: Vec<f32> = CARDINALS
1573 .iter()
1574 .map(|c| {
1575 origin_alt
1576 - 0.5
1577 * (land.get_alt_approx(origin + *c * TerrainChunkSize::RECT_SIZE.x as i32)
1578 + land.get_alt_approx(
1579 origin + 2 * *c * TerrainChunkSize::RECT_SIZE.x as i32,
1580 ))
1581 })
1582 .collect();
1583 let mut cardinal = 0;
1584 let mut max_drop = 0.0;
1585 for (i, drop) in alt_drops.iter().enumerate() {
1586 if *drop > max_drop {
1587 max_drop = *drop;
1588 cardinal = i;
1589 }
1590 }
1591 let dir = match cardinal {
1592 0 => Dir::X,
1593 1 => Dir::Y,
1594 2 => Dir::NegX,
1595 3 => Dir::NegY,
1596 _ => Dir::X,
1597 };
1598 let size = 2.0;
1599
1600 let mut valid_course = true;
1601 let mut positions = Vec::new();
1602
1603 let pos = origin;
1605 let tile_pos: Vec2<i32> = Vec2::zero();
1606 positions.push((pos, tile_pos));
1607
1608 const CHUNK_OFFSET: usize = 5;
1612 let offset = CHUNK_OFFSET as i32 * TerrainChunkSize::RECT_SIZE.x as i32;
1614 let tile_offset = offset / TILE_SIZE as i32;
1617 let pos_offset = tile_offset * TILE_SIZE as i32;
1618
1619 let pos = origin + pos_offset * dir.to_vec2();
1621 let tile_pos = tile_offset * dir.to_vec2();
1622 positions.push((pos, tile_pos));
1623
1624 let mut last_pos = pos;
1628 let mut last_tile_pos = tile_pos;
1629 for j in 1..(CHUNK_OFFSET * 9 + 1) {
1630 let c_downhill = land.get_chunk_wpos(last_pos).and_then(|c| c.downhill);
1631 if let Some(downhill) = c_downhill {
1632 let downhill_chunk =
1633 downhill.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / (sz as i32));
1634 let downhill_chunk_pos = TerrainChunkSize::center_wpos(downhill_chunk);
1635 let downhill_vec = downhill_chunk_pos - last_pos;
1636 let tile_offset = downhill_vec / (TILE_SIZE as i32);
1639 let pos_offset = tile_offset * TILE_SIZE as i32;
1640 let pos = last_pos + pos_offset;
1641 let tile_pos = last_tile_pos + tile_offset;
1642 last_pos = pos;
1643 last_tile_pos = tile_pos;
1644 if j % CHUNK_OFFSET == 0 {
1647 positions.push((pos, tile_pos));
1648 }
1649 } else {
1650 valid_course = false;
1651 }
1652 }
1653 if valid_course && positions.len() > 1 {
1660 for (i, window) in positions.windows(2).enumerate() {
1661 if !window.is_empty() {
1662 let [(pos, tile_pos), (next_pos, next_tile_pos)] = window else {
1663 panic!(
1664 "previous condition required positions Vec to have at least two \
1665 elements"
1666 );
1667 };
1668 if i == 0 {
1669 let aabr = Aabr {
1671 min: Vec2::broadcast(-size as i32),
1672 max: Vec2::broadcast(size as i32),
1673 };
1674 let glider_platform = plot::GliderPlatform::generate(
1675 land,
1676 &mut reseed(&mut rng),
1677 &site,
1678 *pos,
1679 dir,
1680 );
1681 let alt = glider_platform.alt - 5;
1682 let plot = site.create_plot(Plot {
1683 kind: PlotKind::GliderPlatform(glider_platform),
1684 root_tile: aabr.center(),
1685 tiles: aabr_tiles(aabr).collect(),
1686 });
1687 site.blit_aabr(aabr, Tile {
1688 kind: TileKind::Building,
1689 plot: Some(plot),
1690 hard_alt: Some(alt),
1691 });
1692 } else if i < 9 {
1693 let dir = if i > 1 {
1696 Dir::from_vec2(next_pos - pos)
1697 } else {
1698 dir
1699 };
1700 let aabr = Aabr {
1701 min: Vec2::broadcast(-size as i32) + tile_pos,
1702 max: Vec2::broadcast(size as i32) + tile_pos,
1703 };
1704 let glider_ring = plot::GliderRing::generate(
1705 land,
1706 &mut reseed(&mut rng),
1707 &site,
1708 pos,
1709 i,
1710 dir,
1711 );
1712 let plot = site.create_plot(Plot {
1713 kind: PlotKind::GliderRing(glider_ring),
1714 root_tile: aabr.center(),
1715 tiles: aabr_tiles(aabr).collect(),
1716 });
1717 site.blit_aabr(aabr, Tile {
1718 kind: TileKind::Building,
1719 plot: Some(plot),
1720 hard_alt: None,
1721 });
1722 } else if i == 9 {
1723 let dir = Dir::from_vec2(next_pos - pos);
1727 let aabr = Aabr {
1728 min: Vec2::broadcast(-size as i32) + tile_pos,
1729 max: Vec2::broadcast(size as i32) + tile_pos,
1730 };
1731 let glider_ring = plot::GliderRing::generate(
1732 land,
1733 &mut reseed(&mut rng),
1734 &site,
1735 pos,
1736 i,
1737 dir,
1738 );
1739 let plot = site.create_plot(Plot {
1740 kind: PlotKind::GliderRing(glider_ring),
1741 root_tile: aabr.center(),
1742 tiles: aabr_tiles(aabr).collect(),
1743 });
1744 site.blit_aabr(aabr, Tile {
1745 kind: TileKind::Building,
1746 plot: Some(plot),
1747 hard_alt: None,
1748 });
1749 let size = 10.0;
1751 let aabr = Aabr {
1752 min: Vec2::broadcast(-size as i32) + next_tile_pos,
1753 max: Vec2::broadcast(size as i32) + next_tile_pos,
1754 };
1755 let glider_finish = plot::GliderFinish::generate(
1756 land,
1757 &mut reseed(&mut rng),
1758 &site,
1759 *next_pos,
1760 );
1761 let plot = site.create_plot(Plot {
1762 kind: PlotKind::GliderFinish(glider_finish),
1763 root_tile: aabr.center(),
1764 tiles: aabr_tiles(aabr).collect(),
1765 });
1766 site.blit_aabr(aabr, Tile {
1767 kind: TileKind::Building,
1768 plot: Some(plot),
1769 hard_alt: None,
1770 });
1771 }
1772 }
1773 }
1774 }
1775
1776 site
1777 }
1778
1779 pub fn generate_cliff_town(
1780 land: &Land,
1781 index: IndexRef,
1782 rng: &mut impl Rng,
1783 origin: Vec2<i32>,
1784 generator_stats: &mut SitesGenMeta,
1785 ) -> Self {
1786 let mut rng = reseed(rng);
1787 let name = NameGen::location(&mut rng).generate_arabic();
1788 let mut site = Site {
1789 origin,
1790 name: name.clone(),
1791 kind: Some(SiteKind::CliffTown),
1792 ..Site::default()
1793 };
1794 let mut campfires = 0;
1795 let road_kind = plot::RoadKind {
1796 lights: plot::RoadLights::Default,
1797 material: plot::RoadMaterial::Sandstone,
1798 };
1799
1800 generator_stats.add(&site.name, GenStatSiteKind::CliffTown);
1802 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
1803
1804 let build_chance = Lottery::from(vec![(30.0, 1), (50.0, 2)]);
1805 let mut airship_docks = 0;
1806 for _ in 0..80 {
1807 match *build_chance.choose_seeded(rng.gen()) {
1808 1 => {
1809 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
1811 generator_stats.attempt(&site.name, GenStatPlotKind::House);
1812 let campfire = campfires < 4;
1813 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1814 site.find_roadside_aabr(
1815 &mut rng,
1816 8..(size + 1).pow(2),
1817 Extent2::broadcast(size),
1818 )
1819 }) {
1820 let cliff_tower = plot::CliffTower::generate(
1821 land,
1822 index,
1823 &mut reseed(&mut rng),
1824 &site,
1825 door_tile,
1826 door_dir,
1827 aabr,
1828 campfire,
1829 alt,
1830 );
1831 let cliff_tower_alt = cliff_tower.alt;
1832 let plot = site.create_plot(Plot {
1833 kind: PlotKind::CliffTower(cliff_tower),
1834 root_tile: aabr.center(),
1835 tiles: aabr_tiles(aabr).collect(),
1836 });
1837 site.blit_aabr(aabr, Tile {
1838 kind: TileKind::Building,
1839 plot: Some(plot),
1840 hard_alt: Some(cliff_tower_alt),
1841 });
1842 campfires += 1;
1843 generator_stats.success(&site.name, GenStatPlotKind::House);
1844 } else {
1845 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
1846 }
1847 },
1848 2 if airship_docks < 1 => {
1849 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.0).round() as u32;
1851 generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
1852 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
1853 site.find_roadside_aabr(
1854 &mut rng,
1855 8..(size + 1).pow(2),
1856 Extent2::broadcast(size),
1857 )
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 = (6.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2006 generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
2007 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2008 site.find_roadside_aabr(
2009 &mut rng,
2010 4..(size + 1).pow(2),
2011 Extent2::broadcast(size),
2012 )
2013 }) {
2014 let savannah_airship_dock = plot::SavannahAirshipDock::generate(
2015 land,
2016 &mut reseed(&mut rng),
2017 &site,
2018 door_tile,
2019 door_dir,
2020 aabr,
2021 alt,
2022 );
2023 let savannah_airship_dock_alt = savannah_airship_dock.alt;
2024 let plot = site.create_plot(Plot {
2025 kind: PlotKind::SavannahAirshipDock(savannah_airship_dock),
2026 root_tile: aabr.center(),
2027 tiles: aabr_tiles(aabr).collect(),
2028 });
2029
2030 site.blit_aabr(aabr, Tile {
2031 kind: TileKind::Building,
2032 plot: Some(plot),
2033 hard_alt: Some(savannah_airship_dock_alt),
2034 });
2035 airship_dock += 1;
2036 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
2037 } else {
2038 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2039 }
2040 },
2041 4 => {
2043 Self::generate_farm(false, &mut rng, &mut site, land);
2044 },
2045 5 => {
2046 Self::generate_barn(false, &mut rng, &mut site, land);
2047 },
2048 _ => {},
2049 }
2050 }
2051 site
2052 }
2053
2054 pub fn generate_coastal_town(
2055 land: &Land,
2056 index: IndexRef,
2057 rng: &mut impl Rng,
2058 origin: Vec2<i32>,
2059 generator_stats: &mut SitesGenMeta,
2060 ) -> Self {
2061 let mut rng = reseed(rng);
2062 let name = NameGen::location(&mut rng).generate_danari();
2063 let mut site = Site {
2064 origin,
2065 name: name.clone(),
2066 kind: Some(SiteKind::CoastalTown),
2067 ..Site::default()
2068 };
2069 let road_kind = plot::RoadKind {
2070 lights: plot::RoadLights::Default,
2071 material: plot::RoadMaterial::Marble,
2072 };
2073
2074 site.demarcate_obstacles(land);
2076 generator_stats.add(&site.name, GenStatSiteKind::CoastalTown);
2077 site.make_initial_plaza_default(land, index, &mut rng, generator_stats, &name, road_kind);
2078
2079 let mut workshops = 0;
2080 let build_chance = Lottery::from(vec![(38.0, 1), (5.0, 2), (15.0, 3), (15.0, 4), (5.0, 5)]);
2081 let mut airship_docks = 0;
2082 for _ in 0..55 {
2083 match *build_chance.choose_seeded(rng.gen()) {
2084 n if (n == 2 && workshops < 3) || workshops == 0 => {
2085 let size = (7.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2087 generator_stats.attempt(&site.name, GenStatPlotKind::Workshop);
2088 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2089 site.find_roadside_aabr(
2090 &mut rng,
2091 7..(size + 1).pow(2),
2092 Extent2::broadcast(size),
2093 )
2094 }) {
2095 let coastal_workshop = plot::CoastalWorkshop::generate(
2096 land,
2097 &mut reseed(&mut rng),
2098 &site,
2099 door_tile,
2100 door_dir,
2101 aabr,
2102 alt,
2103 );
2104 let coastal_workshop_alt = coastal_workshop.alt;
2105 let plot = site.create_plot(Plot {
2106 kind: PlotKind::CoastalWorkshop(coastal_workshop),
2107 root_tile: aabr.center(),
2108 tiles: aabr_tiles(aabr).collect(),
2109 });
2110
2111 site.blit_aabr(aabr, Tile {
2112 kind: TileKind::Building,
2113 plot: Some(plot),
2114 hard_alt: Some(coastal_workshop_alt),
2115 });
2116 workshops += 1;
2117 generator_stats.success(&site.name, GenStatPlotKind::Workshop);
2118 } else {
2119 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2120 }
2121 },
2122 1 => {
2123 let size = (7.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2126 generator_stats.attempt(&site.name, GenStatPlotKind::House);
2127 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2128 site.find_roadside_aabr(
2129 &mut rng,
2130 7..(size + 1).pow(2),
2131 Extent2::broadcast(size),
2132 )
2133 }) {
2134 let coastal_house = plot::CoastalHouse::generate(
2135 land,
2136 &mut reseed(&mut rng),
2137 &site,
2138 door_tile,
2139 door_dir,
2140 aabr,
2141 alt,
2142 );
2143 let coastal_house_alt = coastal_house.alt;
2144 let plot = site.create_plot(Plot {
2145 kind: PlotKind::CoastalHouse(coastal_house),
2146 root_tile: aabr.center(),
2147 tiles: aabr_tiles(aabr).collect(),
2148 });
2149
2150 site.blit_aabr(aabr, Tile {
2151 kind: TileKind::Building,
2152 plot: Some(plot),
2153 hard_alt: Some(coastal_house_alt),
2154 });
2155
2156 generator_stats.success(&site.name, GenStatPlotKind::House);
2157 } else {
2158 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2159 }
2160 },
2161 3 if airship_docks < 1 => {
2162 let size = (7.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
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(
2167 &mut rng,
2168 7..(size + 1).pow(2),
2169 Extent2::broadcast(size),
2170 )
2171 }) {
2172 let coastal_airship_dock = plot::CoastalAirshipDock::generate(
2173 land,
2174 &mut reseed(&mut rng),
2175 &site,
2176 door_tile,
2177 door_dir,
2178 aabr,
2179 alt,
2180 );
2181 let coastal_airship_dock_alt = coastal_airship_dock.alt;
2182 let plot = site.create_plot(Plot {
2183 kind: PlotKind::CoastalAirshipDock(coastal_airship_dock),
2184 root_tile: aabr.center(),
2185 tiles: aabr_tiles(aabr).collect(),
2186 });
2187
2188 site.blit_aabr(aabr, Tile {
2189 kind: TileKind::Building,
2190 plot: Some(plot),
2191 hard_alt: Some(coastal_airship_dock_alt),
2192 });
2193 airship_docks += 1;
2194 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
2195 } else {
2196 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2197 }
2198 },
2199 4 => {
2201 Self::generate_farm(false, &mut rng, &mut site, land);
2202 },
2203 5 => {
2204 Self::generate_barn(false, &mut rng, &mut site, land);
2205 },
2206 _ => {},
2207 }
2208 }
2209 site
2210 }
2211
2212 pub fn generate_desert_city(
2213 land: &Land,
2214 index: IndexRef,
2215 rng: &mut impl Rng,
2216 origin: Vec2<i32>,
2217 generator_stats: &mut SitesGenMeta,
2218 ) -> Self {
2219 let mut rng = reseed(rng);
2220
2221 let name = NameGen::location(&mut rng).generate_arabic();
2222 let mut site = Site {
2223 origin,
2224 name: name.clone(),
2225 kind: Some(SiteKind::DesertCity),
2226 ..Site::default()
2227 };
2228 let road_kind = plot::RoadKind {
2229 lights: plot::RoadLights::Default,
2230 material: plot::RoadMaterial::Sandstone,
2231 };
2232
2233 site.demarcate_obstacles(land);
2235 const DESERT_CITY_PLAZA_RADIUS: u32 = 3;
2238 const DESERT_CITY_PLAZA_SEARCH_INNER: u32 = 19;
2239 const DESERT_CITY_PLAZA_SEARCH_WIDTH: u32 = 12;
2240 generator_stats.add(&site.name, GenStatSiteKind::DesertCity);
2241 site.make_initial_plaza(
2242 land,
2243 index,
2244 &mut rng,
2245 DESERT_CITY_PLAZA_RADIUS,
2246 DESERT_CITY_PLAZA_SEARCH_INNER,
2247 DESERT_CITY_PLAZA_SEARCH_WIDTH,
2248 generator_stats,
2249 &name,
2250 road_kind,
2251 );
2252
2253 let size = 17.0 as i32;
2254 let aabr = Aabr {
2255 min: Vec2::broadcast(-size),
2256 max: Vec2::broadcast(size),
2257 };
2258
2259 let desert_city_arena =
2260 plot::DesertCityArena::generate(land, &mut reseed(&mut rng), &site, aabr);
2261
2262 let desert_city_arena_alt = desert_city_arena.alt;
2263 let plot = site.create_plot(Plot {
2264 kind: PlotKind::DesertCityArena(desert_city_arena),
2265 root_tile: aabr.center(),
2266 tiles: aabr_tiles(aabr).collect(),
2267 });
2268
2269 site.blit_aabr(aabr, Tile {
2270 kind: TileKind::Building,
2271 plot: Some(plot),
2272 hard_alt: Some(desert_city_arena_alt),
2273 });
2274
2275 let build_chance = Lottery::from(vec![(20.0, 1), (10.0, 2), (5.0, 3), (10.0, 4), (0.0, 5)]);
2276
2277 let mut temples = 0;
2278 let mut airship_docks = 0;
2279 let mut campfires = 0;
2280
2281 for _ in 0..35 {
2282 match *build_chance.choose_seeded(rng.gen()) {
2283 1 => {
2285 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2286 generator_stats.attempt(&site.name, GenStatPlotKind::MultiPlot);
2287 let campfire = campfires < 4;
2288 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2289 site.find_roadside_aabr(
2290 &mut rng,
2291 8..(size + 1).pow(2),
2292 Extent2::broadcast(size),
2293 )
2294 }) {
2295 let desert_city_multi_plot = plot::DesertCityMultiPlot::generate(
2296 land,
2297 &mut reseed(&mut rng),
2298 &site,
2299 door_tile,
2300 door_dir,
2301 aabr,
2302 campfire,
2303 alt,
2304 );
2305 let desert_city_multi_plot_alt = desert_city_multi_plot.alt;
2306 let plot = site.create_plot(Plot {
2307 kind: PlotKind::DesertCityMultiPlot(desert_city_multi_plot),
2308 root_tile: aabr.center(),
2309 tiles: aabr_tiles(aabr).collect(),
2310 });
2311
2312 site.blit_aabr(aabr, Tile {
2313 kind: TileKind::Building,
2314 plot: Some(plot),
2315 hard_alt: Some(desert_city_multi_plot_alt),
2316 });
2317 campfires += 1;
2318 generator_stats.success(&site.name, GenStatPlotKind::MultiPlot);
2319 } else {
2320 site.make_plaza(land, index, &mut rng, generator_stats, &name, road_kind);
2321 }
2322 },
2323 2 if temples < 1 => {
2325 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2326 generator_stats.attempt(&site.name, GenStatPlotKind::Temple);
2327 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2328 site.find_roadside_aabr(
2329 &mut rng,
2330 8..(size + 1).pow(2),
2331 Extent2::broadcast(size),
2332 )
2333 }) {
2334 let desert_city_temple = plot::DesertCityTemple::generate(
2335 land,
2336 &mut reseed(&mut rng),
2337 &site,
2338 door_tile,
2339 door_dir,
2340 aabr,
2341 alt,
2342 );
2343 let desert_city_temple_alt = desert_city_temple.alt;
2344 let plot = site.create_plot(Plot {
2345 kind: PlotKind::DesertCityTemple(desert_city_temple),
2346 root_tile: aabr.center(),
2347 tiles: aabr_tiles(aabr).collect(),
2348 });
2349
2350 site.blit_aabr(aabr, Tile {
2351 kind: TileKind::Building,
2352 plot: Some(plot),
2353 hard_alt: Some(desert_city_temple_alt),
2354 });
2355 temples += 1;
2356 generator_stats.success(&site.name, GenStatPlotKind::Temple);
2357 }
2358 },
2359 3 if airship_docks < 1 => {
2361 let size = (6.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2362 generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
2363 if let Some((aabr, door_tile, door_dir, alt)) = attempt(32, || {
2364 site.find_roadside_aabr(
2365 &mut rng,
2366 8..(size + 1).pow(2),
2367 Extent2::broadcast(size),
2368 )
2369 }) {
2370 let desert_city_airship_dock = plot::DesertCityAirshipDock::generate(
2371 land,
2372 &mut reseed(&mut rng),
2373 &site,
2374 door_tile,
2375 door_dir,
2376 aabr,
2377 alt,
2378 );
2379 let desert_city_airship_dock_alt = desert_city_airship_dock.alt;
2380 let plot = site.create_plot(Plot {
2381 kind: PlotKind::DesertCityAirshipDock(desert_city_airship_dock),
2382 root_tile: aabr.center(),
2383 tiles: aabr_tiles(aabr).collect(),
2384 });
2385
2386 site.blit_aabr(aabr, Tile {
2387 kind: TileKind::Building,
2388 plot: Some(plot),
2389 hard_alt: Some(desert_city_airship_dock_alt),
2390 });
2391 airship_docks += 1;
2392 generator_stats.success(&site.name, GenStatPlotKind::AirshipDock);
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);
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 ) -> bool {
2454 let size = (9.0 + rng.gen::<f32>().powf(5.0) * 1.5).round() as u32;
2455 if let Some((aabr, door_tile, door_dir, _alt)) = attempt(32, || {
2456 site.find_rural_aabr(&mut rng, 9..(size + 1).pow(2), Extent2::broadcast(size))
2457 }) {
2458 let barn = plot::Barn::generate(
2459 land,
2460 &mut reseed(&mut rng),
2461 site,
2462 door_tile,
2463 door_dir,
2464 aabr,
2465 is_desert,
2466 );
2467 let barn_alt = barn.alt;
2468 let plot = site.create_plot(Plot {
2469 kind: PlotKind::Barn(barn),
2470 root_tile: aabr.center(),
2471 tiles: aabr_tiles(aabr).collect(),
2472 });
2473
2474 site.blit_aabr(aabr, Tile {
2475 kind: TileKind::Building,
2476 plot: Some(plot),
2477 hard_alt: Some(barn_alt),
2478 });
2479
2480 true
2481 } else {
2482 false
2483 }
2484 }
2485
2486 pub fn generate_haniwa(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2487 let mut rng = reseed(rng);
2488 let mut site = Site {
2489 origin,
2490 name: format!(
2491 "{} {}",
2492 NameGen::location(&mut rng).generate_haniwa(),
2493 [
2494 "Catacombs",
2495 "Crypt",
2496 "Tomb",
2497 "Gravemound",
2498 "Tunnels",
2499 "Vault",
2500 "Chambers",
2501 "Halls",
2502 "Tumulus",
2503 "Barrow",
2504 ]
2505 .choose(&mut rng)
2506 .unwrap()
2507 ),
2508 kind: Some(SiteKind::Haniwa),
2509 ..Site::default()
2510 };
2511 let size = 24.0 as i32;
2512 let aabr = Aabr {
2513 min: Vec2::broadcast(-size),
2514 max: Vec2::broadcast(size),
2515 };
2516 {
2517 let haniwa = plot::Haniwa::generate(land, &mut reseed(&mut rng), &site, aabr);
2518 let haniwa_alt = haniwa.alt;
2519 let plot = site.create_plot(Plot {
2520 kind: PlotKind::Haniwa(haniwa),
2521 root_tile: aabr.center(),
2522 tiles: aabr_tiles(aabr).collect(),
2523 });
2524
2525 site.blit_aabr(aabr, Tile {
2526 kind: TileKind::Building,
2527 plot: Some(plot),
2528 hard_alt: Some(haniwa_alt),
2529 });
2530 }
2531 site
2532 }
2533
2534 pub fn generate_chapel_site(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2535 let mut rng = reseed(rng);
2536 let mut site = Site {
2537 origin,
2538 name: NameGen::location(&mut rng).generate_danari(),
2539 kind: Some(SiteKind::ChapelSite),
2540 ..Site::default()
2541 };
2542
2543 let size = 10.0 as i32;
2545 let aabr = Aabr {
2546 min: Vec2::broadcast(-size),
2547 max: Vec2::broadcast(size),
2548 };
2549 {
2550 let sea_chapel = plot::SeaChapel::generate(land, &mut reseed(&mut rng), &site, aabr);
2551 let sea_chapel_alt = sea_chapel.alt;
2552 let plot = site.create_plot(Plot {
2553 kind: PlotKind::SeaChapel(sea_chapel),
2554 root_tile: aabr.center(),
2555 tiles: aabr_tiles(aabr).collect(),
2556 });
2557
2558 site.blit_aabr(aabr, Tile {
2559 kind: TileKind::Building,
2560 plot: Some(plot),
2561 hard_alt: Some(sea_chapel_alt),
2562 });
2563 }
2564 site
2565 }
2566
2567 pub fn generate_pirate_hideout(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2568 let mut rng = reseed(rng);
2569 let mut site = Site {
2570 origin,
2571 name: "".to_string(),
2572 kind: Some(SiteKind::PirateHideout),
2573 ..Site::default()
2574 };
2575
2576 let size = 8.0 as i32;
2577 let aabr = Aabr {
2578 min: Vec2::broadcast(-size),
2579 max: Vec2::broadcast(size),
2580 };
2581 {
2582 let pirate_hideout =
2583 plot::PirateHideout::generate(land, &mut reseed(&mut rng), &site, aabr);
2584 let pirate_hideout_alt = pirate_hideout.alt;
2585 let plot = site.create_plot(Plot {
2586 kind: PlotKind::PirateHideout(pirate_hideout),
2587 root_tile: aabr.center(),
2588 tiles: aabr_tiles(aabr).collect(),
2589 });
2590
2591 site.blit_aabr(aabr, Tile {
2592 kind: TileKind::Building,
2593 plot: Some(plot),
2594 hard_alt: Some(pirate_hideout_alt),
2595 });
2596 }
2597 site
2598 }
2599
2600 pub fn generate_jungle_ruin(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2601 let mut rng = reseed(rng);
2602 let mut site = Site {
2603 origin,
2604 name: "".to_string(),
2605 kind: Some(SiteKind::JungleRuin),
2606 ..Site::default()
2607 };
2608 let size = 8.0 as i32;
2609 let aabr = Aabr {
2610 min: Vec2::broadcast(-size),
2611 max: Vec2::broadcast(size),
2612 };
2613 {
2614 let jungle_ruin = plot::JungleRuin::generate(land, &mut reseed(&mut rng), &site, aabr);
2615 let jungle_ruin_alt = jungle_ruin.alt;
2616 let plot = site.create_plot(Plot {
2617 kind: PlotKind::JungleRuin(jungle_ruin),
2618 root_tile: aabr.center(),
2619 tiles: aabr_tiles(aabr).collect(),
2620 });
2621
2622 site.blit_aabr(aabr, Tile {
2623 kind: TileKind::Building,
2624 plot: Some(plot),
2625 hard_alt: Some(jungle_ruin_alt),
2626 });
2627 }
2628 site
2629 }
2630
2631 pub fn generate_rock_circle(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2632 let mut rng = reseed(rng);
2633 let mut site = Site {
2634 origin,
2635 kind: Some(SiteKind::RockCircle),
2636 ..Site::default()
2637 };
2638 let size = 8.0 as i32;
2639 let aabr = Aabr {
2640 min: Vec2::broadcast(-size),
2641 max: Vec2::broadcast(size),
2642 };
2643 {
2644 let rock_circle = plot::RockCircle::generate(land, &mut reseed(&mut rng), &site, aabr);
2645 let rock_circle_alt = rock_circle.alt;
2646 let plot = site.create_plot(Plot {
2647 kind: PlotKind::RockCircle(rock_circle),
2648 root_tile: aabr.center(),
2649 tiles: aabr_tiles(aabr).collect(),
2650 });
2651
2652 site.blit_aabr(aabr, Tile {
2653 kind: TileKind::Building,
2654 plot: Some(plot),
2655 hard_alt: Some(rock_circle_alt),
2656 });
2657 }
2658 site
2659 }
2660
2661 pub fn generate_troll_cave(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2662 let mut rng = reseed(rng);
2663 let mut site = Site {
2664 origin,
2665 name: "".to_string(),
2666 kind: Some(SiteKind::TrollCave),
2667 ..Site::default()
2668 };
2669 let size = 2.0 as i32;
2670 let aabr = Aabr {
2671 min: Vec2::broadcast(-size),
2672 max: Vec2::broadcast(size),
2673 };
2674 let site_temp = temp_at_wpos(land, origin);
2675 {
2676 let troll_cave =
2677 plot::TrollCave::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp);
2678 let troll_cave_alt = troll_cave.alt;
2679 let plot = site.create_plot(Plot {
2680 kind: PlotKind::TrollCave(troll_cave),
2681 root_tile: aabr.center(),
2682 tiles: aabr_tiles(aabr).collect(),
2683 });
2684
2685 site.blit_aabr(aabr, Tile {
2686 kind: TileKind::Building,
2687 plot: Some(plot),
2688 hard_alt: Some(troll_cave_alt),
2689 });
2690 }
2691 site
2692 }
2693
2694 pub fn generate_camp(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2695 let mut rng = reseed(rng);
2696 let mut site = Site {
2697 origin,
2698 name: "".to_string(),
2699 kind: Some(SiteKind::Camp),
2700 ..Site::default()
2701 };
2702 let size = 2.0 as i32;
2703 let aabr = Aabr {
2704 min: Vec2::broadcast(-size),
2705 max: Vec2::broadcast(size),
2706 };
2707 let site_temp = temp_at_wpos(land, origin);
2708 {
2709 let camp = plot::Camp::generate(land, &mut reseed(&mut rng), &site, aabr, site_temp);
2710 let camp_alt = camp.alt;
2711 let plot = site.create_plot(Plot {
2712 kind: PlotKind::Camp(camp),
2713 root_tile: aabr.center(),
2714 tiles: aabr_tiles(aabr).collect(),
2715 });
2716
2717 site.blit_aabr(aabr, Tile {
2718 kind: TileKind::Building,
2719 plot: Some(plot),
2720 hard_alt: Some(camp_alt),
2721 });
2722 }
2723 site
2724 }
2725
2726 pub fn generate_cultist(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2727 let mut rng = reseed(rng);
2728 let mut site = Site {
2729 origin,
2730 name: {
2731 let name = NameGen::location(&mut rng).generate();
2732 match rng.gen_range(0..5) {
2733 0 => format!("{} Dungeon", name),
2734 1 => format!("{} Lair", name),
2735 2 => format!("{} Crib", name),
2736 3 => format!("{} Catacombs", name),
2737 _ => format!("{} Pit", name),
2738 }
2739 },
2740 kind: Some(SiteKind::Cultist),
2741 ..Site::default()
2742 };
2743 let size = 22.0 as i32;
2744 let aabr = Aabr {
2745 min: Vec2::broadcast(-size),
2746 max: Vec2::broadcast(size),
2747 };
2748 {
2749 let cultist = plot::Cultist::generate(land, &mut reseed(&mut rng), &site, aabr);
2750 let cultist_alt = cultist.alt;
2751 let plot = site.create_plot(Plot {
2752 kind: PlotKind::Cultist(cultist),
2753 root_tile: aabr.center(),
2754 tiles: aabr_tiles(aabr).collect(),
2755 });
2756
2757 site.blit_aabr(aabr, Tile {
2758 kind: TileKind::Building,
2759 plot: Some(plot),
2760 hard_alt: Some(cultist_alt),
2761 });
2762 }
2763 site
2764 }
2765
2766 pub fn generate_sahagin(
2767 land: &Land,
2768 index: IndexRef,
2769 rng: &mut impl Rng,
2770 origin: Vec2<i32>,
2771 ) -> Self {
2772 let mut rng = reseed(rng);
2773 let mut site = Site {
2774 origin,
2775 name: {
2776 let name = NameGen::location(&mut rng).generate();
2777 match rng.gen_range(0..5) {
2778 0 => format!("{} Isle", name),
2779 1 => format!("{} Islet", name),
2780 2 => format!("{} Key", name),
2781 3 => format!("{} Cay", name),
2782 _ => format!("{} Rock", name),
2783 }
2784 },
2785 kind: Some(SiteKind::Sahagin),
2786 ..Site::default()
2787 };
2788 let size = 16.0 as i32;
2789 let aabr = Aabr {
2790 min: Vec2::broadcast(-size),
2791 max: Vec2::broadcast(size),
2792 };
2793 {
2794 let sahagin = plot::Sahagin::generate(land, index, &mut reseed(&mut rng), &site, aabr);
2795 let sahagin_alt = sahagin.alt;
2796 let plot = site.create_plot(Plot {
2797 kind: PlotKind::Sahagin(sahagin),
2798 root_tile: aabr.center(),
2799 tiles: aabr_tiles(aabr).collect(),
2800 });
2801
2802 site.blit_aabr(aabr, Tile {
2803 kind: TileKind::Building,
2804 plot: Some(plot),
2805 hard_alt: Some(sahagin_alt),
2806 });
2807 }
2808 site
2809 }
2810
2811 pub fn generate_vampire_castle(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
2812 let mut rng = reseed(rng);
2813 let mut site = Site {
2814 origin,
2815 name: {
2816 let name = NameGen::location(&mut rng).generate_vampire();
2817 match rng.gen_range(0..4) {
2818 0 => format!("{} Keep", name),
2819 1 => format!("{} Chateau", name),
2820 2 => format!("{} Manor", name),
2821 _ => format!("{} Palace", name),
2822 }
2823 },
2824 kind: Some(SiteKind::VampireCastle),
2825 ..Site::default()
2826 };
2827 let size = 22.0 as i32;
2828 let aabr = Aabr {
2829 min: Vec2::broadcast(-size),
2830 max: Vec2::broadcast(size),
2831 };
2832 {
2833 let vampire_castle =
2834 plot::VampireCastle::generate(land, &mut reseed(&mut rng), &site, aabr);
2835 let vampire_castle_alt = vampire_castle.alt;
2836 let plot = site.create_plot(Plot {
2837 kind: PlotKind::VampireCastle(vampire_castle),
2838 root_tile: aabr.center(),
2839 tiles: aabr_tiles(aabr).collect(),
2840 });
2841
2842 site.blit_aabr(aabr, Tile {
2843 kind: TileKind::Building,
2844 plot: Some(plot),
2845 hard_alt: Some(vampire_castle_alt),
2846 });
2847 }
2848 site
2849 }
2850
2851 pub fn generate_bridge(
2852 land: &Land,
2853 index: IndexRef,
2854 rng: &mut impl Rng,
2855 start_chunk: Vec2<i32>,
2856 end_chunk: Vec2<i32>,
2857 ) -> Self {
2858 let mut rng = reseed(rng);
2859 let start = TerrainChunkSize::center_wpos(start_chunk);
2860 let end = TerrainChunkSize::center_wpos(end_chunk);
2861 let origin = (start + end) / 2;
2862
2863 let mut site = Site {
2864 origin,
2865 name: format!("Bridge of {}", NameGen::location(&mut rng).generate_town()),
2866 kind: Some(SiteKind::Bridge(start_chunk, end_chunk)),
2867 ..Site::default()
2868 };
2869
2870 let start_tile = site.wpos_tile_pos(start);
2871 let end_tile = site.wpos_tile_pos(end);
2872
2873 let width = 1;
2874
2875 let orth = (start_tile - end_tile).yx().map(|dir| dir.signum().abs());
2876
2877 let start_aabr = Aabr {
2878 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
2879 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
2880 };
2881
2882 let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
2883
2884 let start_tile = site.wpos_tile_pos(bridge.start.xy());
2885 let end_tile = site.wpos_tile_pos(bridge.end.xy());
2886
2887 let width = (bridge.width() + TILE_SIZE as i32 / 2) / TILE_SIZE as i32;
2888 let aabr = Aabr {
2889 min: start_tile.map2(end_tile, |a, b| a.min(b)) - orth * width,
2890 max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
2891 };
2892
2893 let line = LineSegment2 {
2894 start: site.tile_wpos(bridge.dir.select_aabr_with(start_aabr, start_aabr.center())),
2895 end: site.tile_wpos(
2896 bridge
2897 .dir
2898 .opposite()
2899 .select_aabr_with(start_aabr, start_aabr.center()),
2900 ),
2901 }
2902 .as_();
2903
2904 for y in start_aabr.min.y..start_aabr.max.y {
2905 for x in start_aabr.min.x..start_aabr.max.x {
2906 let tpos = Vec2::new(x, y);
2907 let tile_aabr = Aabr {
2908 min: site.tile_wpos(tpos),
2909 max: site.tile_wpos(tpos + 1) - 1,
2910 };
2911 if let Some(tile) = site.tiles.get_mut(tpos) {
2912 let closest_point = line.projected_point(tile_aabr.center().as_());
2913 let w = TILE_SIZE as f32;
2914 if tile_aabr
2915 .as_()
2916 .projected_point(closest_point)
2917 .distance_squared(closest_point)
2918 < w.powi(2)
2919 {
2920 tile.kind = TileKind::Path {
2921 closest_pos: closest_point,
2922 path: Path { width: w },
2923 };
2924 }
2925 }
2926 }
2927 }
2928
2929 let plot = site.create_plot(Plot {
2930 kind: PlotKind::Bridge(bridge),
2931 root_tile: start_tile,
2932 tiles: aabr_tiles(aabr).collect(),
2933 });
2934
2935 site.blit_aabr(aabr, Tile {
2936 kind: TileKind::Bridge,
2937 plot: Some(plot),
2938 hard_alt: None,
2939 });
2940
2941 site
2942 }
2943
2944 pub fn wpos_tile_pos(&self, wpos2d: Vec2<i32>) -> Vec2<i32> {
2945 (wpos2d - self.origin).map(|e| e.div_euclid(TILE_SIZE as i32))
2946 }
2947
2948 pub fn wpos_tile(&self, wpos2d: Vec2<i32>) -> &Tile {
2949 self.tiles.get(self.wpos_tile_pos(wpos2d))
2950 }
2951
2952 pub fn tile_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> { self.origin + tile * TILE_SIZE as i32 }
2953
2954 pub fn tile_center_wpos(&self, tile: Vec2<i32>) -> Vec2<i32> {
2955 self.origin + tile * TILE_SIZE as i32 + TILE_SIZE as i32 / 2
2956 }
2957
2958 pub fn render_tile(&self, canvas: &mut Canvas, tpos: Vec2<i32>) {
2959 let tile = self.tiles.get(tpos);
2960 let twpos = self.tile_wpos(tpos);
2961 let border = TILE_SIZE as i32;
2962 let cols = (-border..TILE_SIZE as i32 + border).flat_map(|y| {
2963 (-border..TILE_SIZE as i32 + border)
2964 .map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))
2965 });
2966 if let TileKind::Path { closest_pos, path } = &tile.kind {
2967 let near_connections = CARDINALS.iter().filter_map(|rpos| {
2968 let npos = tpos + rpos;
2969 let tile = self.tiles.get(npos);
2970 let tile_aabr = Aabr {
2971 min: self.tile_wpos(tpos).map(|e| e as f32),
2972 max: self.tile_wpos(tpos + 1).map(|e| e as f32) - 1.0,
2973 };
2974 match tile.kind {
2975 TileKind::Road { a, b, w } => {
2976 if let Some(PlotKind::Road(road)) = tile.plot.map(|p| &self.plot(p).kind) {
2977 let start = road.path.nodes[a as usize];
2978 let end = road.path.nodes[b as usize];
2979 let dir = Dir::from_vec2(end - start);
2980 let orth = dir.orthogonal();
2981 let aabr = Aabr {
2982 min: self.tile_center_wpos(start)
2983 - w as i32 * 2 * orth.to_vec2()
2984 - dir.to_vec2() * TILE_SIZE as i32 / 2,
2985 max: self.tile_center_wpos(end)
2986 + w as i32 * 2 * orth.to_vec2()
2987 + dir.to_vec2() * TILE_SIZE as i32 / 2,
2988 }
2989 .made_valid()
2990 .as_();
2991 Some(aabr)
2992 } else {
2993 None
2994 }
2995 },
2996 TileKind::Bridge | TileKind::Plaza => Some(tile_aabr),
2997 _ => tile
2998 .plot
2999 .and_then(|plot| self.plot(plot).kind().meta())
3000 .and_then(|meta| meta.door_tile())
3001 .is_some_and(|door_tile| door_tile == npos)
3002 .then_some(tile_aabr),
3003 }
3004 });
3005 cols.for_each(|(wpos2d, _offs)| {
3006 let wpos2df = wpos2d.map(|e| e as f32);
3007
3008 if closest_pos.distance_squared(wpos2d.as_()) < path.width.powi(2)
3009 || near_connections
3010 .clone()
3011 .map(|aabr| aabr.distance_to_point(wpos2df))
3012 .min_by_key(|d| (*d * 100.0) as i32)
3013 .is_some_and(|d| d <= 1.5)
3014 {
3015 let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
3016 let sub_surface_color = canvas
3017 .col(wpos2d)
3018 .map_or(Rgb::zero(), |col| col.sub_surface_color);
3019 for z in -8..6 {
3020 let wpos = Vec3::new(wpos2d.x, wpos2d.y, alt + z);
3021 canvas.map(wpos, |b| {
3022 if b.kind() == BlockKind::Snow {
3023 b.into_vacant()
3024 } else if b.is_filled() {
3025 if b.is_terrain() {
3026 Block::new(
3027 BlockKind::Earth,
3028 path.surface_color((sub_surface_color * 255.0).as_(), wpos),
3029 )
3030 } else {
3031 b
3032 }
3033 } else {
3034 b.into_vacant()
3035 }
3036 })
3037 }
3038 }
3039 });
3040 }
3041 }
3042
3043 pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
3044 let tile_aabr = Aabr {
3045 min: self.wpos_tile_pos(canvas.wpos()) - 1,
3046 max: self
3047 .wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2)
3048 + 3, };
3050
3051 let mut plots = DHashSet::default();
3053
3054 for y in tile_aabr.min.y..tile_aabr.max.y {
3055 for x in tile_aabr.min.x..tile_aabr.max.x {
3056 self.render_tile(canvas, Vec2::new(x, y));
3057
3058 if let Some(plot) = self.tiles.get(Vec2::new(x, y)).plot {
3059 plots.insert(plot);
3060 }
3061 }
3062 }
3063
3064 canvas.foreach_col(|canvas, wpos2d, col| {
3065 let tile = self.wpos_tile(wpos2d);
3066 for z_off in (-2..4).rev() {
3067 if let Some(plot) = tile.plot.map(|p| &self.plots[p]) {
3068 canvas.map_resource(
3069 Vec3::new(
3070 wpos2d.x,
3071 wpos2d.y,
3072 foreach_plot!(&plot.kind, plot => plot.rel_terrain_offset(col)) + z_off,
3073 ),
3074 |block| {
3075 foreach_plot!(
3076 &plot.kind,
3077 plot => plot.terrain_surface_at(
3078 wpos2d,
3079 block,
3080 dynamic_rng,
3081 col,
3082 z_off,
3083 self,
3084 ).unwrap_or(block),
3085 )
3086 },
3087 );
3088 }
3089 }
3090 });
3091
3092 for (id, plot) in self.plots.iter() {
3094 if matches!(&plot.kind, PlotKind::GiantTree(_)) {
3095 plots.insert(id);
3096 }
3097 }
3098
3099 let mut plots_to_render = plots.into_iter().collect::<Vec<_>>();
3100 plots_to_render
3102 .sort_unstable_by_key(|plot| (self.plots[*plot].kind.render_ordering(), *plot));
3103
3104 let wpos2d = canvas.info().wpos();
3105 let chunk_aabr = Aabr {
3106 min: wpos2d,
3107 max: wpos2d + TerrainChunkSize::RECT_SIZE.as_::<i32>(),
3108 };
3109
3110 let info = canvas.info();
3111
3112 for plot in plots_to_render {
3113 let (prim_tree, fills, mut entities) =
3114 foreach_plot!(&self.plots[plot].kind, plot => plot.render_collect(self, canvas));
3115
3116 let mut spawn = |pos, last_block| {
3117 if let Some(entity) = match &self.plots[plot].kind {
3118 PlotKind::GiantTree(tree) => tree.entity_at(pos, &last_block, dynamic_rng),
3119 _ => None,
3120 } {
3121 entities.push(entity);
3122 }
3123 };
3124
3125 for (prim, fill) in fills {
3126 for mut aabb in Fill::get_bounds_disjoint(&prim_tree, prim) {
3127 aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z);
3128 aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z);
3129
3130 for x in aabb.min.x..aabb.max.x {
3131 for y in aabb.min.y..aabb.max.y {
3132 let wpos = Vec2::new(x, y);
3133 let col_tile = self.wpos_tile(wpos);
3134 if
3135 col_tile
3137 .plot
3138 .and_then(|p| self.plots[p].z_range())
3139 .zip(self.plots[plot].z_range())
3140 .is_some_and(|(a, b)| a.end > b.end)
3141 {
3142 continue;
3143 }
3144 let mut last_block = None;
3145
3146 let col = canvas
3147 .col(wpos)
3148 .map(|col| col.get_info())
3149 .unwrap_or_default();
3150
3151 for z in aabb.min.z..aabb.max.z {
3152 let pos = Vec3::new(x, y, z);
3153
3154 let mut sprite_cfg = None;
3155
3156 let map = |block| {
3157 let current_block = fill.sample_at(
3158 &prim_tree,
3159 prim,
3160 pos,
3161 &info,
3162 block,
3163 &mut sprite_cfg,
3164 &col,
3165 );
3166 if let (Some(last_block), None) = (last_block, current_block) {
3167 spawn(pos, last_block);
3168 }
3169 last_block = current_block;
3170 current_block.unwrap_or(block)
3171 };
3172
3173 match fill {
3174 Fill::ResourceSprite { .. } => canvas.map_resource(pos, map),
3175 _ => canvas.map(pos, map),
3176 };
3177
3178 if let Some(sprite_cfg) = sprite_cfg {
3179 canvas.set_sprite_cfg(pos, sprite_cfg);
3180 }
3181 }
3182 if let Some(block) = last_block {
3183 spawn(Vec3::new(x, y, aabb.max.z), block);
3184 }
3185 }
3186 }
3187 }
3188 }
3189
3190 for entity in entities {
3191 canvas.spawn(entity);
3192 }
3193 }
3194 }
3195
3196 pub fn apply_supplement(
3197 &self,
3198 dynamic_rng: &mut impl Rng,
3199 wpos2d: Vec2<i32>,
3200 supplement: &mut crate::ChunkSupplement,
3201 ) {
3202 for (_, plot) in self.plots.iter() {
3203 match &plot.kind {
3204 PlotKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement),
3205 PlotKind::Adlet(a) => a.apply_supplement(dynamic_rng, wpos2d, supplement),
3206 _ => {},
3207 }
3208 }
3209 }
3210}
3211
3212pub fn test_site() -> Site {
3213 let index = crate::index::Index::new(0);
3214 let index_ref = IndexRef {
3215 colors: &index.colors(),
3216 features: &index.features(),
3217 index: &index,
3218 };
3219 let mut gen_meta = SitesGenMeta::new(0);
3220 Site::generate_city(
3221 &Land::empty(),
3222 index_ref,
3223 &mut thread_rng(),
3224 Vec2::zero(),
3225 0.5,
3226 None,
3227 &mut gen_meta,
3228 )
3229}
3230
3231fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
3232 if land
3233 .get_chunk_wpos(wpos)
3234 .is_none_or(|c| c.river.near_water())
3235 {
3236 Some(HazardKind::Water)
3237 } else {
3238 Some(land.get_gradient_approx(wpos))
3239 .filter(|g| *g > 0.8)
3240 .map(|gradient| HazardKind::Hill { gradient })
3241 }
3242}
3243
3244fn temp_at_wpos(land: &Land, wpos: Vec2<i32>) -> f32 {
3245 land.get_chunk_wpos(wpos)
3246 .map(|c| c.temp)
3247 .unwrap_or(CONFIG.temperate_temp)
3248}
3249
3250pub fn aabr_tiles(aabr: Aabr<i32>) -> impl Iterator<Item = Vec2<i32>> {
3251 (0..aabr.size().h)
3252 .flat_map(move |y| (0..aabr.size().w).map(move |x| aabr.min + Vec2::new(x, y)))
3253}