veloren_world/site2/plot/
giant_tree.rs1use 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 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 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 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}