Skip to main content

veloren_world/site/plot/
giant_tree.rs

1use crate::{
2    Land, Sampler, SpriteKind,
3    layer::tree::{ProceduralTree, TreeConfig},
4    site::{Fill, Painter, Site, Structure, namegen::NameGen},
5    util::FastNoise,
6};
7use common::{
8    generation::EntityInfo,
9    terrain::{Block, BlockKind},
10};
11use rand::RngExt;
12use vek::*;
13
14pub struct GiantTree {
15    name: String,
16    wpos: Vec3<i32>,
17    tree: ProceduralTree,
18    seed: u32,
19}
20
21impl GiantTree {
22    pub fn generate(
23        site: &Site,
24        center_tile: Vec2<i32>,
25        land: &Land,
26        rng: &mut impl RngExt,
27    ) -> Self {
28        let wpos = site.tile_center_wpos(center_tile);
29        Self {
30            name: format!("Tree of {}", NameGen::location(rng).generate()),
31            // Get the tree's altitude
32            wpos: wpos.with_z(land.get_alt_approx(wpos) as i32),
33            tree: {
34                let config = TreeConfig::giant(rng, 4.0, true);
35                ProceduralTree::generate(config, rng)
36            },
37            seed: rng.random(),
38        }
39    }
40
41    pub fn name(&self) -> &str { &self.name }
42
43    pub fn radius(&self) -> f32 { 100.0 }
44
45    pub fn tree(&self) -> &ProceduralTree { &self.tree }
46
47    pub fn entity_at(
48        &self,
49        pos: Vec3<i32>,
50        above_block: &Block,
51        dynamic_rng: &mut impl RngExt,
52    ) -> Option<EntityInfo> {
53        if above_block.kind() == BlockKind::Leaves && dynamic_rng.random_bool(0.000055) {
54            let entity = EntityInfo::at(pos.as_());
55            match dynamic_rng.random_range(0..=7) {
56                0 => Some(entity.with_asset_expect(
57                    "common.entity.wild.aggressive.horn_beetle",
58                    dynamic_rng,
59                    None,
60                )),
61                1 => Some(entity.with_asset_expect(
62                    "common.entity.wild.aggressive.stag_beetle",
63                    dynamic_rng,
64                    None,
65                )),
66                2..=3 => Some(entity.with_asset_expect(
67                    "common.entity.wild.aggressive.deadwood",
68                    dynamic_rng,
69                    None,
70                )),
71                4 => Some(entity.with_asset_expect(
72                    "common.entity.wild.aggressive.maneater",
73                    dynamic_rng,
74                    None,
75                )),
76                5..=7 => Some(entity.with_asset_expect(
77                    "common.entity.wild.peaceful.parrot",
78                    dynamic_rng,
79                    None,
80                )),
81                _ => None,
82            }
83        } else {
84            None
85        }
86    }
87
88    pub fn leaf_color(&self) -> Rgb<u8> {
89        let fast_noise = FastNoise::new(self.seed);
90        let dark = Rgb::new(10, 70, 50).map(|e| e as f32);
91        let light = Rgb::new(80, 140, 10).map(|e| e as f32);
92        Lerp::lerp(
93            dark,
94            light,
95            fast_noise.get((self.wpos.map(|e| e as f64) * 0.05) * 0.5 + 0.5),
96        )
97        .map(|e| e as u8)
98    }
99}
100
101impl Structure for GiantTree {
102    #[cfg(feature = "dyn-lib")]
103    #[unsafe(export_name = "as_dyn_structure_gianttree")]
104    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
105        Some((Self::as_dyn_impl(self), "as_dyn_structure_gianttree"))
106    }
107
108    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
109        let leaf_col = self.leaf_color();
110        let mut rng = rand::rng();
111        self.tree.walk(|branch, parent| {
112            let aabr = Aabr {
113                min: self.wpos.xy() + branch.get_aabb().min.xy().as_(),
114                max: self.wpos.xy() + branch.get_aabb().max.xy().as_(),
115            };
116            if aabr.collides_with_aabr(painter.render_aabr().as_()) {
117                painter
118                    .line_two_radius(
119                        self.wpos + branch.get_line().start.as_(),
120                        self.wpos + branch.get_line().end.as_(),
121                        parent.get_wood_radius(),
122                        branch.get_wood_radius(),
123                    )
124                    .fill(Fill::Block(Block::new(
125                        BlockKind::Wood,
126                        Rgb::new(80, 32, 0),
127                    )));
128                if branch.get_leaf_radius() > branch.get_wood_radius() {
129                    painter
130                        .line_two_radius(
131                            self.wpos + branch.get_line().start.as_(),
132                            self.wpos + branch.get_line().end.as_(),
133                            parent.get_leaf_radius(),
134                            branch.get_leaf_radius(),
135                        )
136                        .fill(Fill::Block(Block::new(BlockKind::Leaves, leaf_col)));
137                    // Calculate direction of the branch
138                    let branch_start = branch.get_line().start;
139                    let branch_end = branch.get_line().end;
140                    let branch_direction = (branch_end - branch_start).normalized();
141
142                    // Generate a sprite at the surface of the leafy part of the branch
143                    let displacement =
144                        (branch_direction * branch.get_leaf_radius()).map(|e| e.round() as i32);
145                    let pos = self.wpos + branch_end.as_() + displacement;
146                    if rng.random_bool(0.07) {
147                        let ori = rng.random_range(0..=3) * 2;
148                        painter.resource_sprite(pos, SpriteKind::Ironwood, ori);
149                    }
150                }
151                true
152            } else {
153                false
154            }
155        });
156    }
157}