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