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