1use super::*;
2use crate::{Land, util::within_distance};
3use generation::render_prefab;
4use rand::prelude::*;
5use vek::*;
6
7const TILE_SIZE: i32 = 13;
8
9pub struct DwarvenMine {
11 name: String,
12 bounds: Aabr<i32>,
14 pub(crate) alt: i32,
16 origin: Vec2<i32>,
17}
18
19impl DwarvenMine {
20 pub fn generate(
21 land: &Land,
22 rng: &mut impl Rng,
23 site: &Site,
24 wpos: Vec2<i32>,
25 tile_aabr: Aabr<i32>,
26 ) -> Self {
27 let bounds = Aabr {
28 min: site.tile_wpos(tile_aabr.min),
29 max: site.tile_wpos(tile_aabr.max),
30 };
31
32 let name = format!("{} Mine", NameGen::location(rng).generate_mine());
33
34 Self {
37 name,
38 bounds,
39 origin: wpos - TILE_SIZE / 2,
40 alt: land.get_alt_approx(site.tile_center_wpos(wpos)) as i32,
41 }
42 }
43
44 pub fn name(&self) -> &str { &self.name }
45
46 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
47 let near_entrance = within_distance(wpos, self.origin, 64);
48 SpawnRules {
49 trees: !near_entrance,
50 max_warp: (!near_entrance) as i32 as f32,
51 ..SpawnRules::default()
52 }
53 }
54}
55
56impl Structure for DwarvenMine {
57 #[cfg(feature = "use-dyn-lib")]
58 const UPDATE_FN: &'static [u8] = b"render_mine\0";
59
60 #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_mine"))]
61 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
62 let center = self.bounds.center();
63 let entrance_path = "site_structures.dwarves.entrance";
65 let entrance_pos: Vec3<i32> = (center.x, center.y, self.alt - 37).into(); render_prefab(entrance_path, entrance_pos, painter);
67 let hallway0_path = "site_structures.dwarves.hallway";
69 let hallway0_pos: Vec3<i32> = entrance_pos + Vec3::new(-67, 8, -37);
70 render_prefab(hallway0_path, hallway0_pos + Vec3::new(0, 0, 0), painter);
71 let hallway1_path = "site_structures.dwarves.hallway1";
73 let hallway1_pos: Vec3<i32> = hallway0_pos + Vec3::new(-55, -7, -33);
74 render_prefab(hallway1_path, hallway1_pos, painter);
75 let mining_site_path = "site_structures.dwarves.mining_site";
77 let mining_site_pos: Vec3<i32> = hallway1_pos + Vec3::new(-5, 28, 21);
78 render_prefab(mining_site_path, mining_site_pos, painter);
79 let after_mining_path = "site_structures.dwarves.after_forgemaster";
81 let after_mining_pos: Vec3<i32> = mining_site_pos + Vec3::new(-12, -9, -3);
82 render_prefab(
83 after_mining_path,
84 after_mining_pos + Vec3::new(0, -2, 0),
85 painter,
86 );
87 let hallway2_path = "site_structures.dwarves.hallway2";
89 let hallway2_pos: Vec3<i32> = after_mining_pos + Vec3::new(-3, 35, -18);
90 render_prefab(hallway2_path, hallway2_pos, painter);
91 let excavation_site_path = "site_structures.dwarves.excavation_site";
93 let excavation_site_pos: Vec3<i32> = hallway2_pos + Vec3::new(-35, 83, -10);
94 render_prefab(excavation_site_path, excavation_site_pos, painter);
95 let forgemaster_boss_path = "site_structures.dwarves.forgemaster_boss";
97 let forgemaster_boss_pos: Vec3<i32> = excavation_site_pos + Vec3::new(115, 0, -17);
98 render_prefab(forgemaster_boss_path, forgemaster_boss_pos, painter);
99 let cleansing_room_path = "site_structures.dwarves.cleansing_room";
101 let cleansing_room_pos: Vec3<i32> = excavation_site_pos + Vec3::new(-47, -125, 6);
102 render_prefab(cleansing_room_path, cleansing_room_pos, painter);
103 let smelting_room_path = "site_structures.dwarves.smelting_room";
105 let smelting_room_pos: Vec3<i32> = cleansing_room_pos + Vec3::new(49, -6, 0);
106 render_prefab(smelting_room_path, smelting_room_pos, painter);
107 let forgemaster_path = "site_structures.dwarves.forgemaster_room";
109 let forgemaster_pos: Vec3<i32> = smelting_room_pos + Vec3::new(8, 36, 10);
110 render_prefab(forgemaster_path, forgemaster_pos, painter);
111
112 let random_entities: Vec<(Vec3<i32>, u8)> = vec![
114 (forgemaster_pos + Vec3::new(11, 20, 1), 0),
115 (forgemaster_pos + Vec3::new(0, 20, 1), 0),
116 (forgemaster_pos + Vec3::new(-11, 20, 1), 0),
117 ];
118
119 for (pos, rot) in random_entities {
120 spawn_random_entity(pos, painter, rot);
121 }
122
123 let miner = "common.entity.dungeon.dwarven_quarry.miner";
125 let iron_dwarf = "common.entity.dungeon.dwarven_quarry.iron_dwarf";
126 let turret = "common.entity.dungeon.dwarven_quarry.turret";
127 let lavathrower = "common.entity.dungeon.dwarven_quarry.lavathrower";
128 let mine_guard = "common.entity.dungeon.dwarven_quarry.mine_guard";
129 let flamekeeper = "common.entity.dungeon.dwarven_quarry.flamekeeper";
130 let forgemaster = "common.entity.dungeon.dwarven_quarry.forgemaster";
131 let irongolem = "common.entity.dungeon.dwarven_quarry.irongolem";
132 let irongolem_key = "common.entity.dungeon.dwarven_quarry.irongolem_key";
133 let snaretongue_forge_key = "common.entity.dungeon.dwarven_quarry.snaretongue_forge_key";
134 let snaretongue_miner_key = "common.entity.dungeon.dwarven_quarry.snaretongue_miner_key";
135
136 let entrance_offset: Vec3<f32> = (20.0, -20.0, 45.0).into();
137 let miner_pos: Vec<(Vec3<f32>, &str)> = vec![
138 (
140 (entrance_pos + Vec3::new(-6, 0, 0)).map(|x| x as f32) + entrance_offset,
141 miner,
142 ),
143 (
144 (entrance_pos + Vec3::new(-2, 0, 0)).map(|x| x as f32) + entrance_offset,
145 miner,
146 ),
147 (
149 hallway0_pos.map(|x| x as f32) + Vec3::new(10.0, -4.0, 16.0),
150 miner,
151 ),
152 (
153 hallway0_pos.map(|x| x as f32) + Vec3::new(-10.0, -4.0, 10.0),
154 miner,
155 ),
156 (
157 hallway0_pos.map(|x| x as f32) + Vec3::new(-10.0, -6.0, 10.0),
158 miner,
159 ),
160 (
162 hallway0_pos.map(|x| x as f32) + Vec3::new(-30.0, -4.0, 0.0),
163 miner,
164 ),
165 (
166 hallway0_pos.map(|x| x as f32) + Vec3::new(-46.0, -4.0, -5.0),
167 miner,
168 ),
169 (
171 mining_site_pos.map(|x| x as f32) + Vec3::new(14.0, 17.0, 10.0),
172 flamekeeper,
173 ),
174 (
175 mining_site_pos.map(|x| x as f32) + Vec3::new(14.0, 27.0, 8.0),
176 mine_guard,
177 ),
178 (
179 mining_site_pos.map(|x| x as f32) + Vec3::new(-4.0, 32.0, 8.0),
180 turret,
181 ),
182 (
183 mining_site_pos.map(|x| x as f32) + Vec3::new(32.0, 32.0, 10.0),
184 turret,
185 ),
186 (
187 mining_site_pos.map(|x| x as f32) + Vec3::new(14.0, 32.0, 10.0),
188 mine_guard,
189 ),
190 (
191 mining_site_pos.map(|x| x as f32) + Vec3::new(-8.0, 0.0, 10.0),
192 miner,
193 ),
194 (
195 mining_site_pos.map(|x| x as f32) + Vec3::new(36.0, 0.0, 10.0),
196 miner,
197 ),
198 (
199 mining_site_pos.map(|x| x as f32) + Vec3::new(-8.0, 15.0, 0.0),
200 iron_dwarf,
201 ),
202 (
203 mining_site_pos.map(|x| x as f32) + Vec3::new(36.0, 15.0, 0.0),
204 iron_dwarf,
205 ),
206 (
209 hallway2_pos.map(|x| x as f32) + Vec3::new(-4.0, -22.0, 20.0),
210 miner,
211 ),
212 (
213 hallway2_pos.map(|x| x as f32) + Vec3::new(-4.0, 7.0, 10.0),
214 miner,
215 ),
216 (
217 hallway2_pos.map(|x| x as f32) + Vec3::new(-5.0, 30.0, 0.0),
218 mine_guard,
219 ),
220 (
222 hallway2_pos.map(|x| x as f32) + Vec3::new(0.0, 44.0, 0.0),
223 iron_dwarf,
224 ),
225 (
226 hallway2_pos.map(|x| x as f32) + Vec3::new(2.0, 44.0, 0.0),
227 lavathrower,
228 ),
229 (
230 forgemaster_boss_pos.map(|x| x as f32) + Vec3::new(23.0, 1.0, 20.0),
231 forgemaster,
232 ),
233 (
235 excavation_site_pos.map(|x| x as f32) + Vec3::new(0.0, -25.0, 18.0),
236 lavathrower,
237 ),
238 (
240 excavation_site_pos.map(|x| x as f32) + Vec3::new(50.0, 42.0, 25.0),
241 mine_guard,
242 ),
243 (
244 excavation_site_pos.map(|x| x as f32) + Vec3::new(48.0, 40.0, 12.0),
245 turret,
246 ),
247 (
248 excavation_site_pos.map(|x| x as f32) + Vec3::new(44.0, 50.0, 15.0),
249 lavathrower,
250 ),
251 (
253 excavation_site_pos.map(|x| x as f32) + Vec3::new(50.0, 18.0, 18.0),
254 turret,
255 ),
256 (
257 excavation_site_pos.map(|x| x as f32) + Vec3::new(50.0, 10.0, 18.0),
258 lavathrower,
259 ),
260 (
262 excavation_site_pos.map(|x| x as f32) + Vec3::new(-1.0, 44.0, 20.0),
263 lavathrower,
264 ),
265 (
266 excavation_site_pos.map(|x| x as f32) + Vec3::new(-1.0, 42.0, 20.0),
267 mine_guard,
268 ),
269 (
271 excavation_site_pos.map(|x| x as f32) + Vec3::new(-30.0, -12.0, 8.0),
272 irongolem_key,
273 ),
274 (
277 cleansing_room_pos.map(|x| x as f32) + Vec3::new(-10.0, -5.0, 10.0),
278 snaretongue_forge_key,
279 ),
280 (
281 cleansing_room_pos.map(|x| x as f32) + Vec3::new(-14.0, 60.0, -10.0),
282 snaretongue_miner_key,
283 ),
284 (
286 smelting_room_pos.map(|x| x as f32) + Vec3::new(-8.0, -2.0, 5.0),
287 lavathrower,
288 ),
289 (
290 smelting_room_pos.map(|x| x as f32) + Vec3::new(20.0, -10.0, 5.0),
291 lavathrower,
292 ),
293 (
296 forgemaster_pos.map(|x| x as f32) + Vec3::new(0.0, 4.0, 3.0),
297 irongolem,
298 ),
299 ];
300
301 let entity_group_positions = vec![
303 (
305 smelting_room_pos.map(|x| x as f32) + Vec3::new(26.0, -10.0, 5.0),
306 vec![iron_dwarf],
307 2,
308 5.0,
309 4.0,
310 ),
311 (
312 smelting_room_pos.map(|x| x as f32) + Vec3::new(6.0, 2.0, 12.0),
313 vec![iron_dwarf],
314 2,
315 5.0,
316 4.0,
317 ),
318 (
319 smelting_room_pos.map(|x| x as f32) + Vec3::new(8.0, -10.0, 18.0),
320 vec![miner, iron_dwarf, mine_guard],
321 4,
322 5.0,
323 4.0,
324 ),
325 (
327 hallway1_pos.map(|x| x as f32) + Vec3::new(-5.0, 2.0, 0.0),
328 vec![miner, iron_dwarf, mine_guard],
329 2,
330 5.0,
331 4.0,
332 ),
333 (
334 hallway1_pos.map(|x| x as f32) + Vec3::new(-20.0, 10.0, 0.0),
335 vec![miner, iron_dwarf, mine_guard],
336 2,
337 5.0,
338 4.0,
339 ),
340 (
341 hallway1_pos.map(|x| x as f32) + Vec3::new(4.0, 15.0, 0.0),
342 vec![miner, iron_dwarf, mine_guard],
343 2,
344 5.0,
345 4.0,
346 ),
347 (
348 hallway1_pos.map(|x| x as f32) + Vec3::new(-5.0, -8.0, 18.0),
349 vec![miner, iron_dwarf, mine_guard],
350 3,
351 5.0,
352 4.0,
353 ),
354 (
355 hallway1_pos.map(|x| x as f32) + Vec3::new(-5.0, -8.0, 30.0),
356 vec![miner, iron_dwarf, mine_guard],
357 3,
358 5.0,
359 4.0,
360 ),
361 (
362 hallway1_pos.map(|x| x as f32) + Vec3::new(-20.0, 18.0, 10.0),
363 vec![miner, iron_dwarf, mine_guard],
364 3,
365 5.0,
366 4.0,
367 ),
368 (
369 hallway1_pos.map(|x| x as f32) + Vec3::new(4.0, 15.0, 12.0),
370 vec![miner, iron_dwarf, mine_guard],
371 3,
372 5.0,
373 4.0,
374 ),
375 ];
376
377 for (position, entity_paths, num_entities, max_distance, entity_distance) in
378 entity_group_positions
379 {
380 spawn_entities(
381 position,
382 painter,
383 &entity_paths,
384 num_entities,
385 max_distance,
386 entity_distance,
387 );
388 }
389
390 for (pos, entity) in miner_pos {
391 spawn_entity(pos, painter, entity);
392 }
393 }
394}
395fn spawn_entity(pos: Vec3<f32>, painter: &Painter, entity_path: &str) {
396 let mut rng = rand::rng();
397 painter.spawn(
398 EntityInfo::at(pos)
399 .with_asset_expect(entity_path, &mut rng, None)
400 .with_no_flee(),
401 );
402}
403
404fn spawn_entities(
405 pos: Vec3<f32>,
406 painter: &Painter,
407 entity_paths: &[&str],
408 num_entities: u32,
409 max_distance: f32,
410 entity_distance: f32,
411) {
412 let mut rng = rand::rng();
413 let num_paths = entity_paths.len();
414
415 let side_length = (num_entities as f32).sqrt().ceil() as u32;
416 let spacing = entity_distance;
417
418 for i in 0..num_entities {
419 let path_index = rng.random_range(0..num_paths);
420 let entity_path = entity_paths[path_index];
421
422 let row = i / side_length;
423 let col = i % side_length;
424
425 let x_offset = col as f32 * spacing - max_distance;
426 let y_offset = row as f32 * spacing - max_distance;
427
428 let spawn_pos = pos + Vec3::new(x_offset, y_offset, 0.0);
429
430 painter.spawn(EntityInfo::at(spawn_pos).with_asset_expect(entity_path, &mut rng, None));
431 }
432}
433
434fn spawn_random_entity(pos: Vec3<i32>, painter: &Painter, rot: u8) {
435 let mut rng = rand::rng();
436
437 let entities = [
438 "common.entity.dungeon.dwarven_quarry.miner",
439 "common.entity.dungeon.dwarven_quarry.turret",
440 "common.entity.dungeon.dwarven_quarry.iron_dwarf",
441 ];
442
443 let sprites = [
444 SpriteKind::Anvil,
445 SpriteKind::Forge,
446 SpriteKind::RepairBench,
447 SpriteKind::DungeonChest5,
448 ];
449
450 let random_number = rng.random_range(0..=10);
451
452 if random_number <= 3 {
453 let pos_f32 = pos.map(|coord| coord as f32);
454 let random_entity_index = rng.random_range(0..entities.len());
455 let random_entity = entities[random_entity_index];
456 spawn_entity(pos_f32, painter, random_entity);
457 } else if random_number <= 9 {
458 let random_sprite_index = rng.random_range(0..sprites.len());
459 let random_sprite = sprites[random_sprite_index];
460 painter.rotated_sprite(pos, random_sprite, rot);
461 }
462}