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