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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 for _ in 0..=thread_rng.gen_range(0..2) {
544 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 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 for rooms in &self.center_room_positions {
677 let floor = rooms.z + (room_size / 4) + 1;
678 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 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 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 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 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 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 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 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}