veloren_world/layer/
shrub.rs

1use crate::{
2    Canvas,
3    all::ForestKind,
4    layer::cave::tunnel_bounds_at,
5    util::{Sampler, StructureGen2d, UnitChooser, gen_cache::StructureGenCache, seed_expan},
6};
7use common::{
8    assets::AssetHandle,
9    terrain::structure::{Structure, StructuresGroup},
10};
11use lazy_static::lazy_static;
12use rand::prelude::*;
13use rand_chacha::ChaChaRng;
14use vek::*;
15
16lazy_static! {
17    static ref JUNGLE_SHRUBS: AssetHandle<StructuresGroup> = Structure::load_group("shrubs.jungle");
18    static ref SAVANNAH_SHRUBS: AssetHandle<StructuresGroup> =
19        Structure::load_group("shrubs.savannah");
20    static ref TEMPERATE_SHRUBS: AssetHandle<StructuresGroup> =
21        Structure::load_group("shrubs.temperate");
22    static ref TAIGA_SHRUBS: AssetHandle<StructuresGroup> = Structure::load_group("shrubs.taiga");
23}
24
25struct Shrub {
26    wpos: Vec3<i32>,
27    seed: u32,
28    kind: ForestKind,
29}
30
31pub fn apply_shrubs_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) {
32    let mut shrub_gen = StructureGenCache::new(StructureGen2d::new(canvas.index().seed, 8, 4));
33
34    let info = canvas.info();
35
36    canvas.foreach_col(|_, wpos2d, _| {
37        shrub_gen.get(wpos2d, |wpos, seed| {
38            let col = info.col_or_gen(wpos)?;
39
40            let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
41
42            const BASE_SHRUB_DENSITY: f64 = 0.15;
43            if rng.gen_bool((BASE_SHRUB_DENSITY * col.tree_density as f64).clamped(0.0, 1.0))
44                && col.water_dist.is_none_or(|d| d > 8.0)
45                && col.alt > col.water_level
46                && col.spawn_rate > 0.9
47                && col.path.is_none_or(|(d, _, _, _)| d > 6.0)
48                && !tunnel_bounds_at(wpos, &info, &info.land())
49                    .any(|(_, z_range, _, _, _, _)| z_range.contains(&(col.alt as i32 - 1)))
50            {
51                let kind = *info
52                    .chunks()
53                    .make_forest_lottery(wpos)
54                    .choose_seeded(seed)
55                    .as_ref()?;
56                if rng.gen_bool(kind.shrub_density_factor() as f64) {
57                    Some(Shrub {
58                        wpos: wpos.with_z(col.alt as i32),
59                        seed,
60                        kind,
61                    })
62                } else {
63                    None
64                }
65            } else {
66                None
67            }
68        });
69    });
70
71    for shrub in shrub_gen.generated() {
72        let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(shrub.seed));
73
74        let units = UnitChooser::new(shrub.seed).get(shrub.seed).into();
75
76        let shrubs = match shrub.kind {
77            ForestKind::Mangrove => *JUNGLE_SHRUBS,
78            ForestKind::Acacia | ForestKind::Baobab => *SAVANNAH_SHRUBS,
79            ForestKind::Oak | ForestKind::Chestnut => *TEMPERATE_SHRUBS,
80            ForestKind::Pine => *TAIGA_SHRUBS,
81            _ => continue, // TODO: Add more shrub varieties
82        }
83        .read();
84
85        let structure = shrubs.choose(&mut rng).unwrap();
86        canvas.blit_structure(shrub.wpos, structure, shrub.seed, units, true);
87    }
88}