1use crate::hud::CraftingTab;
2use common::{
3 terrain::{Block, BlockKind, SpriteKind, sprite},
4 vol::ReadVol,
5};
6use common_base::span;
7use rand::prelude::*;
8use rand_chacha::ChaCha8Rng;
9use vek::*;
10
11#[derive(Clone, Copy, Debug)]
12pub enum Interaction {
13 Collect,
16 Craft(CraftingTab),
17 Mount,
18 Read,
19 LightToggle(bool),
20}
21
22#[derive(Copy, Clone)]
23pub enum FireplaceType {
24 House,
25 Workshop, }
27
28pub struct SmokerProperties {
29 pub position: Vec3<i32>,
30 pub kind: FireplaceType,
31}
32
33impl SmokerProperties {
34 fn new(position: Vec3<i32>, kind: FireplaceType) -> Self { Self { position, kind } }
35}
36
37#[derive(Default)]
38pub struct BlocksOfInterest {
39 pub leaves: Vec<Vec3<i32>>,
40 pub drip: Vec<Vec3<i32>>,
41 pub grass: Vec<Vec3<i32>>,
42 pub slow_river: Vec<Vec3<i32>>,
43 pub fast_river: Vec<Vec3<i32>>,
44 pub waterfall: Vec<(Vec3<i32>, Vec3<f32>)>,
45 pub lavapool: Vec<Vec3<i32>>,
46 pub fires: Vec<Vec3<i32>>,
47 pub smokers: Vec<SmokerProperties>,
48 pub beehives: Vec<Vec3<i32>>,
49 pub reeds: Vec<Vec3<i32>>,
50 pub fireflies: Vec<Vec3<i32>>,
51 pub flowers: Vec<Vec3<i32>>,
52 pub fire_bowls: Vec<Vec3<i32>>,
53 pub snow: Vec<Vec3<i32>>,
54 pub spores: Vec<Vec3<i32>>,
55 pub cricket1: Vec<Vec3<i32>>,
57 pub cricket2: Vec<Vec3<i32>>,
58 pub cricket3: Vec<Vec3<i32>>,
59 pub frogs: Vec<Vec3<i32>>,
60 pub one_way_walls: Vec<(Vec3<i32>, Vec3<f32>)>,
61 pub interactables: Vec<(Vec3<i32>, Interaction)>,
64 pub lights: Vec<(Vec3<i32>, u8)>,
65 pub train_smokes: Vec<Vec3<i32>>,
66 pub temperature: f32,
68 pub humidity: f32,
69}
70
71impl BlocksOfInterest {
72 pub fn from_blocks(
73 blocks: impl Iterator<Item = (Vec3<i32>, Block)>,
74 river_velocity: Vec3<f32>,
75 temperature: f32,
76 humidity: f32,
77 chunk: &impl ReadVol<Vox = Block>,
78 ) -> Self {
79 span!(_guard, "from_chunk", "BlocksOfInterest::from_chunk");
80 let mut leaves = Vec::new();
81 let mut drip = Vec::new();
82 let mut grass = Vec::new();
83 let mut slow_river = Vec::new();
84 let mut fast_river = Vec::new();
85 let mut waterfall = Vec::new();
86 let mut lavapool = Vec::new();
87 let mut fires = Vec::new();
88 let mut smokers = Vec::new();
89 let mut beehives = Vec::new();
90 let mut reeds = Vec::new();
91 let mut fireflies = Vec::new();
92 let mut flowers = Vec::new();
93 let mut interactables = Vec::new();
94 let mut lights = Vec::new();
95 let mut minor_lights = Vec::new();
98 let mut fire_bowls = Vec::new();
99 let mut snow = Vec::new();
100 let mut cricket1 = Vec::new();
101 let mut cricket2 = Vec::new();
102 let mut cricket3 = Vec::new();
103 let mut frogs = Vec::new();
104 let mut one_way_walls = Vec::new();
105 let mut spores = Vec::new();
106 let mut train_smokes = Vec::new();
107
108 let mut rng = ChaCha8Rng::from_seed(thread_rng().gen());
109
110 blocks.for_each(|(pos, block)| {
111 match block.kind() {
112 BlockKind::Leaves
113 if rng.gen_range(0..16) == 0
114 && chunk
115 .get(pos - Vec3::unit_z())
116 .map_or(true, |b| !b.is_filled()) =>
117 {
118 leaves.push(pos)
119 },
120 BlockKind::WeakRock if rng.gen_range(0..6) == 0 => drip.push(pos),
121 BlockKind::Grass => {
122 if rng.gen_range(0..16) == 0 {
123 grass.push(pos);
124 }
125 match rng.gen_range(0..8192) {
126 1 => cricket1.push(pos),
127 2 => cricket2.push(pos),
128 3 => cricket3.push(pos),
129 _ => {},
130 }
131 },
132 BlockKind::Water => {
133 let is_waterfall = chunk
134 .get(pos + vek::Vec3::unit_z())
135 .is_ok_and(|b| b.is_air())
136 && [
137 vek::Vec2::new(0, 1),
138 vek::Vec2::new(1, 0),
139 vek::Vec2::new(0, -1),
140 vek::Vec2::new(-1, 0),
141 ]
142 .iter()
143 .map(|p| {
144 (1..=2)
145 .take_while(|i| {
146 chunk.get(pos + p.with_z(*i)).is_ok_and(|b| b.is_liquid())
147 })
148 .count()
149 })
150 .any(|s| s >= 2);
151
152 if is_waterfall {
153 waterfall.push((pos, river_velocity));
154 }
155
156 let river_speed_sq = river_velocity.magnitude_squared();
157 if is_waterfall || river_speed_sq > 0.9_f32.powi(2) {
159 fast_river.push(pos)
160 } else if river_speed_sq > 0.3_f32.powi(2) {
161 slow_river.push(pos)
162 }
163 },
164 BlockKind::Snow if rng.gen_range(0..16) == 0 => snow.push(pos),
165 BlockKind::Lava
166 if chunk
167 .get(pos + Vec3::unit_z())
168 .map_or(true, |b| !b.is_filled()) =>
169 {
170 if rng.gen_range(0..5) == 0 {
171 fires.push(pos + Vec3::unit_z())
172 }
173 if rng.gen_range(0..16) == 0 {
174 lavapool.push(pos)
175 }
176 },
177 BlockKind::GlowingMushroom if rng.gen_range(0..8) == 0 => spores.push(pos),
178 BlockKind::Snow | BlockKind::Ice if rng.gen_range(0..16) == 0 => snow.push(pos),
179 _ => {
180 if let Some(sprite) = block.get_sprite() {
181 if sprite.category() == sprite::Category::Lamp {
182 if let Ok(sprite::LightEnabled(enabled)) = block.get_attr() {
183 interactables.push((pos, Interaction::LightToggle(!enabled)));
184 }
185 }
186
187 if block.is_mountable() {
188 interactables.push((pos, Interaction::Mount));
189 }
190
191 match sprite {
192 SpriteKind::Ember => {
193 fires.push(pos);
194 smokers.push(SmokerProperties::new(pos, FireplaceType::House));
195 },
196 SpriteKind::TrainSmoke => {
197 train_smokes.push(pos);
198 },
199 SpriteKind::FireBlock => {
200 fire_bowls.push(pos);
201 },
202 SpriteKind::StreetLamp => fire_bowls.push(pos + Vec3::unit_z() * 2),
205 SpriteKind::FireBowlGround => fire_bowls.push(pos + Vec3::unit_z()),
206 SpriteKind::StreetLampTall => fire_bowls.push(pos + Vec3::unit_z() * 4),
207 SpriteKind::WallSconce => fire_bowls.push(pos + Vec3::unit_z()),
208 SpriteKind::Beehive => beehives.push(pos),
209 SpriteKind::Reed => {
210 reeds.push(pos);
211 fireflies.push(pos);
212 if rng.gen_range(0..12) == 0 {
213 frogs.push(pos);
214 }
215 },
216 SpriteKind::CaveMushroom => fireflies.push(pos),
217 SpriteKind::PinkFlower => flowers.push(pos),
218 SpriteKind::PurpleFlower => flowers.push(pos),
219 SpriteKind::RedFlower => flowers.push(pos),
220 SpriteKind::WhiteFlower => flowers.push(pos),
221 SpriteKind::YellowFlower => flowers.push(pos),
222 SpriteKind::Sunflower => flowers.push(pos),
223 SpriteKind::CraftingBench => {
224 interactables.push((pos, Interaction::Craft(CraftingTab::All)))
225 },
226 SpriteKind::SmokeDummy => {
227 smokers.push(SmokerProperties::new(pos, FireplaceType::Workshop));
228 },
229 SpriteKind::Forge => interactables
230 .push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
231 SpriteKind::TanningRack => interactables
232 .push((pos, Interaction::Craft(CraftingTab::ProcessedMaterial))),
233 SpriteKind::SpinningWheel => {
234 interactables.push((pos, Interaction::Craft(CraftingTab::All)))
235 },
236 SpriteKind::Loom => {
237 interactables.push((pos, Interaction::Craft(CraftingTab::All)))
238 },
239 SpriteKind::Cauldron => {
240 fires.push(pos);
241 interactables.push((pos, Interaction::Craft(CraftingTab::Potion)))
242 },
243 SpriteKind::Anvil => {
244 interactables.push((pos, Interaction::Craft(CraftingTab::Weapon)))
245 },
246 SpriteKind::CookingPot => {
247 fires.push(pos);
248 interactables.push((pos, Interaction::Craft(CraftingTab::Food)))
249 },
250 SpriteKind::DismantlingBench => {
251 fires.push(pos);
252 interactables
253 .push((pos, Interaction::Craft(CraftingTab::Dismantle)))
254 },
255 SpriteKind::RepairBench => {
256 interactables.push((pos, Interaction::Craft(CraftingTab::All)))
257 },
258 SpriteKind::OneWayWall => one_way_walls.push((
259 pos,
260 Vec2::unit_y()
261 .rotated_z(
262 std::f32::consts::PI
263 * 0.25
264 * block
265 .get_attr::<sprite::Ori>()
266 .unwrap_or(sprite::Ori(0))
267 .0
268 as f32,
269 )
270 .with_z(0.0),
271 )),
272 SpriteKind::Sign | SpriteKind::HangingSign => {
273 interactables.push((pos, Interaction::Read))
274 },
275 SpriteKind::MycelBlue => spores.push(pos),
276 SpriteKind::Mold => spores.push(pos),
277 _ => {},
278 }
279 }
280 },
281 }
282 if block.default_tool().is_some() {
284 interactables.push((pos, Interaction::Collect));
285 }
286 if let Some(glow) = block.get_glow() {
287 if block.get_sprite().is_none() {
290 minor_lights.push((pos, glow));
291 } else {
292 lights.push((pos, glow));
293 }
294 }
295 });
296
297 const MAX_MINOR_LIGHTS: usize = 64;
300 lights.extend(
301 minor_lights
302 .choose_multiple(&mut rng, MAX_MINOR_LIGHTS)
303 .copied(),
304 );
305
306 Self {
307 leaves,
308 drip,
309 grass,
310 slow_river,
311 fast_river,
312 waterfall,
313 lavapool,
314 fires,
315 smokers,
316 beehives,
317 reeds,
318 fireflies,
319 flowers,
320 fire_bowls,
321 snow,
322 spores,
323 cricket1,
324 cricket2,
325 cricket3,
326 frogs,
327 one_way_walls,
328 interactables,
329 lights,
330 temperature,
331 humidity,
332 train_smokes,
333 }
334 }
335}