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