1use super::*;
2use crate::{
3 Land,
4 assets::AssetHandle,
5 site2::gen::{PrimitiveTransform, place_circular},
6 util::{DIAGONALS, NEIGHBORS, RandomField, Sampler},
7};
8use common::{
9 generation::EntityInfo,
10 terrain::{BlockKind, SpriteKind, Structure as PrefabStructure, StructuresGroup},
11};
12use lazy_static::lazy_static;
13use rand::prelude::*;
14use std::{f32::consts::TAU, ops::RangeInclusive, sync::Arc};
15use vek::*;
16
17pub struct TerracottaPalace {
19 bounds: Aabr<i32>,
21 pub(crate) alt: i32,
23}
24
25impl TerracottaPalace {
26 pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
27 let bounds = Aabr {
28 min: site.tile_wpos(tile_aabr.min),
29 max: site.tile_wpos(tile_aabr.max),
30 };
31 Self {
32 bounds,
33 alt: land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
34 as i32
35 + 2,
36 }
37 }
38
39 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
40 SpawnRules {
41 waypoints: false,
42 trees: wpos.distance_squared(self.bounds.center()) > (85_i32).pow(2),
43 ..SpawnRules::default()
44 }
45 }
46}
47
48impl Structure for TerracottaPalace {
49 #[cfg(feature = "use-dyn-lib")]
50 const UPDATE_FN: &'static [u8] = b"render_terracotta_palace\0";
51
52 #[cfg_attr(
53 feature = "be-dyn-lib",
54 unsafe(export_name = "render_terracotta_palace")
55 )]
56 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
57 let base = self.alt + 1;
58 let center = self.bounds.center();
59 let mut rng = thread_rng();
60 let clay_broken = Fill::Sampling(Arc::new(|center| {
61 Some(match (RandomField::new(0).get(center)) % 42 {
62 0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
63 9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
64 18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
65 27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
66 36..=38 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
67 _ => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
68 })
69 }));
70 let clay_broken_2 = Fill::Sampling(Arc::new(|center| {
71 Some(match (RandomField::new(0).get(center)) % 64 {
72 0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
73 9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
74 18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
75 27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
76 36..=40 => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
77 _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
78 })
79 }));
80 let clay_unbroken = Fill::Sampling(Arc::new(|center| {
81 Some(match (RandomField::new(0).get(center)) % 40 {
82 0..=8 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
83 9..=17 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
84 18..=26 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
85 27..=35 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
86 _ => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
87 })
88 }));
89 let grass_fill = Fill::Sampling(Arc::new(|wpos| {
90 Some(match (RandomField::new(0).get(wpos)) % 20 {
91 1..=2 => Block::air(SpriteKind::JungleRedGrass),
92 3..=7 => Block::air(SpriteKind::JungleLeafyPlant),
93 8 => Block::air(SpriteKind::JungleFern),
94 _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
95 })
96 }));
97 let roof_color = Fill::Sampling(Arc::new(|center| {
98 Some(match (RandomField::new(0).get(center)) % 400 {
99 0..=4 => Block::new(BlockKind::Rock, Rgb::new(242, 161, 53)),
100 5..=9 => Block::new(BlockKind::Rock, Rgb::new(253, 199, 81)),
101 10..=14 => Block::new(BlockKind::Rock, Rgb::new(254, 210, 91)),
102 15..=19 => Block::new(BlockKind::Rock, Rgb::new(254, 216, 101)),
103 20..=21 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
104 22..=23 => Block::new(BlockKind::Rock, Rgb::new(250, 185, 71)),
105 _ => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
106 })
107 }));
108 let sand = Fill::Brick(BlockKind::Sand, Rgb::new(235, 178, 99), 12);
109 let size = 60;
110 let room_size = 15 * (size / 10);
111 let roof_size = 16 * (size / 10);
112 let carve_size = 14 * (size / 10);
113 let roof_height = room_size / 3;
114 let storeys = 5;
115 let var = size / 5;
116 let clear_var = var / 2;
117 let clear_limit = painter.aabb(Aabb {
118 min: (center - (room_size / 2) - 2).with_z(base),
119 max: (center + (room_size / 2) + 2).with_z(base + (2 * room_size)),
120 });
121 let clear_limit_down = painter.aabb(Aabb {
122 min: (center - (room_size / 2) - 5).with_z(base - (2 * room_size)),
123 max: (center + (room_size / 2) + 5).with_z(base),
124 });
125 let spikes_fill = Fill::Sampling(Arc::new(|center| {
126 Some(match (RandomField::new(0).get(center)) % 8 {
127 0 => Block::air(SpriteKind::IronSpike),
128 _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
129 })
130 }));
131 let mut chamber_npcs = vec![
133 "common.entity.dungeon.terracotta.cursekeeper_fake",
134 "common.entity.dungeon.terracotta.cursekeeper_fake",
135 "common.entity.dungeon.terracotta.cursekeeper_fake",
136 "common.entity.dungeon.terracotta.cursekeeper",
137 ];
138 let mut statue_npcs = vec![
139 "common.entity.dungeon.terracotta.terracotta_statue_key",
140 "common.entity.dungeon.terracotta.terracotta_statue",
141 "common.entity.dungeon.terracotta.terracotta_statue",
142 "common.entity.dungeon.terracotta.terracotta_statue",
143 ];
144 let mut cellar_statue_npcs = vec![
145 "common.entity.dungeon.terracotta.terracotta_statue_key",
146 "common.entity.dungeon.terracotta.terracotta_statue",
147 "common.entity.dungeon.terracotta.terracotta_statue",
148 ];
149 let model_radius = (2 * (room_size / 3)) + 6;
151 for dir in NEIGHBORS {
152 let pos = center + dir * model_radius;
153 let palm_model_pos = Vec2::new(pos.x + 2, pos.y);
154 let found_var_a = (RandomField::new(0).get(pos.with_z(base)) % 10) as i32;
155 let found_var_b = (RandomField::new(0).get(pos.with_z(base + 1)) % 10) as i32;
156 let height_var = (found_var_a + found_var_b) / 2;
157 let foundation_limiter_1 = painter.aabb(Aabb {
159 min: (pos - (room_size / 3) - 1 - found_var_a).with_z(base - room_size),
160 max: (pos + (room_size / 3) + 1 + found_var_b).with_z(base - 1 + height_var),
161 });
162 painter
163 .rounded_aabb(Aabb {
164 min: (pos - (room_size / 3) - 1 - found_var_a).with_z(base - room_size),
165 max: (pos + (room_size / 3) + 1 + found_var_b).with_z(base + (room_size / 2)),
166 })
167 .intersect(foundation_limiter_1)
168 .fill(clay_unbroken.clone());
169 let foundation_limiter_2 = painter.aabb(Aabb {
170 min: (pos - (room_size / 3) - 1 - found_var_a).with_z(base - 2),
171 max: (pos + (room_size / 3) + 1 + found_var_b).with_z(base + height_var),
172 });
173 painter
174 .rounded_aabb(Aabb {
175 min: (pos - (room_size / 3) - 1 - found_var_a).with_z(base - (room_size / 2)),
176 max: (pos + (room_size / 3) + 1 + found_var_b).with_z(base + (room_size / 2)),
177 })
178 .intersect(foundation_limiter_2)
179 .fill(clay_broken_2.clone());
180 let foundation_limiter_3 = painter.aabb(Aabb {
181 min: (pos - (room_size / 3) - 1 - found_var_a).with_z(base - 2),
182 max: (pos + (room_size / 3) + 1 + found_var_b).with_z(base + height_var + 8),
183 });
184 painter
185 .rounded_aabb(Aabb {
186 min: (pos - (room_size / 3) + 2 - found_var_a).with_z(base - (room_size / 2)),
187 max: (pos + (room_size / 3) - 2 + found_var_b).with_z(base + (room_size / 2)),
188 })
189 .intersect(foundation_limiter_3)
190 .clear();
191 let foundation_limiter_4 = painter.aabb(Aabb {
192 min: (pos - (room_size / 3) - found_var_a).with_z(base - 2),
193 max: (pos + (room_size / 3) + found_var_b).with_z(base - 1),
194 });
195 painter
196 .rounded_aabb(Aabb {
197 min: (pos - (room_size / 3) - found_var_a).with_z(base - (room_size / 2)),
198 max: (pos + (room_size / 3) + found_var_b).with_z(base + (room_size / 2)),
199 })
200 .intersect(foundation_limiter_4)
201 .fill(sand.clone());
202 for dir in NEIGHBORS {
204 let decay_pos = pos + (dir * (room_size / 3));
205 painter
206 .sphere(Aabb {
207 min: (decay_pos - 8).with_z(base),
208 max: (decay_pos + 8).with_z(base + 16),
209 })
210 .clear();
211 }
212 painter
214 .cylinder(Aabb {
215 min: (pos - 12).with_z(base - 1),
216 max: (pos + 12).with_z(base),
217 })
218 .fill(grass_fill.clone());
219 let model_pos = pos.with_z(base - 5);
221 lazy_static! {
222 pub static ref MODEL: AssetHandle<StructuresGroup> = PrefabStructure::load_group(
223 "site_structures.terracotta.terracotta_decor_large"
224 );
225 }
226 let rng = RandomField::new(0).get(model_pos) % 62;
227 let model = MODEL.read();
228 let model = model[rng as usize % model.len()].clone();
229 painter
230 .prim(Primitive::Prefab(Box::new(model.clone())))
231 .translate(model_pos)
232 .fill(Fill::Prefab(Box::new(model), model_pos, rng));
233 for dir in DIAGONALS {
234 let model_pos = palm_model_pos + dir * 12;
235 match RandomField::new(0).get(model_pos.with_z(base)) % 4 {
236 0 => {},
237 _ => {
238 for p in 0..2 {
239 let palm_pos = (model_pos + 3 + (2 * p)).with_z(base - 5);
240 let exit = Aabb {
242 min: (center - (room_size / 2) - 8).with_z(base - 6),
243 max: (center - (room_size / 2) + 8).with_z(base - 4),
244 };
245 if !exit.contains_point(palm_pos) {
246 lazy_static! {
247 pub static ref MODEL: AssetHandle<StructuresGroup> =
248 PrefabStructure::load_group("trees.palms");
249 }
250 let rng = RandomField::new(0).get(palm_pos) % 62;
251 let model = MODEL.read();
252 let model = model[rng as usize % model.len()].clone();
253 painter
254 .prim(Primitive::Prefab(Box::new(model.clone())))
255 .translate(palm_pos)
256 .fill(Fill::Prefab(Box::new(model), palm_pos, rng));
257 }
258 }
259 },
260 }
261 }
262 }
263 let mut pavillons = vec![];
265 let pavillon_dist_x = room_size / 3;
266 let pavillon_dist_y = 2 * (room_size / 3);
267 let pavillon_size_1 = room_size / 8;
268 let pavillon_size_2 = room_size / 10;
269 for dir in DIAGONALS {
270 pavillons.push(Vec2::new(
271 center.x + dir.x * pavillon_dist_x,
272 center.y + dir.y * pavillon_dist_y,
273 ));
274 pavillons.push(Vec2::new(
275 center.x + dir.x * pavillon_dist_y,
276 center.y + dir.y * pavillon_dist_x,
277 ));
278 }
279 for pavillon_pos in pavillons {
280 let entry_carve_limiter = painter.aabb(Aabb {
281 min: (pavillon_pos - pavillon_size_1).with_z(base - pavillon_size_1),
282 max: (pavillon_pos + pavillon_size_1).with_z(base + pavillon_size_1),
283 });
284 let top_carve_limiter = painter.aabb(Aabb {
285 min: (pavillon_pos - pavillon_size_2).with_z(base + pavillon_size_1 - 2),
286 max: (pavillon_pos + pavillon_size_2)
287 .with_z(base + pavillon_size_1 + pavillon_size_2 - 2),
288 });
289 painter
291 .superquadric(
292 Aabb {
293 min: (pavillon_pos - pavillon_size_1).with_z(base - pavillon_size_1),
294 max: (pavillon_pos + pavillon_size_1).with_z(base + pavillon_size_1),
295 },
296 2.5,
297 )
298 .fill(clay_broken.clone());
299 painter
301 .superquadric(
302 Aabb {
303 min: (pavillon_pos - pavillon_size_2).with_z(base + pavillon_size_1 - 2),
304 max: (pavillon_pos + pavillon_size_2)
305 .with_z(base + pavillon_size_1 + pavillon_size_2 - 2),
306 },
307 2.5,
308 )
309 .fill(clay_broken.clone());
310 painter
311 .superquadric(
312 Aabb {
313 min: (pavillon_pos - pavillon_size_2 + 1)
314 .with_z(base + pavillon_size_1 - 1),
315 max: (pavillon_pos + pavillon_size_2 - 1)
316 .with_z(base + pavillon_size_1 + pavillon_size_2 - 3),
317 },
318 2.5,
319 )
320 .fill(roof_color.clone());
321 painter
322 .aabb(Aabb {
323 min: (pavillon_pos - 2).with_z(base + pavillon_size_1 + pavillon_size_2 - 3),
324 max: (pavillon_pos + 2).with_z(base + pavillon_size_1 + pavillon_size_2 - 2),
325 })
326 .fill(roof_color.clone());
327 painter
328 .aabb(Aabb {
329 min: (pavillon_pos - 1).with_z(base + pavillon_size_1 + pavillon_size_2 - 2),
330 max: (pavillon_pos + 1).with_z(base + pavillon_size_1 + pavillon_size_2),
331 })
332 .fill(roof_color.clone());
333 for dir in CARDINALS {
334 let pavillon_carve_pos = pavillon_pos + dir * pavillon_size_2;
335 painter
337 .superquadric(
338 Aabb {
339 min: (pavillon_carve_pos - pavillon_size_2 + 3).with_z(base - 1),
340 max: (pavillon_carve_pos + pavillon_size_2 - 2)
341 .with_z(base + pavillon_size_2 - 1),
342 },
343 2.5,
344 )
345 .intersect(entry_carve_limiter)
346 .clear();
347 painter
349 .superquadric(
350 Aabb {
351 min: (pavillon_carve_pos - pavillon_size_2 + 2)
352 .with_z(base + pavillon_size_1 + 2),
353 max: (pavillon_carve_pos + pavillon_size_2 - 2)
354 .with_z(base + pavillon_size_1 + pavillon_size_2 + 2),
355 },
356 2.5,
357 )
358 .intersect(top_carve_limiter)
359 .clear();
360 }
361 painter
363 .cylinder(Aabb {
364 min: (pavillon_pos - pavillon_size_2 + 2).with_z(base - 1),
365 max: (pavillon_pos + pavillon_size_2 - 2).with_z(base + 5),
366 })
367 .clear();
368 painter
370 .cylinder(Aabb {
371 min: (pavillon_pos - pavillon_size_2 - 2).with_z(base - 2),
372 max: (pavillon_pos + pavillon_size_2 + 2).with_z(base - 1),
373 })
374 .fill(clay_unbroken.clone());
375 }
376 painter
378 .superquadric(
379 Aabb {
380 min: (center - (room_size / 2)).with_z(base - (room_size / 2)),
381 max: (center + (room_size / 2)).with_z(base + (room_size / 2)),
382 },
383 2.5,
384 )
385 .fill(clay_broken.clone());
386 painter
388 .superquadric(
389 Aabb {
390 min: (center - (room_size / 2)).with_z(base - (room_size / 2)),
391 max: (center + (room_size / 2)).with_z(base + (room_size / 2)),
392 },
393 2.5,
394 )
395 .intersect(clear_limit_down)
396 .fill(clay_unbroken.clone());
397 for s in 0..storeys {
399 painter
400 .superquadric(
401 Aabb {
402 min: (center - (roof_size / 2) + (s * var)).with_z(
403 base + roof_height - (size / 4) + (s * (roof_size / 4)) + (s * var),
404 ),
405 max: (center + (roof_size / 2) - (s * var)).with_z(
406 base + roof_height - (size / 4) + roof_size + (s * (roof_size / 4))
407 - (s * var),
408 ),
409 },
410 2.5,
411 )
412 .fill(clay_broken.clone());
413 painter
414 .superquadric(
415 Aabb {
416 min: (center - (roof_size / 2) + (s * var) + 1).with_z(
417 base + roof_height - (size / 4) + (s * (roof_size / 4)) + (s * var) + 1,
418 ),
419 max: (center + (roof_size / 2) - (s * var) - 1).with_z(
420 base + roof_height - (size / 4) + roof_size + (s * (roof_size / 4))
421 - (s * var)
422 - 1,
423 ),
424 },
425 2.5,
426 )
427 .fill(roof_color.clone());
428 for dir in CARDINALS {
429 let pos = center + dir * (size - (s * var));
430
431 painter
432 .superquadric(
433 Aabb {
434 min: (pos - (carve_size / 2) + (s * clear_var)).with_z(
435 base + roof_height + (s * (roof_size / 4)) + (s * clear_var),
436 ),
437 max: (pos + (carve_size / 2) - (s * clear_var)).with_z(
438 base + roof_height + carve_size + (s * (roof_size / 4))
439 - (s * clear_var),
440 ),
441 },
442 2.5,
443 )
444 .intersect(clear_limit)
445 .clear();
446 }
447 }
448 painter
450 .superquadric(
451 Aabb {
452 min: (center - (room_size / 2) + 5).with_z(base - (room_size / 2) + 5),
453 max: (center + (room_size / 2) - 5).with_z(base + (room_size / 2) - 5),
454 },
455 2.5,
456 )
457 .union(
458 painter
459 .superquadric(
460 Aabb {
461 min: Vec2::new(
462 center.x - (room_size / 4),
463 center.y - (3 * room_size / 4),
464 )
465 .with_z(base - (room_size / 4)),
466 max: Vec2::new(
467 center.x + (room_size / 4),
468 center.y + (3 * room_size / 4),
469 )
470 .with_z(base + (room_size / 4)),
471 },
472 2.5,
473 )
474 .union(
475 painter.superquadric(
476 Aabb {
477 min: Vec2::new(
478 center.x - (3 * room_size / 4),
479 center.y - (room_size / 4),
480 )
481 .with_z(base - (room_size / 4)),
482 max: Vec2::new(
483 center.x + (3 * room_size / 4),
484 center.y + (room_size / 4),
485 )
486 .with_z(base + (room_size / 4)),
487 },
488 2.5,
489 ),
490 ),
491 )
492 .intersect(clear_limit)
493 .clear();
494 painter
496 .superquadric(
497 Aabb {
498 min: (center - (room_size / 3))
499 .with_z(base + (3 * (room_size / 10)) - (room_size / 3)),
500 max: (center + (room_size / 3))
501 .with_z(base + (3 * (room_size / 10)) + (room_size / 4)),
502 },
503 2.5,
504 )
505 .clear();
506 painter
507 .superquadric(
508 Aabb {
509 min: (center - (room_size / 4))
510 .with_z(base + (3 * (room_size / 10)) - (room_size / 4)),
511 max: (center + (room_size / 4))
512 .with_z(base + (3 * (room_size / 10)) + (room_size / 3)),
513 },
514 2.5,
515 )
516 .clear();
517 painter
519 .cylinder(Aabb {
520 min: (center - (room_size / 2) + 8).with_z(base + (3 * (room_size / 10)) - 1),
521 max: (center + (room_size / 2) - 8).with_z(base + (3 * (room_size / 10)) + 2),
522 })
523 .fill(clay_unbroken.clone());
524 painter
525 .cylinder(Aabb {
526 min: (center - (room_size / 4) + 1).with_z(base + (3 * (room_size / 10)) - 1),
527 max: (center + (room_size / 4) - 1).with_z(base + (3 * (room_size / 10)) + 2),
528 })
529 .clear();
530 painter
532 .cylinder(Aabb {
533 min: (center - (room_size / 4) + 1).with_z(base + (3 * (room_size / 10)) + 1),
534 max: (center + (room_size / 4) - 1).with_z(base + (3 * (room_size / 10)) + 2),
535 })
536 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
537 painter
538 .cylinder(Aabb {
539 min: (center - (room_size / 4) - 1).with_z(base + (2 * (room_size / 10)) + 3),
540 max: (center + (room_size / 4) + 1).with_z(base + (2 * (room_size / 10)) + 4),
541 })
542 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
543
544 for chain_pos in place_circular(center, ((room_size / 4) + 1) as f32, 20) {
545 painter
546 .aabb(Aabb {
547 min: (chain_pos - 1).with_z(base + (2 * (room_size / 10)) + 3),
548 max: chain_pos.with_z(base + (3 * (room_size / 10)) - 1),
549 })
550 .fill(Fill::Block(Block::air(SpriteKind::MetalChain)));
551 painter
552 .cylinder(Aabb {
553 min: (chain_pos - 3).with_z(base + (2 * (room_size / 10)) + 2),
554 max: (chain_pos + 1).with_z(base + (2 * (room_size / 10)) + 3),
555 })
556 .fill(roof_color.clone());
557 }
558 painter
559 .cylinder(Aabb {
560 min: (center - (room_size / 8) - 5).with_z(base - 1),
561 max: (center + (room_size / 8) + 5).with_z(base),
562 })
563 .fill(clay_unbroken.clone());
564 painter
565 .cylinder(Aabb {
566 min: (center - (room_size / 8) - 5).with_z(base),
567 max: (center + (room_size / 8) + 5).with_z(base + 1),
568 })
569 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
570 painter
571 .cylinder(Aabb {
572 min: (center - (room_size / 8) - 3).with_z(base + 1),
573 max: (center + (room_size / 8) + 3).with_z(base + (3 * (room_size / 10)) + 2),
574 })
575 .fill(spikes_fill);
576 painter
577 .cylinder(Aabb {
578 min: (center - (room_size / 8) - 2).with_z(base - 20),
579 max: (center + (room_size / 8) + 2).with_z(base + (3 * (room_size / 10)) + 2),
580 })
581 .fill(clay_unbroken.clone());
582 painter
583 .cylinder(Aabb {
584 min: (center - (room_size / 8)).with_z(base + (3 * (room_size / 10)) + 1),
585 max: (center + (room_size / 8)).with_z(base + (3 * (room_size / 10)) + 2),
586 })
587 .fill(roof_color.clone());
588 painter
589 .cylinder(Aabb {
590 min: (center - (room_size / 8)).with_z(base - 10),
591 max: (center + (room_size / 8)).with_z(base - 15),
592 })
593 .fill(roof_color.clone());
594 painter
596 .superquadric(
597 Aabb {
598 min: (center - (room_size / 2)).with_z(base - 10 - room_size),
599 max: (center + (room_size / 2)).with_z(base - 10),
600 },
601 2.5,
602 )
603 .fill(clay_unbroken.clone());
604 painter
606 .line(
607 Vec2::new(center.x - (room_size / 2) - 8, center.y)
608 .with_z(base - 10 - (room_size / 2)),
609 Vec2::new(center.x + (room_size / 2) + 8, center.y)
610 .with_z(base - 10 - (room_size / 2)),
611 8.0,
612 )
613 .fill(clay_unbroken.clone());
614 painter
615 .line(
616 Vec2::new(center.x, center.y - (room_size / 2) - 8)
617 .with_z(base - 10 - (room_size / 2)),
618 Vec2::new(center.x, center.y + (room_size / 2) + 8)
619 .with_z(base - 10 - (room_size / 2)),
620 8.0,
621 )
622 .fill(clay_unbroken.clone());
623 painter
624 .line(
625 Vec2::new(center.x - (room_size / 2) - 8, center.y)
626 .with_z(base - 10 - (room_size / 2)),
627 Vec2::new(center.x + (room_size / 2) + 8, center.y)
628 .with_z(base - 10 - (room_size / 2)),
629 5.0,
630 )
631 .clear();
632 painter
633 .line(
634 Vec2::new(center.x, center.y - (room_size / 2) - 8)
635 .with_z(base - 10 - (room_size / 2)),
636 Vec2::new(center.x, center.y + (room_size / 2) + 8)
637 .with_z(base - 10 - (room_size / 2)),
638 5.0,
639 )
640 .clear();
641 for dir in CARDINALS {
643 let room_center = center + dir * ((room_size / 2) + 8);
644 painter
645 .cylinder(Aabb {
646 min: (room_center - 3).with_z(base - 30 - (room_size / 2) - (room_size / 16)),
647 max: (room_center + 3).with_z(base - 6 - (room_size / 2) - (room_size / 16)),
648 })
649 .clear();
650 let npc_pos = room_center.with_z(base - 24 - (room_size / 2) - (room_size / 16));
652 let npc = chamber_npcs
653 .swap_remove(RandomField::new(0).get(npc_pos) as usize % chamber_npcs.len());
654
655 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(npc, &mut rng, None));
656 }
657 painter
659 .aabb(Aabb {
660 min: Vec2::new(center.x - (room_size / 2), center.y - 4)
661 .with_z(base - 10 - (room_size / 2)),
662 max: Vec2::new(center.x - (room_size / 2) + 1, center.y + 5)
663 .with_z(base - 10 - (room_size / 2) + 5),
664 })
665 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyDoor)));
666 painter
667 .aabb(Aabb {
668 min: Vec2::new(center.x - (room_size / 2), center.y)
669 .with_z(base - 10 - (room_size / 2) + 1),
670 max: Vec2::new(center.x - (room_size / 2) + 1, center.y + 1)
671 .with_z(base - 10 - (room_size / 2) + 2),
672 })
673 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyhole)));
674 painter
676 .aabb(Aabb {
677 min: Vec2::new(center.x + (room_size / 2) - 1, center.y - 4)
678 .with_z(base - 10 - (room_size / 2)),
679 max: Vec2::new(center.x + (room_size / 2), center.y + 5)
680 .with_z(base - 10 - (room_size / 2) + 5),
681 })
682 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyDoor)));
683 painter
684 .aabb(Aabb {
685 min: Vec2::new(center.x + (room_size / 2) - 1, center.y)
686 .with_z(base - 10 - (room_size / 2) + 1),
687 max: Vec2::new(center.x + (room_size / 2), center.y + 1)
688 .with_z(base - 10 - (room_size / 2) + 2),
689 })
690 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyhole)));
691 painter
693 .aabb(Aabb {
694 min: Vec2::new(center.x - 4, center.y - (room_size / 2))
695 .with_z(base - 10 - (room_size / 2)),
696 max: Vec2::new(center.x + 5, center.y - (room_size / 2) + 1)
697 .with_z(base - 10 - (room_size / 2) + 5),
698 })
699 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyDoor)));
700 painter
701 .aabb(Aabb {
702 min: Vec2::new(center.x, center.y - (room_size / 2))
703 .with_z(base - 10 - (room_size / 2) + 1),
704 max: Vec2::new(center.x + 1, center.y - (room_size / 2) + 1)
705 .with_z(base - 10 - (room_size / 2) + 2),
706 })
707 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyhole)));
708 painter
710 .aabb(Aabb {
711 min: Vec2::new(center.x - 4, center.y + (room_size / 2) - 1)
712 .with_z(base - 10 - (room_size / 2)),
713 max: Vec2::new(center.x + 5, center.y + (room_size / 2))
714 .with_z(base - 10 - (room_size / 2) + 5),
715 })
716 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyDoor)));
717 painter
718 .aabb(Aabb {
719 min: Vec2::new(center.x, center.y + (room_size / 2) - 1)
720 .with_z(base - 10 - (room_size / 2) + 1),
721 max: Vec2::new(center.x + 1, center.y + (room_size / 2))
722 .with_z(base - 10 - (room_size / 2) + 2),
723 })
724 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyhole)));
725 painter
727 .superquadric(
728 Aabb {
729 min: (center - (room_size / 2) + 1).with_z(base - 10 - room_size + 1),
730 max: (center + (room_size / 2) - 1).with_z(base - 10 - 1),
731 },
732 2.5,
733 )
734 .clear();
735 let sq_limit = painter.aabb(Aabb {
738 min: (center - (room_size / 4) - 11).with_z(base - 20 - (room_size / 2)),
739 max: (center - (room_size / 4) + 16).with_z(base - 1),
740 });
741 painter
742 .superquadric(
743 Aabb {
744 min: (center - (room_size / 4) - 9).with_z(base - 20 - (room_size / 2)),
745 max: (center - (room_size / 4) + 16).with_z(base + 20),
746 },
747 3.5,
748 )
749 .intersect(sq_limit)
750 .fill(clay_unbroken.clone());
751 let cyl_1_pos = Vec2::new(
752 center.x - (room_size / 4) - 2,
753 center.y - (room_size / 4) - 13,
754 );
755 let cyl_2_pos = Vec2::new(
756 center.x - (room_size / 4) - 12,
757 center.y - (room_size / 4) - 5,
758 );
759 painter
760 .aabb(Aabb {
761 min: (cyl_1_pos - 15).with_z(base - 10 - (room_size / 2)),
762 max: (cyl_1_pos + 15).with_z(base - 10),
763 })
764 .fill(clay_unbroken.clone());
765
766 painter
767 .aabb(Aabb {
768 min: (cyl_2_pos - 15).with_z(base - 10 - (room_size / 2)),
769 max: (cyl_2_pos + 15).with_z(base - 10),
770 })
771 .fill(clay_unbroken.clone());
772 painter
774 .line(
775 (center - (room_size / 4)).with_z(base - 10 - (room_size / 2)),
776 (center - (room_size / 2)).with_z(base - 5),
777 10.0,
778 )
779 .fill(clay_unbroken.clone());
780 let exit_pos = Vec2::new(
782 center.x - (room_size / 4) + 9,
783 center.y + 4 - (room_size / 4),
784 );
785 painter
786 .cylinder(Aabb {
787 min: (exit_pos - 4).with_z(base - 10 - (room_size / 2)),
788 max: (exit_pos + 5).with_z(base + 4 - (room_size / 2)),
789 })
790 .fill(clay_unbroken.clone());
791 painter
792 .aabb(Aabb {
793 min: Vec2::new(exit_pos.x + 5, exit_pos.y - 2).with_z(base - 10 - (room_size / 2)),
794 max: Vec2::new(exit_pos.x + 6, exit_pos.y + 3).with_z(base + 25 - (room_size / 2)),
795 })
796 .fill(clay_unbroken.clone());
797 painter
798 .aabb(Aabb {
799 min: Vec2::new(exit_pos.x + 5, exit_pos.y - 1).with_z(base - 10 - (room_size / 2)),
800 max: Vec2::new(exit_pos.x + 6, exit_pos.y + 2).with_z(base + 6 - (room_size / 2)),
801 })
802 .clear();
803 painter
804 .cylinder(Aabb {
805 min: (exit_pos - 3).with_z(base - 10 - (room_size / 2)),
806 max: (exit_pos + 4).with_z(base + 10 - (room_size / 2)),
807 })
808 .clear();
809 painter
810 .aabb(Aabb {
811 min: Vec2::new(exit_pos.x + 4, exit_pos.y - 1).with_z(base + 3 - (room_size / 2)),
812 max: Vec2::new(exit_pos.x + 5, exit_pos.y + 2).with_z(base + 6 - (room_size / 2)),
813 })
814 .clear();
815 painter
816 .aabb(Aabb {
817 min: Vec2::new(exit_pos.x + 4, exit_pos.y - 1).with_z(base + 2 - (room_size / 2)),
818 max: Vec2::new(exit_pos.x + 5, exit_pos.y + 2).with_z(base + 3 - (room_size / 2)),
819 })
820 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
821 painter
822 .aabb(Aabb {
823 min: Vec2::new(exit_pos.x + 5, exit_pos.y - 1).with_z(base - 10 - (room_size / 2)),
824 max: Vec2::new(exit_pos.x + 6, exit_pos.y + 2).with_z(base + 6 - (room_size / 2)),
825 })
826 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyDoor)));
827 painter
828 .aabb(Aabb {
829 min: Vec2::new(exit_pos.x + 5, exit_pos.y).with_z(base - 9 - (room_size / 2)),
830 max: Vec2::new(exit_pos.x + 6, exit_pos.y + 1).with_z(base - 8 - (room_size / 2)),
831 })
832 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyhole)));
833 let chests_position = exit_pos + 8;
835 painter
836 .cylinder(Aabb {
837 min: (chests_position - 4).with_z(base - 10 - (room_size / 2)),
838 max: (chests_position + 5).with_z(base - 9 - (room_size / 2)),
839 })
840 .fill(roof_color.clone());
841 painter
842 .cylinder(Aabb {
843 min: (chests_position - 3).with_z(base - 10 - (room_size / 2)),
844 max: (chests_position + 4).with_z(base - 9 - (room_size / 2)),
845 })
846 .fill(clay_unbroken.clone());
847 for dir in CARDINALS {
848 let chest_pos = chests_position + (dir * 2);
849 painter.sprite(
850 chest_pos.with_z(base - 9 - (room_size / 2)),
851 SpriteKind::TerracottaChest,
852 );
853 }
854 for dir in DIAGONALS {
857 let pos = center + dir * ((size / 2) + 6);
858 painter
859 .cylinder(Aabb {
860 min: (pos - 3).with_z(base - (room_size / 2)),
861 max: (pos + 3).with_z(base + 3),
862 })
863 .fill(clay_broken.clone());
864 painter
865 .cylinder(Aabb {
866 min: (pos - 4).with_z(base + 3),
867 max: (pos + 4).with_z(base + 4),
868 })
869 .fill(clay_broken_2.clone());
870 painter
871 .cylinder(Aabb {
872 min: (pos - 5).with_z(base + 4),
873 max: (pos + 5).with_z(base + 5),
874 })
875 .fill(clay_broken_2.clone());
876 painter
877 .cylinder(Aabb {
878 min: (pos - 6).with_z(base + 5),
879 max: (pos + 6).with_z(base + 6),
880 })
881 .fill(clay_broken.clone());
882 painter
883 .cylinder(Aabb {
884 min: (pos - 4).with_z(base + 5),
885 max: (pos + 4).with_z(base + 6),
886 })
887 .fill(Fill::Block(Block::air(SpriteKind::TerracottaBlock)));
888 painter
889 .cylinder(Aabb {
890 min: (pos - 4).with_z(base + 4),
891 max: (pos + 4).with_z(base + 5),
892 })
893 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
894 painter
895 .cylinder(Aabb {
896 min: (pos - 1).with_z(base + 4),
897 max: (pos + 1).with_z(base + 6),
898 })
899 .fill(clay_broken.clone());
900
901 let pillar_npc_pos = (center + dir * ((size / 2) + 9)).with_z(base + 6);
902 let statue_pos = (center + dir * ((size / 2) + 5)).with_z(base + 6);
903
904 spawn_random_entity(pillar_npc_pos, painter, 1..=1);
905
906 let statue = statue_npcs
907 .swap_remove(RandomField::new(0).get(statue_pos) as usize % statue_npcs.len());
908 painter.spawn(
909 EntityInfo::at((statue_pos).as_()).with_asset_expect(statue, &mut rng, None),
910 );
911 }
912 painter
914 .line(
915 (center - (room_size / 4)).with_z(base - 10 - (room_size / 2)),
916 (center - (room_size / 2)).with_z(base),
917 7.0,
918 )
919 .clear();
920 painter
921 .cylinder(Aabb {
922 min: (center - (room_size / 2) - 10).with_z(base),
923 max: (center - (room_size / 2) + 10).with_z(base + 10),
924 })
925 .clear();
926 painter
928 .cylinder(Aabb {
929 min: (center - (room_size / 8) + 3).with_z(base - 20),
930 max: (center + (room_size / 8) - 3).with_z(base + (3 * (room_size / 10)) + 2),
931 })
932 .clear();
933 painter
934 .cylinder(Aabb {
935 min: (center - (room_size / 8) + 3).with_z(base + (3 * (room_size / 10)) + 1),
936 max: (center + (room_size / 8) - 3).with_z(base + (3 * (room_size / 10)) + 2),
937 })
938 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
939 painter
940 .cylinder(Aabb {
941 min: (center - 2).with_z(base + (3 * (room_size / 10)) + 1),
942 max: (center + 2).with_z(base + (3 * (room_size / 10)) + 2),
943 })
944 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyDoor)));
945 painter
946 .cylinder(Aabb {
947 min: (center).with_z(base + (3 * (room_size / 10)) + 1),
948 max: (center + 1).with_z(base + (3 * (room_size / 10)) + 2),
949 })
950 .fill(Fill::Block(Block::air(SpriteKind::TerracottaKeyhole)));
951 painter
953 .aabb(Aabb {
954 min: (center - (room_size / 2)).with_z(base - 10 - room_size),
955 max: (center + (room_size / 2)).with_z(base - 10 - (room_size / 2)),
956 })
957 .fill(clay_unbroken.clone());
958 let lamps_top = 6.0_f32;
961 let radius_lamps_top = (room_size / 4) + 3;
962 let phi_lamps_top = TAU / lamps_top;
963 for n in 1..=lamps_top as i32 {
964 let pos = Vec2::new(
965 center.x + (radius_lamps_top as f32 * ((n as f32 * phi_lamps_top).cos())) as i32,
966 center.y + (radius_lamps_top as f32 * ((n as f32 * phi_lamps_top).sin())) as i32,
967 );
968 painter.sprite(
969 pos.with_z(base + (3 * (room_size / 10)) + 2),
970 SpriteKind::TerracottaStatue,
971 );
972 }
973 let chests = 4.0_f32;
974 let radius_chests = (room_size / 4) + 6;
975 let phi_chests = TAU / chests;
976 for n in 1..=chests as i32 {
977 let pos = Vec2::new(
978 center.x + (radius_chests as f32 * ((n as f32 * phi_chests).cos())) as i32,
979 center.y + (radius_chests as f32 * ((n as f32 * phi_chests).sin())) as i32,
980 );
981 painter.sprite(
982 pos.with_z(base + (3 * (room_size / 10)) + 2),
983 SpriteKind::TerracottaChest,
984 );
985 }
986 let radius_lamps_main = (room_size / 4) + 12;
988 let lamps_main = 8.0_f32;
989 let phi_lamps_main = TAU / lamps_main;
990 for n in 1..=lamps_main as i32 {
991 let pos = Vec2::new(
992 center.x + (radius_lamps_main as f32 * ((n as f32 * phi_lamps_main).cos())) as i32,
993 center.y + (radius_lamps_main as f32 * ((n as f32 * phi_lamps_main).sin())) as i32,
994 );
995 painter.sprite(pos.with_z(base), SpriteKind::TerracottaStatue);
996 }
997 let radius_statues_cellar = (room_size / 2) - 12;
999 let statues_cellar = 11.0_f32;
1000 let phi_statues_cellar = TAU / statues_cellar;
1001 for n in 1..=statues_cellar as i32 {
1002 let pos = Vec2::new(
1003 center.x
1004 + (radius_statues_cellar as f32 * ((n as f32 * phi_statues_cellar).cos()))
1005 as i32,
1006 center.y
1007 + (radius_statues_cellar as f32 * ((n as f32 * phi_statues_cellar).sin()))
1008 as i32,
1009 );
1010 match n {
1011 1 | 5 | 9 => {
1012 painter
1013 .cylinder(Aabb {
1014 min: (pos - 8).with_z(base - 22 - (room_size / 2)),
1015 max: (pos + 8).with_z(base - 10 - (room_size / 2)),
1016 })
1017 .clear();
1018 painter
1019 .cylinder(Aabb {
1020 min: (pos - 8).with_z(base - 22 - (room_size / 2)),
1021 max: (pos + 8).with_z(base - 21 - (room_size / 2)),
1022 })
1023 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
1024 painter
1025 .cylinder(Aabb {
1026 min: (pos - 8).with_z(base - 11 - (room_size / 2)),
1027 max: (pos + 8).with_z(base - 10 - (room_size / 2)),
1028 })
1029 .fill(Fill::Block(Block::air(SpriteKind::TerracottaBlock)));
1030 painter
1031 .cylinder(Aabb {
1032 min: (pos).with_z(base - 22 - (room_size / 2)),
1033 max: (pos + 1).with_z(base - 10 - (room_size / 2)),
1034 })
1035 .fill(clay_unbroken.clone());
1036 let cellar_statue_pos = pos.with_z(base - 10 - (room_size / 2));
1037 let cellar_statue = cellar_statue_npcs.swap_remove(
1038 RandomField::new(0).get(cellar_statue_pos) as usize
1039 % cellar_statue_npcs.len(),
1040 );
1041 painter.spawn(EntityInfo::at(cellar_statue_pos.as_()).with_asset_expect(
1042 cellar_statue,
1043 &mut rng,
1044 None,
1045 ));
1046 },
1047 _ => {
1048 painter.sprite(
1049 pos.with_z(base - 10 - (room_size / 2)),
1050 SpriteKind::TerracottaStatue,
1051 );
1052 },
1053 }
1054 }
1055 let npcs = 5.0_f32;
1057 let radius_npcs = (room_size / 4) + 6;
1058 let phi_npcs = TAU / npcs;
1059 for n in 1..=npcs as i32 {
1060 let pos = Vec2::new(
1061 center.x + (radius_npcs as f32 * ((n as f32 * phi_npcs).cos())) as i32,
1062 center.y + (radius_npcs as f32 * ((n as f32 * phi_npcs).sin())) as i32,
1063 )
1064 .with_z(base + (3 * (room_size / 10)) + 2);
1065 spawn_random_entity(pos, painter, 1..=1);
1066 }
1067 painter.spawn(
1069 EntityInfo::at((center.with_z(base + (3 * (room_size / 10)) + 2)).as_())
1070 .with_asset_expect("common.entity.dungeon.terracotta.mogwai", &mut rng, None),
1071 );
1072
1073 let radius_npcs_main = (room_size / 4) + 10;
1075 let npcs_main = 15.0_f32;
1076 let phi_npcs_main = TAU / npcs_main;
1077 for n in 1..=npcs_main as i32 {
1078 let pos = Vec2::new(
1079 center.x + (radius_npcs_main as f32 * ((n as f32 * phi_npcs_main).cos())) as i32,
1080 center.y + (radius_npcs_main as f32 * ((n as f32 * phi_npcs_main).sin())) as i32,
1081 )
1082 .with_z(base);
1083 spawn_random_entity(pos, painter, 1..=1);
1084 }
1085 }
1086}
1087
1088pub fn spawn_random_entity(pos: Vec3<i32>, painter: &Painter, amount: RangeInclusive<i32>) {
1089 let mut rng = thread_rng();
1090 let entities = [
1091 "common.entity.dungeon.terracotta.besieger",
1092 "common.entity.dungeon.terracotta.demolisher",
1093 "common.entity.dungeon.terracotta.punisher",
1094 "common.entity.dungeon.terracotta.pursuer",
1095 "common.entity.dungeon.terracotta.shamanic_spirit",
1096 "common.entity.dungeon.terracotta.jiangshi",
1097 ];
1098 for n in amount {
1099 let random_entity_index = rng.gen_range(0..entities.len());
1100 let random_entity = entities[random_entity_index];
1101 let position = Vec3::new(pos.x + n, pos.y + n, pos.z);
1102 painter.spawn(EntityInfo::at(position.as_()).with_asset_expect(
1103 random_entity,
1104 &mut rng,
1105 None,
1106 ));
1107 }
1108}