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(rand::rng().random());
109
110 blocks.for_each(|(pos, block)| {
111 match block.kind() {
112 BlockKind::Leaves
113 if rng.random_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.random_range(0..6) == 0 => drip.push(pos),
121 BlockKind::Grass => {
122 if rng.random_range(0..16) == 0 {
123 grass.push(pos);
124 }
125 match rng.random_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.random_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.random_range(0..5) == 0 {
171 fires.push(pos + Vec3::unit_z())
172 }
173 if rng.random_range(0..16) == 0 {
174 lavapool.push(pos)
175 }
176 },
177 BlockKind::GlowingMushroom if rng.random_range(0..8) == 0 => spores.push(pos),
178 BlockKind::Snow | BlockKind::Ice if rng.random_range(0..16) == 0 => snow.push(pos),
179 _ => {
180 if let Some(sprite) = block.get_sprite() {
181 if sprite.category() == sprite::Category::Lamp
182 && let Ok(sprite::LightEnabled(enabled)) = block.get_attr()
183 {
184 interactables.push((pos, Interaction::LightToggle(!enabled)));
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.random_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 interactables.push((pos, Interaction::Craft(CraftingTab::Potion)))
241 },
242 SpriteKind::Anvil => {
243 interactables.push((pos, Interaction::Craft(CraftingTab::Weapon)))
244 },
245 SpriteKind::CookingPot => {
246 fires.push(pos);
247 interactables.push((pos, Interaction::Craft(CraftingTab::Food)))
248 },
249 SpriteKind::DismantlingBench => interactables
250 .push((pos, Interaction::Craft(CraftingTab::Dismantle))),
251 SpriteKind::RepairBench => {
252 interactables.push((pos, Interaction::Craft(CraftingTab::All)))
253 },
254 SpriteKind::OneWayWall => one_way_walls.push((
255 pos,
256 Vec2::unit_y()
257 .rotated_z(
258 std::f32::consts::PI
259 * 0.25
260 * block
261 .get_attr::<sprite::Ori>()
262 .unwrap_or(sprite::Ori(0))
263 .0
264 as f32,
265 )
266 .with_z(0.0),
267 )),
268 SpriteKind::Sign | SpriteKind::HangingSign => {
269 interactables.push((pos, Interaction::Read))
270 },
271 SpriteKind::MycelBlue => spores.push(pos),
272 SpriteKind::Mold => spores.push(pos),
273 _ => {},
274 }
275 }
276 },
277 }
278 if block.is_collectible() {
280 interactables.push((pos, Interaction::Collect));
281 }
282 if let Some(glow) = block.get_glow() {
283 if block.get_sprite().is_none() {
286 minor_lights.push((pos, glow));
287 } else {
288 lights.push((pos, glow));
289 }
290 }
291 });
292
293 const MAX_MINOR_LIGHTS: usize = 64;
296 lights.extend(
297 minor_lights
298 .choose_multiple(&mut rng, MAX_MINOR_LIGHTS)
299 .copied(),
300 );
301
302 Self {
303 leaves,
304 drip,
305 grass,
306 slow_river,
307 fast_river,
308 waterfall,
309 lavapool,
310 fires,
311 smokers,
312 beehives,
313 reeds,
314 fireflies,
315 flowers,
316 fire_bowls,
317 snow,
318 spores,
319 cricket1,
320 cricket2,
321 cricket3,
322 frogs,
323 one_way_walls,
324 interactables,
325 lights,
326 temperature,
327 humidity,
328 train_smokes,
329 }
330 }
331}