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