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    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        // Entrance
64        let entrance_path = "site_structures.dwarves.entrance";
65        let entrance_pos: Vec3<i32> = (center.x, center.y, self.alt - 37).into(); //+2
66        render_prefab(entrance_path, entrance_pos, painter);
67        // Hallway 0
68        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        // Hallway 1
72        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        // Mining
76        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        // After Mining
80        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        // Hallway 2
88        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        // Excavation Site
92        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        // Forgemaster Boss
96        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        // Cleansing Room
100        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        // Smelting Room
104        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        // Forgemaster Room
108        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        // Spawn random NPCs/Rotated Sprites
113        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        //Entities
124        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            // Entrance
139            (
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            // Hallway0
148            (
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            // Hallway1
161            (
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            // Mining
170            (
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            // After Mining
207            // Hallway 2
208            (
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            // Excavation Site
221            (
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            // lavathrower on wood railing
234            (
235                excavation_site_pos.map(|x| x as f32) + Vec3::new(0.0, -25.0, 18.0),
236                lavathrower,
237            ),
238            // lavathrower, turret & miner on corner platforms
239            (
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            // lavathrower and turret on ridge (forgemaster side)
252            (
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            // Lavaroom Exit
261            (
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            // Iron Golem
270            (
271                excavation_site_pos.map(|x| x as f32) + Vec3::new(-30.0, -12.0, 8.0),
272                irongolem_key,
273            ),
274            // Cleansing Room
275            // Snaretongue
276            (
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            // Smelting Room
285            (
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            // Treasure Room
294            // Iron Golem
295            (
296                forgemaster_pos.map(|x| x as f32) + Vec3::new(0.0, 4.0, 3.0),
297                irongolem,
298            ),
299        ];
300
301        // Entity Groups
302        let entity_group_positions = vec![
303            // Smelting Room
304            (
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            // Staircase Back up
326            (
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}