veloren_world/site2/plot/
haniwa.rs

1use super::*;
2use crate::{
3    Land,
4    assets::AssetHandle,
5    site2::gen::{PrimitiveTransform, place_circular, place_circular_as_vec},
6    util::{DIAGONALS, LOCALITY, NEIGHBORS, RandomField, sampler::Sampler},
7};
8use common::{
9    generation::EntityInfo,
10    terrain::{Structure as PrefabStructure, StructuresGroup},
11};
12use lazy_static::lazy_static;
13use rand::prelude::*;
14use std::{f32::consts::PI, sync::Arc};
15use vek::*;
16
17pub struct Haniwa {
18    base: i32,
19    diameter: i32,
20    tree_pos: Vec3<i32>,
21    room_size: i32,
22    rotation: f32,
23    pub(crate) alt: i32,
24    pub(crate) center: Vec2<i32>,
25    pub(crate) entrance_pos: Vec3<i32>,
26    pub(crate) mob_room_positions: Vec<Vec3<i32>>,
27    pub(crate) center_room_positions: Vec<Vec3<i32>>,
28    pub(crate) room_positions: Vec<Vec3<i32>>,
29    pub(crate) boss_room_position: Vec3<i32>,
30    pub(crate) mini_boss_room_positions: Vec<Vec3<i32>>,
31}
32impl Haniwa {
33    pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
34        let bounds = Aabr {
35            min: site.tile_wpos(tile_aabr.min),
36            max: site.tile_wpos(tile_aabr.max),
37        };
38        let center = bounds.center();
39        let base = land.get_alt_approx(center) as i32;
40        let diameter = (150 + RandomField::new(0).get(center.with_z(base)) % 10) as i32;
41        let dir_select = (RandomField::new(0).get(center.with_z(base)) % 4) as usize;
42        let rotation = (PI / 2.0) * dir_select as f32;
43        let entrance_dir = CARDINALS[dir_select];
44        let tree_dir = -entrance_dir;
45        let entrance_pos = (center + (entrance_dir * (2 * (diameter / 3)))).with_z(base);
46        let tree_distance = diameter / 2;
47        let tree_pos = (center + (tree_dir * tree_distance)).with_z(base);
48        let room_size = diameter / 4;
49        let mut floors = vec![];
50        for f in 1..=3 {
51            let floor = base - ((diameter / 5) * f) - 2;
52            floors.push(floor)
53        }
54        let mut mob_room_positions = vec![];
55        let mut mini_boss_room_positions = vec![];
56        let mut center_room_positions = vec![];
57        let mut room_positions = vec![];
58        for floor in floors.iter().take(floors.len() - 1) {
59            let (room_distribution, room_distance) =
60                if RandomField::new(0).get(center.with_z(*floor)) % 2 > 0 {
61                    (DIAGONALS, (3 * (room_size / 2)) + 2)
62                } else {
63                    (CARDINALS, 2 * (room_size - 2))
64                };
65            for dir in room_distribution {
66                let room_center = center + dir * room_distance;
67                mob_room_positions.push(room_center.with_z(floor - (room_size / 4) + 1));
68                room_positions.push(room_center.with_z(floor - (room_size / 4) + 1));
69            }
70            mini_boss_room_positions.push(center.with_z(floor - (room_size / 4) + 1));
71        }
72        for floor in &floors {
73            center_room_positions.push(center.with_z(floor - (room_size / 4) + 1));
74            room_positions.push(center.with_z(floor - (room_size / 4) + 1));
75        }
76        let boss_room_position = center_room_positions[center_room_positions.len() - 1];
77        Self {
78            alt: land.get_alt_approx(site.tile_center_wpos(tile_aabr.center())) as i32 + 2,
79            center,
80            base,
81            diameter,
82            entrance_pos,
83            tree_pos,
84            room_size,
85            rotation,
86            room_positions,
87            mob_room_positions,
88            center_room_positions,
89            boss_room_position,
90            mini_boss_room_positions,
91        }
92    }
93
94    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
95        SpawnRules {
96            waypoints: false,
97            trees: wpos.distance_squared(self.center) > (self.diameter / 2).pow(2),
98            ..SpawnRules::default()
99        }
100    }
101}
102
103impl Structure for Haniwa {
104    #[cfg(feature = "use-dyn-lib")]
105    const UPDATE_FN: &'static [u8] = b"render_haniwa\0";
106
107    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_haniwa")]
108    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
109        let center = self.center;
110        let base = self.base;
111        let diameter = self.diameter;
112        let entrance = self.entrance_pos;
113        let mut thread_rng = thread_rng();
114        let rock = Fill::Brick(BlockKind::Rock, Rgb::new(96, 123, 131), 24);
115        let key_door = Fill::Block(Block::air(SpriteKind::HaniwaKeyDoor));
116        let key_hole = Fill::Block(Block::air(SpriteKind::HaniwaKeyhole));
117        let trap = Fill::Block(Block::air(SpriteKind::HaniwaTrap));
118        let rock_broken = Fill::Sampling(Arc::new(|center| {
119            Some(match (RandomField::new(0).get(center)) % 48 {
120                0..=8 => Block::new(BlockKind::Rock, Rgb::new(97, 124, 134)),
121                9..=17 => Block::new(BlockKind::Rock, Rgb::new(92, 118, 128)),
122                18..=26 => Block::new(BlockKind::Rock, Rgb::new(85, 111, 121)),
123                27..=35 => Block::new(BlockKind::Rock, Rgb::new(82, 108, 117)),
124                36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
125                _ => Block::new(BlockKind::Rock, Rgb::new(96, 123, 131)),
126            })
127        }));
128        let lanterns = Fill::Sampling(Arc::new(|center| {
129            Some(match (RandomField::new(0).get(center)) % 200 {
130                0 => Block::air(SpriteKind::FireBowlGround),
131                _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
132            })
133        }));
134        let iron_spikes = Fill::Block(Block::air(SpriteKind::IronSpike));
135        let rock_iron_spikes = Fill::Sampling(Arc::new(|center| {
136            Some(match (RandomField::new(0).get(center)) % 100 {
137                0..=8 => Block::new(BlockKind::Rock, Rgb::new(97, 124, 134)),
138                9..=17 => Block::new(BlockKind::Rock, Rgb::new(92, 118, 128)),
139                18..=26 => Block::new(BlockKind::Rock, Rgb::new(85, 111, 121)),
140                27..=35 => Block::new(BlockKind::Rock, Rgb::new(82, 108, 117)),
141                36..=40 => Block::new(BlockKind::Rock, Rgb::new(96, 123, 131)),
142                _ => Block::air(SpriteKind::IronSpike),
143            })
144        }));
145        let grass = Fill::Brick(BlockKind::Rock, Rgb::new(72, 87, 22), 24);
146        // room npcs
147        let npcs = [
148            "common.entity.dungeon.haniwa.guard",
149            "common.entity.dungeon.haniwa.soldier",
150        ];
151        let height_handle = diameter / 8;
152        let cone_length = 8;
153        let cone_radius = (diameter / 2) as f32;
154        let cones = diameter / 4;
155        let cone_positions = place_circular(center, cone_radius, cones);
156        // tree platform
157        let outside_radius = cone_radius as i32;
158        let tree_pos = Vec2::new(self.tree_pos.x, self.tree_pos.y);
159        let platform_size = 23;
160        painter
161            .cylinder(Aabb {
162                min: (tree_pos - platform_size).with_z(base - 5),
163                max: (tree_pos + platform_size).with_z(base - 10 + platform_size - 1),
164            })
165            .fill(rock_broken.clone());
166        painter
167            .cylinder(Aabb {
168                min: (tree_pos - platform_size).with_z(base - 10 + platform_size - 1),
169                max: (tree_pos + platform_size).with_z(base - 10 + platform_size),
170            })
171            .fill(grass.clone());
172        let carve_positions = place_circular(tree_pos, platform_size as f32, 15);
173        for carve_pos in carve_positions {
174            painter
175                .line(
176                    carve_pos.with_z(base + 2),
177                    carve_pos.with_z(base - 5 + platform_size),
178                    3.5,
179                )
180                .clear();
181        }
182        // gangway
183        painter
184            .ramp(
185                Aabb {
186                    min: Vec2::new(center.x - outside_radius, center.y - 16).with_z(base + 10),
187                    max: Vec2::new(center.x + (outside_radius / 2) + 8, center.y + 16)
188                        .with_z(base + 28),
189                },
190                Dir::X,
191            )
192            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
193            .fill(grass.clone());
194        painter
195            .ramp(
196                Aabb {
197                    min: Vec2::new(center.x - outside_radius, center.y - 16).with_z(base + 9),
198                    max: Vec2::new(center.x + (outside_radius / 2) + 8, center.y + 16)
199                        .with_z(base + 27),
200                },
201                Dir::X,
202            )
203            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
204            .fill(rock_broken.clone());
205
206        let clear_dist_x = outside_radius / 4;
207        let clear_dist_y = (2 * outside_radius) + (outside_radius / 10);
208        let clear_radius = 2 * outside_radius;
209        let clear_limiter = painter
210            .aabb(Aabb {
211                min: Vec2::new(center.x - outside_radius, center.y - 16).with_z(base + 9),
212                max: Vec2::new(center.x + (outside_radius / 2) + 8, center.y + 16)
213                    .with_z(base + 28),
214            })
215            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base));
216        for c in 0..=1 {
217            let clear_pos = Vec2::new(
218                center.x - clear_dist_x,
219                center.y - clear_dist_y + (c * (clear_dist_y * 2)),
220            );
221            painter
222                .cylinder(Aabb {
223                    min: (clear_pos - clear_radius).with_z(base + 9),
224                    max: (clear_pos + clear_radius).with_z(base + 28),
225                })
226                .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
227                .intersect(clear_limiter)
228                .clear();
229        }
230
231        // decor cones
232        for position in cone_positions {
233            for dir in LOCALITY {
234                let cone_pos = position + (dir * 2);
235                let cone_var = 10 + RandomField::new(0).get(cone_pos.with_z(base)) as i32 % 10;
236                painter
237                    .cone_with_radius(
238                        cone_pos.with_z(base),
239                        (cone_length / 3) as f32,
240                        (cone_length + cone_var) as f32,
241                    )
242                    .fill(rock_broken.clone());
243            }
244        }
245        // repaint platform grass layer
246        painter
247            .cylinder(Aabb {
248                min: (tree_pos - platform_size + 5).with_z(base - 10 + platform_size - 1),
249                max: (tree_pos + platform_size - 5).with_z(base - 10 + platform_size),
250            })
251            .fill(grass.clone());
252        // clear platform upwards
253        painter
254            .cylinder(Aabb {
255                min: (tree_pos - platform_size).with_z(base - 10 + platform_size),
256                max: (tree_pos + platform_size).with_z(base + platform_size),
257            })
258            .clear();
259        // main sphere
260        let sphere_limiter = painter.aabb(Aabb {
261            min: (center - diameter - 1).with_z(base - 2),
262            max: (center + diameter + 1).with_z(base + height_handle + 1),
263        });
264        painter
265            .sphere(Aabb {
266                min: (center - diameter - 1).with_z(base - (2 * diameter) + height_handle - 1),
267                max: (center + diameter + 1).with_z(base + height_handle + 1),
268            })
269            .intersect(sphere_limiter)
270            .fill(grass.clone());
271        painter
272            .sphere(Aabb {
273                min: (center - diameter + 1).with_z(base - (2 * diameter) + height_handle + 1),
274                max: (center + diameter - 1).with_z(base + height_handle - 1),
275            })
276            .intersect(sphere_limiter)
277            .fill(rock.clone());
278
279        // decor grass ring
280        let ring_radius = cone_radius as i32 + 4;
281        painter
282            .cylinder(Aabb {
283                min: (center - ring_radius).with_z(base),
284                max: (center + ring_radius).with_z(base + 4),
285            })
286            .fill(grass.clone());
287        let beams = cones + 8;
288        let beam_start_radius = cone_radius + 6_f32;
289        let beam_end_radius = cone_radius + 20_f32;
290        let beam_start = place_circular_as_vec(center, beam_start_radius, beams);
291        let beam_end = place_circular_as_vec(center, beam_end_radius, beams);
292        let room_size = self.room_size;
293
294        for b in 0..beams {
295            painter
296                .line(
297                    beam_start[b as usize].with_z(base + 2),
298                    beam_end[b as usize].with_z(base + 1),
299                    2.5,
300                )
301                .fill(grass.clone());
302        }
303        // entrance terrain clear
304        let entrance_clear = Vec2::new(entrance.x, entrance.y);
305        for c in 0..8 {
306            painter
307                .aabb(Aabb {
308                    min: (entrance_clear - 4 - c).with_z(base + c),
309                    max: (entrance_clear + 4 + c).with_z(base + 1 + c),
310                })
311                .clear();
312        }
313        // entrance tunnel
314        painter
315            .vault(
316                Aabb {
317                    min: Vec2::new(center.x + (diameter / 4), center.y - 12).with_z(base - 5),
318                    max: Vec2::new(center.x + (diameter / 2) + 9, center.y + 12).with_z(base + 22),
319                },
320                Dir::X,
321            )
322            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
323            .fill(rock_broken.clone());
324        for v in 1..=5 {
325            painter
326                .vault(
327                    Aabb {
328                        min: Vec2::new(center.x + (diameter / 2) + 8 + v, center.y - 10 + v)
329                            .with_z(base - 5),
330                        max: Vec2::new(center.x + (diameter / 2) + 9 + v, center.y + 10 - v)
331                            .with_z(base + 22 - v),
332                    },
333                    Dir::X,
334                )
335                .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
336                .fill(rock_broken.clone());
337        }
338        painter
339            .vault(
340                Aabb {
341                    min: Vec2::new(center.x + (diameter / 4), center.y - 4).with_z(base),
342                    max: Vec2::new(center.x + (diameter / 2) + 25, center.y + 4).with_z(base + 16),
343                },
344                Dir::X,
345            )
346            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
347            .clear();
348        // entrance lanterns
349        painter
350            .aabb(Aabb {
351                min: Vec2::new(center.x + (diameter / 4), center.y - 4).with_z(base),
352                max: Vec2::new(center.x + (diameter / 2) + 15, center.y + 4).with_z(base + 1),
353            })
354            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
355            .fill(lanterns.clone());
356        // floor 0
357        painter
358            .cylinder(Aabb {
359                min: (center - beam_end_radius as i32 - 3).with_z(base - (diameter / 4) - 3),
360                max: (center + beam_end_radius as i32 + 3).with_z(base),
361            })
362            .fill(rock.clone());
363        // entrance trap
364        painter
365            .cylinder(Aabb {
366                min: Vec2::new(center.x + (diameter / 3) - 3, center.y - 2).with_z(base - 1),
367                max: Vec2::new(center.x + (diameter / 3) + 3, center.y + 3).with_z(base),
368            })
369            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
370            .fill(trap.clone());
371
372        // room hulls
373        for rooms in &self.room_positions {
374            let room_center = Vec2::new(rooms.x, rooms.y);
375            let floor = rooms.z + (room_size / 4) + 1;
376            painter
377                .superquadric(
378                    Aabb {
379                        min: (room_center - room_size - 2).with_z(floor - (room_size / 4) - 2),
380                        max: (room_center + room_size + 2).with_z(floor + (room_size / 4) + 2),
381                    },
382                    4.0,
383                )
384                .fill(rock.clone());
385            painter
386                .superquadric(
387                    Aabb {
388                        min: (room_center - room_size - 1).with_z(floor - (room_size / 4) - 1),
389                        max: (room_center + room_size + 1).with_z(floor + (room_size / 4) + 1),
390                    },
391                    4.0,
392                )
393                .fill(rock_broken.clone());
394        }
395        for rooms in &self.room_positions {
396            // clear rooms
397            let room_center = Vec2::new(rooms.x, rooms.y);
398            let floor = rooms.z + (room_size / 4) + 1;
399            painter
400                .superquadric(
401                    Aabb {
402                        min: (room_center - room_size).with_z(floor - (room_size / 4)),
403                        max: (room_center + room_size).with_z(floor + (room_size / 4)),
404                    },
405                    4.0,
406                )
407                .clear();
408            // room floor
409            painter
410                .aabb(Aabb {
411                    min: (room_center - room_size).with_z(floor - (room_size / 4) - 2),
412                    max: (room_center + room_size).with_z(floor - (room_size / 4) + 5),
413                })
414                .fill(rock.clone());
415            // room lanterns
416            painter
417                .aabb(Aabb {
418                    min: (room_center - room_size + 7).with_z(floor - (room_size / 4) + 5),
419                    max: (room_center + room_size - 7).with_z(floor - (room_size / 4) + 6),
420                })
421                .fill(lanterns.clone());
422        }
423        // mob rooms
424        for rooms in &self.mob_room_positions {
425            let room_center = Vec2::new(rooms.x, rooms.y);
426            let floor = rooms.z + (room_size / 4) + 1;
427            // inner room
428            painter
429                .aabb(Aabb {
430                    min: (room_center - (2 * (room_size / 3)) - 1)
431                        .with_z(floor - (room_size / 4) + 5),
432                    max: (room_center + (2 * (room_size / 3)) + 1).with_z(floor + (room_size / 4)),
433                })
434                .fill(rock_broken.clone());
435            painter
436                .aabb(Aabb {
437                    min: (room_center - (2 * (room_size / 3)) + 1)
438                        .with_z(floor - (room_size / 4) + 5),
439                    max: (room_center + (2 * (room_size / 3)) - 1)
440                        .with_z(floor + (room_size / 4) - 1),
441                })
442                .clear();
443            // inner room entries
444            painter
445                .vault(
446                    Aabb {
447                        min: Vec2::new(
448                            room_center.x - (2 * (room_size / 3)) - 1,
449                            room_center.y - 4,
450                        )
451                        .with_z(floor - (room_size / 4) + 5),
452                        max: Vec2::new(
453                            room_center.x + (2 * (room_size / 3)) + 1,
454                            room_center.y + 4,
455                        )
456                        .with_z(floor + (room_size / 8)),
457                    },
458                    Dir::X,
459                )
460                .clear();
461            painter
462                .vault(
463                    Aabb {
464                        min: Vec2::new(
465                            room_center.x - 4,
466                            room_center.y - (2 * (room_size / 3)) - 1,
467                        )
468                        .with_z(floor - (room_size / 4) + 5),
469                        max: Vec2::new(
470                            room_center.x + 4,
471                            room_center.y + (2 * (room_size / 3)) + 1,
472                        )
473                        .with_z(floor + (room_size / 8)),
474                    },
475                    Dir::Y,
476                )
477                .clear();
478            // room lanterns inner
479            painter
480                .aabb(Aabb {
481                    min: (room_center - (2 * (room_size / 3)) + 1)
482                        .with_z(floor - (room_size / 4) + 5),
483                    max: (room_center + (2 * (room_size / 3)) - 1)
484                        .with_z(floor - (room_size / 4) + 6),
485                })
486                .fill(lanterns.clone());
487            for dir in DIAGONALS {
488                let pillar_pos = room_center + dir * ((2 * (room_size / 3)) - 6);
489                let stair_pos = Vec2::new(
490                    room_center.x + dir.x * ((2 * (room_size / 3)) - 10),
491                    room_center.y + dir.y * ((2 * (room_size / 3)) - 3),
492                );
493                if (RandomField::new(0).get((pillar_pos).with_z(base)) % 3) > 0 {
494                    // stairs
495                    painter
496                        .line(
497                            Vec2::new(room_center.x, stair_pos.y)
498                                .with_z(floor - (room_size / 4) - 1),
499                            stair_pos.with_z(floor - (room_size / 4) + 6),
500                            4.0,
501                        )
502                        .fill(rock.clone());
503                    painter
504                        .cylinder(Aabb {
505                            min: (pillar_pos - 6).with_z(floor - (room_size / 4) + 5),
506                            max: (pillar_pos + 6).with_z(floor - (room_size / 4) + 6),
507                        })
508                        .fill(rock.clone());
509                    painter
510                        .cylinder(Aabb {
511                            min: (pillar_pos - 6).with_z(floor - (room_size / 4) + 6),
512                            max: (pillar_pos + 6).with_z(floor - (room_size / 4) + 7),
513                        })
514                        .fill(iron_spikes.clone());
515                    painter
516                        .cylinder(Aabb {
517                            min: (pillar_pos - 5).with_z(floor - (room_size / 4) + 5),
518                            max: (pillar_pos + 5).with_z(floor - (room_size / 4) + 9),
519                        })
520                        .fill(rock_broken.clone());
521                    painter
522                        .cylinder(Aabb {
523                            min: (pillar_pos - 6).with_z(floor - (room_size / 4) + 9),
524                            max: (pillar_pos + 7).with_z(floor - (room_size / 4) + 10),
525                        })
526                        .fill(rock.clone());
527                    // chests
528                    if (RandomField::new(0).get((pillar_pos).with_z(base)) % 5) == 0 {
529                        painter
530                            .aabb(Aabb {
531                                min: pillar_pos.with_z(floor - (room_size / 4) + 9),
532                                max: (pillar_pos + 1).with_z(floor - (room_size / 4) + 10),
533                            })
534                            .fill(rock.clone());
535                        painter
536                            .aabb(Aabb {
537                                min: pillar_pos.with_z(floor - (room_size / 4) + 10),
538                                max: (pillar_pos + 1).with_z(floor - (room_size / 4) + 11),
539                            })
540                            .fill(Fill::Block(Block::air(SpriteKind::DungeonChest3)));
541                    }
542                    // room npcs
543                    for _ in 0..=thread_rng.gen_range(0..2) {
544                        // archers on pillars
545                        painter.spawn(
546                            EntityInfo::at(pillar_pos.with_z(floor - (room_size / 4) + 11).as_())
547                                .with_asset_expect(
548                                    "common.entity.dungeon.haniwa.archer",
549                                    &mut thread_rng,
550                                    None,
551                                ),
552                        );
553                    }
554                }
555            }
556            for n in 0..=thread_rng.gen_range(2..=4) {
557                let select =
558                    (RandomField::new(0).get((room_center + n).with_z(floor)) % 2) as usize;
559                painter.spawn(
560                    EntityInfo::at(
561                        (room_center + 10 + n)
562                            .with_z(floor - (room_size / 4) + 6)
563                            .as_(),
564                    )
565                    .with_asset_expect(npcs[select], &mut thread_rng, None),
566                )
567            }
568            let effigy_pos = (room_center - 8).with_z(floor - (room_size / 4) + 6);
569            if (RandomField::new(0).get(effigy_pos) % 2) as usize > 0 {
570                painter.spawn(EntityInfo::at(effigy_pos.as_()).with_asset_expect(
571                    "common.entity.dungeon.haniwa.ancienteffigy",
572                    &mut thread_rng,
573                    None,
574                ));
575            }
576            // room chest
577            match RandomField::new(0).get(room_center.with_z(base)) as i32 % 3 {
578                0 => {
579                    for dir in CARDINALS {
580                        let sentry_pos = room_center + dir * 10;
581                        painter.spawn(
582                            EntityInfo::at(sentry_pos.with_z(floor - (room_size / 4) + 6).as_())
583                                .with_asset_expect(
584                                    "common.entity.dungeon.haniwa.sentry",
585                                    &mut thread_rng,
586                                    None,
587                                ),
588                        )
589                    }
590                    painter
591                        .aabb(Aabb {
592                            min: (room_center - 1).with_z(floor - (room_size / 4) + 5),
593                            max: (room_center + 2).with_z(floor - (room_size / 4) + 6),
594                        })
595                        .fill(rock.clone());
596                    painter
597                        .aabb(Aabb {
598                            min: room_center.with_z(floor - (room_size / 4) + 6),
599                            max: (room_center + 1).with_z(floor - (room_size / 4) + 7),
600                        })
601                        .fill(Fill::Block(Block::air(SpriteKind::HaniwaUrn)));
602                },
603                1 => {
604                    painter
605                        .cylinder(Aabb {
606                            min: (room_center - 8).with_z(floor - (room_size / 4) + 5),
607                            max: (room_center + 8).with_z(floor - (room_size / 4) + 6),
608                        })
609                        .fill(rock_iron_spikes.clone());
610                    painter
611                        .cylinder(Aabb {
612                            min: (room_center - 7).with_z(floor - (room_size / 4) + 5),
613                            max: (room_center + 7).with_z(floor - (room_size / 4) + 6),
614                        })
615                        .fill(rock.clone());
616                    painter
617                        .cylinder(Aabb {
618                            min: (room_center - 7).with_z(floor - (room_size / 4) + 6),
619                            max: (room_center + 7).with_z(floor - (room_size / 4) + 7),
620                        })
621                        .fill(rock_iron_spikes.clone());
622                    painter
623                        .cylinder(Aabb {
624                            min: (room_center - 6).with_z(floor - (room_size / 4) + 6),
625                            max: (room_center + 6).with_z(floor - (room_size / 4) + 7),
626                        })
627                        .fill(rock.clone());
628                    painter
629                        .cylinder(Aabb {
630                            min: (room_center - 6).with_z(floor - (room_size / 4) + 7),
631                            max: (room_center + 6).with_z(floor - (room_size / 4) + 8),
632                        })
633                        .fill(rock_iron_spikes.clone());
634                    painter
635                        .aabb(Aabb {
636                            min: (room_center - 1).with_z(floor - (room_size / 4) + 7),
637                            max: (room_center + 2).with_z(floor - (room_size / 4) + 8),
638                        })
639                        .fill(rock.clone());
640                    painter
641                        .aabb(Aabb {
642                            min: room_center.with_z(floor - (room_size / 4) + 8),
643                            max: (room_center + 1).with_z(floor - (room_size / 4) + 9),
644                        })
645                        .fill(Fill::Block(Block::air(SpriteKind::HaniwaUrn)));
646                },
647                _ => {
648                    for c in 0..=7 {
649                        painter
650                            .cylinder(Aabb {
651                                min: (room_center - 10 + c).with_z(floor - (room_size / 4) + 4),
652                                max: (room_center + 10 - c).with_z(floor - (room_size / 4) + 5),
653                            })
654                            .fill(match c {
655                                0 | 2 | 4 | 6 => trap.clone(),
656                                _ => rock.clone(),
657                            });
658                    }
659
660                    painter
661                        .aabb(Aabb {
662                            min: (room_center - 1).with_z(floor - (room_size / 4) + 5),
663                            max: (room_center + 2).with_z(floor - (room_size / 4) + 6),
664                        })
665                        .fill(rock.clone());
666                    painter
667                        .aabb(Aabb {
668                            min: room_center.with_z(floor - (room_size / 4) + 6),
669                            max: (room_center + 1).with_z(floor - (room_size / 4) + 7),
670                        })
671                        .fill(Fill::Block(Block::air(SpriteKind::HaniwaUrn)));
672                },
673            }
674        }
675        // center rooms
676        for rooms in &self.center_room_positions {
677            let floor = rooms.z + (room_size / 4) + 1;
678            // room decor
679            for dir in NEIGHBORS {
680                let position = center + dir * 20;
681                for p in 0..4 {
682                    painter
683                        .aabb(Aabb {
684                            min: (position - 1 - p).with_z(floor - (room_size / 4) + 5 + (4 * p)),
685                            max: (position + 2 + p).with_z(floor - (room_size / 4) + 9 + (4 * p)),
686                        })
687                        .fill(rock_broken.clone());
688                }
689                for t in 0..2 {
690                    let trap_pos = center + (dir * (12 + (t * 14)));
691                    if RandomField::new(0).get((trap_pos).with_z(floor)) % 3 < 1 {
692                        painter
693                            .aabb(Aabb {
694                                min: (trap_pos - 1).with_z(floor - (room_size / 4) + 4),
695                                max: (trap_pos + 1).with_z(floor - (room_size / 4) + 5),
696                            })
697                            .fill(trap.clone());
698                        painter
699                            .aabb(Aabb {
700                                min: (trap_pos - 1).with_z(floor - (room_size / 4) + 5),
701                                max: (trap_pos + 1).with_z(floor - (room_size / 4) + 6),
702                            })
703                            .clear();
704                    }
705                }
706            }
707        }
708        // center room stairs
709        for f in 0..(self.center_room_positions.len() - 1) {
710            let floor = self.center_room_positions[f].z + (room_size / 4) + 1;
711            let stairs_floor = floor - 5;
712            let stairs_start =
713                Vec2::new(center.x - (diameter / 6) + 2, center.y - (diameter / 6) + 7);
714            for s in 0..(diameter / 5) {
715                painter
716                    .vault(
717                        Aabb {
718                            min: Vec2::new(stairs_start.x + s - 1, stairs_start.y - 4)
719                                .with_z(stairs_floor - s - 2),
720                            max: Vec2::new(stairs_start.x + s, stairs_start.y + 4)
721                                .with_z(stairs_floor + 12 - s + 2),
722                        },
723                        Dir::X,
724                    )
725                    .fill(rock_broken.clone());
726            }
727            for s in 0..(diameter / 5) {
728                painter
729                    .vault(
730                        Aabb {
731                            min: Vec2::new(stairs_start.x + s - 1, stairs_start.y - 2)
732                                .with_z(stairs_floor - s),
733                            max: Vec2::new(stairs_start.x + s, stairs_start.y + 2)
734                                .with_z(stairs_floor + 10 - s),
735                        },
736                        Dir::X,
737                    )
738                    .clear();
739                painter
740                    .aabb(Aabb {
741                        min: Vec2::new(stairs_start.x + s - 1, stairs_start.y - 2)
742                            .with_z(stairs_floor - 1 - s),
743                        max: Vec2::new(stairs_start.x + s, stairs_start.y + 2)
744                            .with_z(stairs_floor - s),
745                    })
746                    .fill(rock.clone());
747                painter
748                    .aabb(Aabb {
749                        min: Vec2::new(stairs_start.x + s - 1, stairs_start.y - 2)
750                            .with_z(stairs_floor - s),
751                        max: Vec2::new(stairs_start.x + s, stairs_start.y + 2)
752                            .with_z(stairs_floor + 1 - s),
753                    })
754                    .fill(lanterns.clone());
755
756                let doors = [0, 8, 16, 24];
757                if doors.contains(&s) {
758                    painter
759                        .vault(
760                            Aabb {
761                                min: Vec2::new(stairs_start.x + s - 1, stairs_start.y - 2)
762                                    .with_z(stairs_floor - s),
763                                max: Vec2::new(stairs_start.x + s, stairs_start.y + 2)
764                                    .with_z(stairs_floor + 10 - s),
765                            },
766                            Dir::X,
767                        )
768                        .fill(key_door.clone());
769                    painter
770                        .aabb(Aabb {
771                            min: Vec2::new(stairs_start.x + s - 1, stairs_start.y)
772                                .with_z(stairs_floor + 2 - s),
773                            max: Vec2::new(stairs_start.x + s, stairs_start.y + 1)
774                                .with_z(stairs_floor + 3 - s),
775                        })
776                        .fill(key_hole.clone());
777                }
778            }
779        }
780
781        // stair case
782        painter
783            .aabb(Aabb {
784                min: Vec2::new(center.x + 3, center.y - 6).with_z(base - (diameter / 4) - 5),
785                max: Vec2::new(center.x + 14, center.y + 6).with_z(base - (diameter / 4) + 15),
786            })
787            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
788            .fill(rock_broken.clone());
789        // tunnel stairs
790        for s in 0..((diameter / 4) - 3) {
791            painter
792                .vault(
793                    Aabb {
794                        min: Vec2::new(center.x + (diameter / 4) - s - 1, center.y - 5)
795                            .with_z(base - s - 1),
796                        max: Vec2::new(center.x + (diameter / 4) - s, center.y + 5)
797                            .with_z(base + 16 - s + 1),
798                    },
799                    Dir::X,
800                )
801                .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
802                .fill(rock_broken.clone());
803
804            painter
805                .vault(
806                    Aabb {
807                        min: Vec2::new(center.x + (diameter / 4) - s - 1, center.y - 4)
808                            .with_z(base - s),
809                        max: Vec2::new(center.x + (diameter / 4) - s, center.y + 4)
810                            .with_z(base + 16 - s),
811                    },
812                    Dir::X,
813                )
814                .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
815                .clear();
816            painter
817                .aabb(Aabb {
818                    min: Vec2::new(center.x + (diameter / 4) - s - 1, center.y - 4)
819                        .with_z(base - 1 - s),
820                    max: Vec2::new(center.x + (diameter / 4) - s, center.y + 4).with_z(base - s),
821                })
822                .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
823                .fill(rock.clone());
824            painter
825                .aabb(Aabb {
826                    min: Vec2::new(center.x + (diameter / 4) - s - 1, center.y - 4)
827                        .with_z(base - s),
828                    max: Vec2::new(center.x + (diameter / 4) - s, center.y + 4)
829                        .with_z(base + 1 - s),
830                })
831                .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
832                .fill(lanterns.clone());
833        }
834
835        // tree model
836        lazy_static! {
837            pub static ref MODEL: AssetHandle<StructuresGroup> =
838                PrefabStructure::load_group("site_structures.haniwa.bonsai");
839        }
840        let model_pos = tree_pos.with_z(base - 10 + platform_size);
841        let rng = RandomField::new(0).get(model_pos) % 10;
842        let model = MODEL.read();
843        let model = model[rng as usize % model.len()].clone();
844        painter
845            .prim(Primitive::Prefab(Box::new(model.clone())))
846            .translate(model_pos)
847            .fill(Fill::Prefab(Box::new(model), model_pos, rng));
848
849        // mini_bosses
850        let golem_pos = Vec3::new(
851            self.mini_boss_room_positions[0].x,
852            self.mini_boss_room_positions[0].y,
853            self.mini_boss_room_positions[0].z + 5,
854        );
855        painter.spawn(EntityInfo::at(golem_pos.as_()).with_asset_expect(
856            "common.entity.dungeon.haniwa.claygolem",
857            &mut thread_rng,
858            None,
859        ));
860        // mid_boss
861        let mid_boss = [
862            "common.entity.dungeon.haniwa.general",
863            "common.entity.dungeon.haniwa.claysteed",
864        ];
865        for npc in mid_boss.iter() {
866            painter.spawn(
867                EntityInfo::at(
868                    Vec3::new(
869                        self.mini_boss_room_positions[1].x,
870                        self.mini_boss_room_positions[1].y,
871                        self.mini_boss_room_positions[1].z + 5,
872                    )
873                    .as_(),
874                )
875                .with_asset_expect(npc, &mut thread_rng, None),
876            );
877        }
878        // boss
879        painter.spawn(
880            EntityInfo::at(
881                Vec3::new(
882                    self.boss_room_position.x,
883                    self.boss_room_position.y,
884                    self.boss_room_position.z + 5,
885                )
886                .as_(),
887            )
888            .with_asset_expect(
889                "common.entity.dungeon.haniwa.gravewarden",
890                &mut thread_rng,
891                None,
892            ),
893        );
894        let bonerattler_pos = (center + ((entrance - center) / 3)).with_z(base);
895        for _ in 0..(1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32 {
896            painter.spawn(EntityInfo::at(bonerattler_pos.as_()).with_asset_expect(
897                "common.entity.dungeon.haniwa.claysteed",
898                &mut thread_rng,
899                None,
900            ))
901        }
902    }
903}