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