Skip to main content

veloren_world/site/plot/
dwarven_mine.rs

1use super::*;
2use crate::{Land, util::within_distance};
3use generation::render_prefab;
4use rand::prelude::*;
5use vek::*;
6
7const TILE_SIZE: i32 = 13;
8
9/// Represents house data generated by the `generate()` method
10pub struct DwarvenMine {
11    name: String,
12    /// Axis aligned bounding region for the house
13    bounds: Aabr<i32>,
14    /// Approximate altitude of the door tile
15    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        // TODO: i18n for the word "mine"
35
36        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        // Entrance
69        let entrance_path = "site_structures.dwarves.entrance";
70        let entrance_pos: Vec3<i32> = (center.x, center.y, self.alt - 37).into(); //+2
71        render_prefab(entrance_path, entrance_pos, painter);
72        // Hallway 0
73        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        // Hallway 1
77        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        // Mining
81        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        // After Mining
85        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        // Hallway 2
93        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        // Excavation Site
97        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        // Forgemaster Boss
101        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        // Cleansing Room
105        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        // Smelting Room
109        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        // Forgemaster Room
113        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        // Spawn random NPCs/Rotated Sprites
118        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        //Entities
129        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            // Entrance
144            (
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            // Hallway0
153            (
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            // Hallway1
166            (
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            // Mining
175            (
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            // After Mining
212            // Hallway 2
213            (
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            // Excavation Site
226            (
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            // lavathrower on wood railing
239            (
240                excavation_site_pos.map(|x| x as f32) + Vec3::new(0.0, -25.0, 18.0),
241                lavathrower,
242            ),
243            // lavathrower, turret & miner on corner platforms
244            (
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            // lavathrower and turret on ridge (forgemaster side)
257            (
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            // Lavaroom Exit
266            (
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            // Iron Golem
275            (
276                excavation_site_pos.map(|x| x as f32) + Vec3::new(-30.0, -12.0, 8.0),
277                irongolem_key,
278            ),
279            // Cleansing Room
280            // Snaretongue
281            (
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            // Smelting Room
290            (
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            // Treasure Room
299            // Iron Golem
300            (
301                forgemaster_pos.map(|x| x as f32) + Vec3::new(0.0, 4.0, 3.0),
302                irongolem,
303            ),
304        ];
305
306        // Entity Groups
307        let entity_group_positions = vec![
308            // Smelting Room
309            (
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            // Staircase Back up
331            (
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}