veloren_world/site2/plot/
giant_tree.rs

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