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