veloren_world/site2/
mod.rs

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
39/// Seed a new RNG from an old RNG, thereby making the old RNG indepedent of
40/// changing use of the new RNG. The practical effect of this is to reduce the
41/// extent to which changes to child generation algorithm produce a 'butterfly
42/// effect' on their parent generators, meaning that generators will be less
43/// likely to produce entirely different outcomes if some detail of a generation
44/// algorithm changes slightly. This is generally good and makes worldgen code
45/// easier to maintain and less liable to breaking changes.
46fn 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    // NOTE: Do we want these to be public?
53    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            // Temporary solution for giving giant_tree's leaves enough space to be painted correctly
69            // TODO: This will have to be replaced by a system as described on discord :
70            // https://discord.com/channels/449602562165833758/450064928720814081/937044837461536808
71            + if self
72                .plots
73                .values()
74                .any(|p| matches!(&p.kind, PlotKind::GiantTree(_)))
75            {
76                // 25 Seems to be big enough for the current scale of 4.0
77                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        // Manhattan distance.
171        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                        // Transition cost
185                        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        // go from the site origin (0,0) at a random angle, as far as possible (up to
303        // the site radius / 6 because sites have ridiculously big radii like 160-600)
304        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        // One major, one minor road
337        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, /* + i */
364                    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                            // If the size of the intersection is greater than zero they intersect
411                            // on that axis and the distance on that axis is 0.
412                            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                        // `get_mut` doesn't increase generation bounds
439                        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    /// The find_roadside_aabr function wants to have an existing plaza or road.
471    /// This function is used to find a suitable location for the first plaza in
472    /// a town, which has the side-effect of creating at least one road.
473    /// This function is more expensive than the make_plaza function but
474    /// fails to find a plaza location only if there are no suitable
475    /// locations within the entire search radius.
476    ///
477    /// It works by exhaustively finding all tiles within a ring pattern around
478    /// the town center where the tile and all surrounding tiles to the
479    /// plaza radius are not hazards or roads. It then chooses the tile with
480    /// the minimum distance from the town center as the plaza location. See
481    /// the comments in common/src/spiral.rs for more information on the spiral
482    /// ring pattern.
483    ///
484    /// demarcate_obstacles() should be called before this function to mark the
485    /// obstacles and roads. (Cliff Towns are an exception).
486    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        // Find all the suitable locations for a plaza.
500        let mut plaza_locations = vec![];
501        // Search over a spiral ring pattern
502        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 all the tiles in the proposed plaza location are also not hazards or roads
508            // then add the tile as a candidate for a plaza location
509            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            // No suitable plaza locations were found, it's unlikely that the town will be
515            // able to be generated, but we can try to make a plaza anyway with
516            // the original make_plaza function.
517            self.make_plaza(land, index, rng, generator_stats, site_name, road_kind)
518        } else {
519            // Choose the minimum distance from the town center.
520            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            // use the first plaza location as the plaza position
526            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    /// This is make_initial_plaza with default options/parameters. This calls
533    /// make_initial_plaza with the default parameters for the plaza_radius
534    /// and search_inner_radius. The plaza_radius will be in the range 1-3,
535    /// and the search_inner_radius will be 7 + plaza_radius. The search_width
536    /// will be PLAZA_MAX_SEARCH_RADIUS - search_inner_radius. The
537    /// search_inner_radius is approximately the same distance
538    /// from the center of town as for the original make_plaza function, so this
539    /// function will place the initial plaza and roads near where the
540    /// original make_plaza function would place them in the case where the site
541    /// is clear of hazards.
542    ///
543    /// This default plaza generation function is used for generating cities,
544    /// cliff towns, savannah towns, and coastal towns. The other town types
545    /// (terracotta, myrmidon, desert city) have a central feature so they
546    /// use specific plaza generation parameters and call the make_initial_plaza
547    /// function directly.
548    ///
549    /// demarcate_obstacles() should be called before this function to mark the
550    /// obstacles and roads.
551    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        // The plaza radius can be 1, 2, or 3.
561        let plaza_radius = rng.gen_range(1..4);
562        // look for plaza locations within a ring with an outer dimension
563        // of 24 tiles and an inner dimension that will offset the plaza from the town
564        // center.
565        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        // place the initial plaza
742        site.demarcate_obstacles(land);
743        // The terracotta_palace is 15 tiles in radius, so the plaza should be outside
744        // the palace.
745        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                    // TerracottaHouse
788                    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                    // TerracottaYard
833                    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        // place the initial plaza
902        site.demarcate_obstacles(land);
903        // The myrmidon_arena is 16 tiles in radius, so the plaza should be outside the
904        // palace.
905        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            // MyrmidonHouse
946            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    // Size is 0..1
1012    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        // place the initial plaza
1030        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), // house
1043            (5.0, 2),  // guard tower
1044            (15.0, 3), // field
1045            (5.0, 4),  // castle
1046            (5.0, 5),  // workshop
1047            (15.0, 6), // airship dock
1048            (15.0, 7), // tavern
1049            (5.0, 8),  // barn
1050        ]);
1051
1052        // These plots have minimums or limits.
1053        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                // Workshop
1061                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                // House
1107                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                // Guard tower
1153                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                        // let plot = site.create_plot(Plot {
1159                        //     kind: PlotKind::Castle(plot::Castle::generate(
1160                        //         land,
1161                        //         &mut rng,
1162                        //         &site,
1163                        //         aabr,
1164                        //     )),
1165                        //     root_tile: aabr.center(),
1166                        //     tiles: aabr_tiles(aabr).collect(),
1167                        //     seed: rng.gen(),
1168                        // });
1169
1170                        // site.blit_aabr(aabr, Tile {
1171                        //     kind: TileKind::Castle,
1172                        //     plot: Some(plot),
1173                        //     hard_alt: None,
1174                        // });
1175                    }
1176                },
1177                // Field
1178                3 => {
1179                    Self::generate_farm(false, &mut rng, &mut site, land);
1180                },
1181                // Castle
1182                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                        // Courtyard
1272                        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                        // Keep
1285                        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                //airship dock
1330                6 if (size > 0.125 && airship_docks == 0) => {
1331                    generator_stats.attempt(&site.name, GenStatPlotKind::AirshipDock);
1332                    // if let Some((_aabr, _, _door_dir, alt)) = attempt(10, || {
1333                    //     site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
1334                    // }) {
1335                    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                    // }
1378                },
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        // TODO use the nearest peak name. Unfortunately this requires `Civs` but we
1449        // only have access to `WorldSim`
1450        site.name = NameGen::location(&mut rng).generate_town() + " Glider Course";
1451
1452        // Pick the starting downhill direction based on the average drop over
1453        // two chunks in the four cardinal directions
1454        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        // Platform
1487        let pos = origin;
1488        let tile_pos: Vec2<i32> = Vec2::zero();
1489        positions.push((pos, tile_pos));
1490
1491        // This defines the distance between rings
1492        // An offset of 5 results in courses that are about 1 minute long
1493        // An offset of 6+ results in not all plots being in range of the site
1494        const CHUNK_OFFSET: usize = 5;
1495        // WARNING: This assumes x and y lengths of a chunk are the same!!!
1496        let offset = CHUNK_OFFSET as i32 * TerrainChunkSize::RECT_SIZE.x as i32;
1497        // Always convert to tiles then back to wpos to remove any integer division
1498        // artifacts
1499        let tile_offset = offset / TILE_SIZE as i32;
1500        let pos_offset = tile_offset * TILE_SIZE as i32;
1501
1502        // Loop 1 is always straight forward from the launch platform
1503        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        // Loops 2-9 follow the downhill path of terrain chunks
1508        // In the future it may be desirable to follow ridges and the like but that
1509        // would be a future MR
1510        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                // Convert to tiles first, then back to wpos to ensure coordinates align, as
1520                // chunks are not tile aligned
1521                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                // Only want to save positions with large enough chunk offsets, not every chunk
1528                // position
1529                if j % CHUNK_OFFSET == 0 {
1530                    positions.push((pos, tile_pos));
1531                }
1532            } else {
1533                valid_course = false;
1534            }
1535        }
1536        // Currently there is no check to ensure the delta z between rings is
1537        // sufficient to successfully fly through the course. This should cause
1538        // no glider course site to be created. Right now it just doesn't spawn
1539        // one in the world (similar to towns when placed near/on bodies of water).
1540        // In the future maybe the generate functions should return an `Option`
1541        // instead of a `Site`
1542        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                        // Launch platform
1553                        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                        // Point each ring after 1 towards the next ring
1578                        // This provides a subtle guide through the course
1579                        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                        // last ring (ring 9) and finish platform
1609                        // Separate condition due to window iterator to ensure
1610                        // the finish platform is generated
1611                        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                        // Finish
1636                        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        // place the initial plaza
1683        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                    // CliffTower
1699                    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                    // CliffTownAirshipDock
1747                    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        // place the initial plaza
1816        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                    // SavannahWorkshop
1835                    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                    // SavannahHut
1881
1882                    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                    // SavannahAirshipDock
1927
1928                    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                // Field
1973                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        // place the initial plaza
2001        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                    // CoastalWorkshop
2019                    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                    // CoastalHouse
2065
2066                    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                    // CoastalAirshipDock
2112                    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                // Field
2157                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        // place the initial plaza
2186        site.demarcate_obstacles(land);
2187        // The desert_city_arena is 17 tiles in radius, so the plaza should be outside
2188        // the palace.
2189        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                // DesertCityMultiplot
2237                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                // DesertCityTemple
2285                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                // DesertCityAirshipDock
2322                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                // cactus farm
2359                4 => {
2360                    Self::generate_farm(true, &mut rng, &mut site, land);
2361                },
2362                // desert barn - disabled for now (0.0 spawn chance)
2363                // need desert-variant sprite
2364                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        // SeaChapel
2508        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 /*| TileKind::Road*/ => (-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                // TileKind::Building => {
3002                //     let base_alt = tile.plot.map(|p| self.plot(p)).map_or(col.alt as i32, |p| p.base_alt);
3003                //     for z in base_alt - 12..base_alt + 16 {
3004                //         canvas.set(
3005                //             Vec3::new(wpos2d.x, wpos2d.y, z),
3006                //             Block::new(BlockKind::Wood, Rgb::new(180, 90 + (seed % 64) as u8, 120))
3007                //         );
3008                //     }
3009                // },
3010                // TileKind::Castle | TileKind::Wall => {
3011                //     let base_alt = tile.plot.map(|p| self.plot(p)).map_or(col.alt as i32, |p| p.base_alt);
3012                //     for z in base_alt - 12..base_alt + if tile.kind == TileKind::Wall { 24 } else { 40 } {
3013                //         canvas.set(
3014                //             Vec3::new(wpos2d.x, wpos2d.y, z),
3015                //             Block::new(BlockKind::Wood, Rgb::new(40, 40, 55))
3016                //         );
3017                //     }
3018                // },
3019                _ => {},
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, // Round up, uninclusive, border
3049        };
3050
3051        // Don't double-generate the same plot per chunk!
3052        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        // TODO: Solve the 'trees are too big' problem and remove this
3065        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        // First sort by priority, then id.
3073        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.is_building() && */
3108                            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}