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