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