veloren_world/site2/plot/
dwarven_mine.rs

1/*  Credits:
2Zesterer: Code, Testing
3Isse: Code
4Gemu: Sprite-Art/NPC-Design and 3D Models
5Floppy: NPC-Integration, Combat-Skill, Code and Animations
6Sam: NPC Combat-Skills
7Pfau: Level Design, NPC Concepts, Story, Code
8Dialex: Testing, Site-Icon
9Froggy: Testing
10Imbris: Honorable Mention
11ChatGPT: Improving Code Quality, Speeding up finding Syntax Errors
12*/
13
14use super::*;
15use crate::Land;
16use gen::render_prefab;
17use rand::prelude::*;
18use vek::*;
19
20const TILE_SIZE: i32 = 13;
21
22/// Represents house data generated by the `generate()` method
23pub struct DwarvenMine {
24    name: String,
25    /// Axis aligned bounding region for the house
26    bounds: Aabr<i32>,
27    /// Approximate altitude of the door tile
28    pub(crate) alt: i32,
29    origin: Vec2<i32>,
30}
31
32impl DwarvenMine {
33    pub fn generate(
34        land: &Land,
35        rng: &mut impl Rng,
36        site: &Site,
37        wpos: Vec2<i32>,
38        tile_aabr: Aabr<i32>,
39    ) -> Self {
40        let bounds = Aabr {
41            min: site.tile_wpos(tile_aabr.min),
42            max: site.tile_wpos(tile_aabr.max),
43        };
44
45        let name = format!("{} Mine", NameGen::location(rng).generate_mine());
46
47        // TODO: i18n for the word "mine"
48
49        Self {
50            name,
51            bounds,
52            origin: wpos - TILE_SIZE / 2,
53            alt: land.get_alt_approx(site.tile_center_wpos(wpos)) as i32,
54        }
55    }
56
57    pub fn name(&self) -> &str { &self.name }
58
59    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
60        let near_entrance = wpos.distance_squared(self.origin) < 64i32.pow(2);
61        SpawnRules {
62            trees: !near_entrance,
63            max_warp: (!near_entrance) as i32 as f32,
64            ..SpawnRules::default()
65        }
66    }
67}
68
69impl Structure for DwarvenMine {
70    #[cfg(feature = "use-dyn-lib")]
71    const UPDATE_FN: &'static [u8] = b"render_mine\0";
72
73    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_mine")]
74
75    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
76        let center = self.bounds.center();
77        // Entrance
78        let entrance_path = "site_structures.dwarves.entrance";
79        let entrance_pos: Vec3<i32> = (center.x, center.y, self.alt - 37).into(); //+2
80        render_prefab(entrance_path, entrance_pos, painter);
81        // Hallway 0
82        let hallway0_path = "site_structures.dwarves.hallway";
83        let hallway0_pos: Vec3<i32> = entrance_pos + Vec3::new(-67, 8, -37);
84        render_prefab(hallway0_path, hallway0_pos + Vec3::new(0, 0, 0), painter);
85        // Hallway 1
86        let hallway1_path = "site_structures.dwarves.hallway1";
87        let hallway1_pos: Vec3<i32> = hallway0_pos + Vec3::new(-55, -7, -33);
88        render_prefab(hallway1_path, hallway1_pos, painter);
89        // Mining
90        let mining_site_path = "site_structures.dwarves.mining_site";
91        let mining_site_pos: Vec3<i32> = hallway1_pos + Vec3::new(-5, 28, 21);
92        render_prefab(mining_site_path, mining_site_pos, painter);
93        // After Mining
94        let after_mining_path = "site_structures.dwarves.after_forgemaster";
95        let after_mining_pos: Vec3<i32> = mining_site_pos + Vec3::new(-12, -9, -3);
96        render_prefab(
97            after_mining_path,
98            after_mining_pos + Vec3::new(0, -2, 0),
99            painter,
100        );
101        // Hallway 2
102        let hallway2_path = "site_structures.dwarves.hallway2";
103        let hallway2_pos: Vec3<i32> = after_mining_pos + Vec3::new(-3, 35, -18);
104        render_prefab(hallway2_path, hallway2_pos, painter);
105        // Excavation Site
106        let excavation_site_path = "site_structures.dwarves.excavation_site";
107        let excavation_site_pos: Vec3<i32> = hallway2_pos + Vec3::new(-35, 83, -10);
108        render_prefab(excavation_site_path, excavation_site_pos, painter);
109        // Forgemaster Boss
110        let forgemaster_boss_path = "site_structures.dwarves.forgemaster_boss";
111        let forgemaster_boss_pos: Vec3<i32> = excavation_site_pos + Vec3::new(115, 0, -17);
112        render_prefab(forgemaster_boss_path, forgemaster_boss_pos, painter);
113        // Cleansing Room
114        let cleansing_room_path = "site_structures.dwarves.cleansing_room";
115        let cleansing_room_pos: Vec3<i32> = excavation_site_pos + Vec3::new(-47, -125, 6);
116        render_prefab(cleansing_room_path, cleansing_room_pos, painter);
117        // Smelting Room
118        let smelting_room_path = "site_structures.dwarves.smelting_room";
119        let smelting_room_pos: Vec3<i32> = cleansing_room_pos + Vec3::new(49, -6, 0);
120        render_prefab(smelting_room_path, smelting_room_pos, painter);
121        // Forgemaster Room
122        let forgemaster_path = "site_structures.dwarves.forgemaster_room";
123        let forgemaster_pos: Vec3<i32> = smelting_room_pos + Vec3::new(8, 36, 10);
124        render_prefab(forgemaster_path, forgemaster_pos, painter);
125
126        // Spawn random NPCs/Rotated Sprites
127        let random_entities: Vec<(Vec3<i32>, u8)> = vec![
128            (forgemaster_pos + Vec3::new(11, 20, 1), 0),
129            (forgemaster_pos + Vec3::new(0, 20, 1), 0),
130            (forgemaster_pos + Vec3::new(-11, 20, 1), 0),
131        ];
132
133        for (pos, rot) in random_entities {
134            spawn_random_entity(pos, painter, rot);
135        }
136
137        //Entities
138        let miner = "common.entity.dungeon.dwarven_quarry.miner";
139        let iron_dwarf = "common.entity.dungeon.dwarven_quarry.iron_dwarf";
140        let turret = "common.entity.dungeon.dwarven_quarry.turret";
141        let lavathrower = "common.entity.dungeon.dwarven_quarry.lavathrower";
142        let mine_guard = "common.entity.dungeon.dwarven_quarry.mine_guard";
143        let flamekeeper = "common.entity.dungeon.dwarven_quarry.flamekeeper";
144        let forgemaster = "common.entity.dungeon.dwarven_quarry.forgemaster";
145        let irongolem = "common.entity.dungeon.dwarven_quarry.irongolem";
146        let irongolem_key = "common.entity.dungeon.dwarven_quarry.irongolem_key";
147        let snaretongue_forge_key = "common.entity.dungeon.dwarven_quarry.snaretongue_forge_key";
148        let snaretongue_miner_key = "common.entity.dungeon.dwarven_quarry.snaretongue_miner_key";
149
150        let entrance_offset: Vec3<f32> = (20.0, -20.0, 45.0).into();
151        let miner_pos: Vec<(Vec3<f32>, &str)> = vec![
152            // Entrance
153            (
154                (entrance_pos + Vec3::new(-6, 0, 0)).map(|x| x as f32) + entrance_offset,
155                miner,
156            ),
157            (
158                (entrance_pos + Vec3::new(-2, 0, 0)).map(|x| x as f32) + entrance_offset,
159                miner,
160            ),
161            // Hallway0
162            (
163                hallway0_pos.map(|x| x as f32) + Vec3::new(10.0, -4.0, 16.0),
164                miner,
165            ),
166            (
167                hallway0_pos.map(|x| x as f32) + Vec3::new(-10.0, -4.0, 10.0),
168                miner,
169            ),
170            (
171                hallway0_pos.map(|x| x as f32) + Vec3::new(-10.0, -6.0, 10.0),
172                miner,
173            ),
174            // Hallway1
175            (
176                hallway0_pos.map(|x| x as f32) + Vec3::new(-30.0, -4.0, 0.0),
177                miner,
178            ),
179            (
180                hallway0_pos.map(|x| x as f32) + Vec3::new(-46.0, -4.0, -5.0),
181                miner,
182            ),
183            // Mining
184            (
185                mining_site_pos.map(|x| x as f32) + Vec3::new(14.0, 17.0, 10.0),
186                flamekeeper,
187            ),
188            (
189                mining_site_pos.map(|x| x as f32) + Vec3::new(14.0, 27.0, 8.0),
190                mine_guard,
191            ),
192            (
193                mining_site_pos.map(|x| x as f32) + Vec3::new(-4.0, 32.0, 8.0),
194                turret,
195            ),
196            (
197                mining_site_pos.map(|x| x as f32) + Vec3::new(32.0, 32.0, 10.0),
198                turret,
199            ),
200            (
201                mining_site_pos.map(|x| x as f32) + Vec3::new(14.0, 32.0, 10.0),
202                mine_guard,
203            ),
204            (
205                mining_site_pos.map(|x| x as f32) + Vec3::new(-8.0, 0.0, 10.0),
206                miner,
207            ),
208            (
209                mining_site_pos.map(|x| x as f32) + Vec3::new(36.0, 0.0, 10.0),
210                miner,
211            ),
212            (
213                mining_site_pos.map(|x| x as f32) + Vec3::new(-8.0, 15.0, 0.0),
214                iron_dwarf,
215            ),
216            (
217                mining_site_pos.map(|x| x as f32) + Vec3::new(36.0, 15.0, 0.0),
218                iron_dwarf,
219            ),
220            // After Mining
221            // Hallway 2
222            (
223                hallway2_pos.map(|x| x as f32) + Vec3::new(-4.0, -22.0, 20.0),
224                miner,
225            ),
226            (
227                hallway2_pos.map(|x| x as f32) + Vec3::new(-4.0, 7.0, 10.0),
228                miner,
229            ),
230            (
231                hallway2_pos.map(|x| x as f32) + Vec3::new(-5.0, 30.0, 0.0),
232                mine_guard,
233            ),
234            // Excavation Site
235            (
236                hallway2_pos.map(|x| x as f32) + Vec3::new(0.0, 44.0, 0.0),
237                iron_dwarf,
238            ),
239            (
240                hallway2_pos.map(|x| x as f32) + Vec3::new(2.0, 44.0, 0.0),
241                lavathrower,
242            ),
243            (
244                forgemaster_boss_pos.map(|x| x as f32) + Vec3::new(23.0, 1.0, 20.0),
245                forgemaster,
246            ),
247            // lavathrower on wood railing
248            (
249                excavation_site_pos.map(|x| x as f32) + Vec3::new(0.0, -25.0, 18.0),
250                lavathrower,
251            ),
252            // lavathrower, turret & miner on corner platforms
253            (
254                excavation_site_pos.map(|x| x as f32) + Vec3::new(50.0, 42.0, 25.0),
255                mine_guard,
256            ),
257            (
258                excavation_site_pos.map(|x| x as f32) + Vec3::new(48.0, 40.0, 12.0),
259                turret,
260            ),
261            (
262                excavation_site_pos.map(|x| x as f32) + Vec3::new(44.0, 50.0, 15.0),
263                lavathrower,
264            ),
265            // lavathrower and turret on ridge (forgemaster side)
266            (
267                excavation_site_pos.map(|x| x as f32) + Vec3::new(50.0, 18.0, 18.0),
268                turret,
269            ),
270            (
271                excavation_site_pos.map(|x| x as f32) + Vec3::new(50.0, 10.0, 18.0),
272                lavathrower,
273            ),
274            // Lavaroom Exit
275            (
276                excavation_site_pos.map(|x| x as f32) + Vec3::new(-1.0, 44.0, 20.0),
277                lavathrower,
278            ),
279            (
280                excavation_site_pos.map(|x| x as f32) + Vec3::new(-1.0, 42.0, 20.0),
281                mine_guard,
282            ),
283            // Iron Golem
284            (
285                excavation_site_pos.map(|x| x as f32) + Vec3::new(-30.0, -12.0, 8.0),
286                irongolem_key,
287            ),
288            // Cleansing Room
289            // Snaretongue
290            (
291                cleansing_room_pos.map(|x| x as f32) + Vec3::new(-10.0, -5.0, 10.0),
292                snaretongue_forge_key,
293            ),
294            (
295                cleansing_room_pos.map(|x| x as f32) + Vec3::new(-14.0, 60.0, -10.0),
296                snaretongue_miner_key,
297            ),
298            // Smelting Room
299            (
300                smelting_room_pos.map(|x| x as f32) + Vec3::new(-8.0, -2.0, 5.0),
301                lavathrower,
302            ),
303            (
304                smelting_room_pos.map(|x| x as f32) + Vec3::new(20.0, -10.0, 5.0),
305                lavathrower,
306            ),
307            // Treasure Room
308            // Iron Golem
309            (
310                forgemaster_pos.map(|x| x as f32) + Vec3::new(0.0, 4.0, 3.0),
311                irongolem,
312            ),
313        ];
314
315        // Entity Groups
316        let entity_group_positions = vec![
317            // Smelting Room
318            (
319                smelting_room_pos.map(|x| x as f32) + Vec3::new(26.0, -10.0, 5.0),
320                vec![iron_dwarf],
321                2,
322                5.0,
323                4.0,
324            ),
325            (
326                smelting_room_pos.map(|x| x as f32) + Vec3::new(6.0, 2.0, 12.0),
327                vec![iron_dwarf],
328                2,
329                5.0,
330                4.0,
331            ),
332            (
333                smelting_room_pos.map(|x| x as f32) + Vec3::new(8.0, -10.0, 18.0),
334                vec![miner, iron_dwarf, mine_guard],
335                4,
336                5.0,
337                4.0,
338            ),
339            // Staircase Back up
340            (
341                hallway1_pos.map(|x| x as f32) + Vec3::new(-5.0, 2.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(-20.0, 10.0, 0.0),
349                vec![miner, iron_dwarf, mine_guard],
350                2,
351                5.0,
352                4.0,
353            ),
354            (
355                hallway1_pos.map(|x| x as f32) + Vec3::new(4.0, 15.0, 0.0),
356                vec![miner, iron_dwarf, mine_guard],
357                2,
358                5.0,
359                4.0,
360            ),
361            (
362                hallway1_pos.map(|x| x as f32) + Vec3::new(-5.0, -8.0, 18.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(-5.0, -8.0, 30.0),
370                vec![miner, iron_dwarf, mine_guard],
371                3,
372                5.0,
373                4.0,
374            ),
375            (
376                hallway1_pos.map(|x| x as f32) + Vec3::new(-20.0, 18.0, 10.0),
377                vec![miner, iron_dwarf, mine_guard],
378                3,
379                5.0,
380                4.0,
381            ),
382            (
383                hallway1_pos.map(|x| x as f32) + Vec3::new(4.0, 15.0, 12.0),
384                vec![miner, iron_dwarf, mine_guard],
385                3,
386                5.0,
387                4.0,
388            ),
389        ];
390
391        for (position, entity_paths, num_entities, max_distance, entity_distance) in
392            entity_group_positions
393        {
394            spawn_entities(
395                position,
396                painter,
397                &entity_paths,
398                num_entities,
399                max_distance,
400                entity_distance,
401            );
402        }
403
404        for (pos, entity) in miner_pos {
405            spawn_entity(pos, painter, entity);
406        }
407    }
408}
409fn spawn_entity(pos: Vec3<f32>, painter: &Painter, entity_path: &str) {
410    let mut rng = thread_rng();
411    painter.spawn(
412        EntityInfo::at(pos)
413            .with_asset_expect(entity_path, &mut rng, None)
414            .with_no_flee(),
415    );
416}
417
418fn spawn_entities(
419    pos: Vec3<f32>,
420    painter: &Painter,
421    entity_paths: &[&str],
422    num_entities: u32,
423    max_distance: f32,
424    entity_distance: f32,
425) {
426    let mut rng = thread_rng();
427    let num_paths = entity_paths.len();
428
429    let side_length = (num_entities as f32).sqrt().ceil() as u32;
430    let spacing = entity_distance;
431
432    for i in 0..num_entities {
433        let path_index = rng.gen_range(0..num_paths);
434        let entity_path = entity_paths[path_index];
435
436        let row = i / side_length;
437        let col = i % side_length;
438
439        let x_offset = col as f32 * spacing - max_distance;
440        let y_offset = row as f32 * spacing - max_distance;
441
442        let spawn_pos = pos + Vec3::new(x_offset, y_offset, 0.0);
443
444        painter.spawn(EntityInfo::at(spawn_pos).with_asset_expect(entity_path, &mut rng, None));
445    }
446}
447
448fn spawn_random_entity(pos: Vec3<i32>, painter: &Painter, rot: u8) {
449    let mut rng = thread_rng();
450
451    let entities = [
452        "common.entity.dungeon.dwarven_quarry.miner",
453        "common.entity.dungeon.dwarven_quarry.turret",
454        "common.entity.dungeon.dwarven_quarry.iron_dwarf",
455    ];
456
457    let sprites = [
458        SpriteKind::Anvil,
459        SpriteKind::Forge,
460        SpriteKind::RepairBench,
461        SpriteKind::DungeonChest5,
462    ];
463
464    let random_number = rng.gen_range(0..=10);
465
466    if random_number <= 3 {
467        let pos_f32 = pos.map(|coord| coord as f32);
468        let random_entity_index = rng.gen_range(0..entities.len());
469        let random_entity = entities[random_entity_index];
470        spawn_entity(pos_f32, painter, random_entity);
471    } else if random_number <= 9 {
472        let random_sprite_index = rng.gen_range(0..sprites.len());
473        let random_sprite = sprites[random_sprite_index];
474        painter.rotated_sprite(pos, random_sprite, rot);
475    } else {
476        return;
477    }
478}