1use super::*;
2use crate::{
3 Land,
4 site2::gen::place_circular,
5 util::{NEIGHBORS, RandomField, Sampler},
6};
7use common::generation::EntityInfo;
8use rand::prelude::*;
9use std::sync::Arc;
10use vek::*;
11
12pub struct ArenaData {
13 base: i32,
14 center: Vec2<i32>,
15 entry_pos: Vec2<i32>,
16 boss_pos: Vec2<i32>,
17 radius: i32,
18}
19
20pub struct MyrmidonArena {
22 bounds: Aabr<i32>,
24 pub(crate) alt: i32,
26 pub(crate) arena_data: ArenaData,
27}
28
29impl MyrmidonArena {
30 pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
31 let bounds = Aabr {
32 min: site.tile_wpos(tile_aabr.min),
33 max: site.tile_wpos(tile_aabr.max),
34 };
35 let center = bounds.center();
36 let base = land.get_alt_approx(center) as i32 + 2;
37 let diameter = (bounds.max.x - bounds.min.x).min(bounds.max.y - bounds.min.y) - 20;
38 let radius = diameter / 2;
39 let entry_pos = Vec2::new(center.x, center.y + radius - 12);
40 let boss_pos = Vec2::new(center.x, center.y - radius + 12);
41
42 let arena_data = ArenaData {
43 base,
44 center,
45 entry_pos,
46 boss_pos,
47 radius,
48 };
49
50 Self {
51 bounds,
52 alt: base,
53 arena_data,
54 }
55 }
56
57 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
58 SpawnRules {
59 waypoints: false,
60 trees: wpos.distance_squared(self.bounds.center()) > (85_i32).pow(2),
61 ..SpawnRules::default()
62 }
63 }
64}
65
66impl Structure for MyrmidonArena {
67 #[cfg(feature = "use-dyn-lib")]
68 const UPDATE_FN: &'static [u8] = b"render_myrmidon_arena\0";
69
70 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_myrmidon_arena")]
71 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
72 let base = self.arena_data.base + 1;
73 let center = self.arena_data.center;
74 let mut thread_rng = thread_rng();
75 let sandstone_unbroken = Fill::Sampling(Arc::new(|center| {
76 Some(match (RandomField::new(0).get(center)) % 37 {
77 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
78 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
79 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
80 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
81 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
82 })
83 }));
84 let sandstone = Fill::Sampling(Arc::new(|center| {
85 Some(match (RandomField::new(0).get(center)) % 42 {
86 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
87 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
88 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
89 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
90 36..=37 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
91 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
92 })
93 }));
94 let weak_sandstone = Fill::Sampling(Arc::new(|center| {
95 Some(match (RandomField::new(0).get(center)) % 42 {
96 0..=8 => Block::new(BlockKind::WeakRock, Rgb::new(245, 212, 129)),
97 9..=17 => Block::new(BlockKind::WeakRock, Rgb::new(246, 214, 133)),
98 18..=26 => Block::new(BlockKind::WeakRock, Rgb::new(247, 216, 136)),
99 27..=35 => Block::new(BlockKind::WeakRock, Rgb::new(248, 219, 142)),
100 36..=37 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
101 _ => Block::new(BlockKind::WeakRock, Rgb::new(235, 178, 99)),
102 })
103 }));
104 let roof_color = Fill::Brick(BlockKind::Sand, Rgb::new(115, 32, 2), 12);
105 let radius = self.arena_data.radius;
106 painter
108 .cylinder(Aabb {
109 min: (center - radius).with_z(base - 80),
110 max: (center + radius).with_z(base),
111 })
112 .fill(sandstone_unbroken.clone());
113 painter
115 .cylinder(Aabb {
116 min: (center - radius + 30).with_z(base - 20),
117 max: (center + radius - 30).with_z(base - 12),
118 })
119 .clear();
120 painter
121 .cylinder(Aabb {
122 min: (center - radius + 33).with_z(base - 12),
123 max: (center + radius - 33).with_z(base - 11),
124 })
125 .clear();
126 painter
127 .cylinder(Aabb {
128 min: (center - radius + 20).with_z(base - 11),
129 max: (center + radius - 20).with_z(base),
130 })
131 .clear();
132 painter
133 .cylinder(Aabb {
134 min: (center - radius + 15).with_z(base - 11),
135 max: (center + radius - 15).with_z(base - 1),
136 })
137 .clear();
138 painter
140 .cylinder(Aabb {
141 min: (center - radius + 5).with_z(base),
142 max: (center + radius - 5).with_z(base + 30),
143 })
144 .clear();
145
146 let circle_radius = radius / 5;
148 painter
149 .cylinder(Aabb {
150 min: (center - circle_radius).with_z(base - 20),
151 max: (center + circle_radius).with_z(base - 19),
152 })
153 .fill(sandstone_unbroken.clone());
154 painter
155 .cylinder(Aabb {
156 min: (center - circle_radius + 1).with_z(base - 19),
157 max: (center + circle_radius - 1).with_z(base - 18),
158 })
159 .fill(sandstone.clone());
160 painter
161 .cylinder(Aabb {
162 min: (center - circle_radius + 2).with_z(base - 19),
163 max: (center + circle_radius - 2).with_z(base - 18),
164 })
165 .clear();
166
167 for dir in CARDINALS {
168 let clear_pos = center + dir * circle_radius;
169 let clear_rand = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
170 if clear_rand < 1 {
171 painter
172 .cylinder(Aabb {
173 min: (clear_pos - 6).with_z(base - 19),
174 max: (clear_pos + 6).with_z(base - 18),
175 })
176 .clear();
177 }
178 }
179 let pillars = 3 + (RandomField::new(0).get(center.with_z(base + 2)) % 3) as i32;
180 let center_pillar_positions = place_circular(center, (circle_radius - 5) as f32, pillars);
181 for pillar in center_pillar_positions {
182 let pillar_rand = RandomField::new(0).get(pillar.with_z(base)) % 3;
183 if pillar_rand > 0 {
184 let pillar_heigth = pillar_rand as i32;
185 painter
186 .cylinder(Aabb {
187 min: (pillar - 3).with_z(base - 19),
188 max: (pillar + 3).with_z(base - 18),
189 })
190 .fill(sandstone.clone());
191 painter
192 .cylinder(Aabb {
193 min: (pillar - 2).with_z(base - 18),
194 max: (pillar + 2).with_z(base - 18 + pillar_heigth),
195 })
196 .fill(sandstone.clone());
197 for dir in CARDINALS {
198 let clear_pos = pillar + dir * 3;
199 let clear_var = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
200
201 if clear_var > 0 {
202 painter
203 .sphere_with_radius(clear_pos.with_z(base - 18 + pillar_heigth), 3.0)
204 .clear();
205 }
206 }
207 }
208 }
209 let pillar_positions = place_circular(center, (radius - 5) as f32, 60);
211 let outer_pillar_positions = place_circular(center, (radius + 5) as f32, 60);
212 let top_decor_positions = place_circular(center, radius as f32, 100);
213
214 let top_platform_height = 45;
215 let platform_1_height = top_platform_height / 3;
216 let platform_2_height = platform_1_height * 2;
217 painter
219 .cylinder(Aabb {
220 min: (center - radius - 7).with_z(base + platform_1_height),
221 max: (center + radius + 7).with_z(base + platform_1_height + 1),
222 })
223 .fill(sandstone.clone());
224 painter
225 .cylinder(Aabb {
226 min: (center - radius + 5).with_z(base + platform_1_height),
227 max: (center + radius - 5).with_z(base + platform_1_height + 1),
228 })
229 .clear();
230 painter
232 .cylinder(Aabb {
233 min: (center - radius - 7).with_z(base + platform_2_height),
234 max: (center + radius + 7).with_z(base + platform_2_height + 1),
235 })
236 .fill(sandstone.clone());
237 painter
238 .cylinder(Aabb {
239 min: (center - radius + 5).with_z(base + platform_2_height),
240 max: (center + radius - 5).with_z(base + platform_2_height + 1),
241 })
242 .clear();
243 for pillar_pos in pillar_positions {
244 painter
245 .cylinder(Aabb {
246 min: (pillar_pos - 5).with_z(base - 1),
247 max: (pillar_pos + 5).with_z(base + (2 * (top_platform_height / 3))),
248 })
249 .fill(sandstone.clone());
250 painter
251 .cylinder(Aabb {
252 min: (pillar_pos - 3).with_z(base + (2 * (top_platform_height / 3))),
253 max: (pillar_pos + 3).with_z(base + (2 * (top_platform_height / 3)) + 1),
254 })
255 .fill(sandstone.clone());
256 painter
257 .cylinder(Aabb {
258 min: (pillar_pos - 2).with_z(base + (2 * (top_platform_height / 3)) + 1),
259 max: (pillar_pos + 2).with_z(base + top_platform_height),
260 })
261 .fill(sandstone.clone());
262 painter
263 .cylinder(Aabb {
264 min: (pillar_pos - 5).with_z(base + top_platform_height),
265 max: (pillar_pos + 5).with_z(base + top_platform_height + 1),
266 })
267 .fill(sandstone.clone());
268 }
269 for outer_pillar_pos in outer_pillar_positions {
270 painter
271 .cylinder(Aabb {
272 min: (outer_pillar_pos - 3).with_z(base - 10),
273 max: (outer_pillar_pos + 3).with_z(base - 5),
274 })
275 .fill(sandstone_unbroken.clone());
276 painter
277 .cylinder(Aabb {
278 min: (outer_pillar_pos - 3).with_z(base - 5),
279 max: (outer_pillar_pos + 3).with_z(base - 1),
280 })
281 .fill(sandstone.clone());
282 painter
283 .cylinder(Aabb {
284 min: (outer_pillar_pos - 3).with_z(base + platform_1_height + 1),
285 max: (outer_pillar_pos + 3).with_z(base + platform_1_height + 2),
286 })
287 .fill(sandstone.clone());
288 painter
289 .cylinder(Aabb {
290 min: (outer_pillar_pos - 3).with_z(base + platform_2_height + 1),
291 max: (outer_pillar_pos + 3).with_z(base + platform_2_height + 2),
292 })
293 .fill(sandstone.clone());
294
295 painter
296 .cylinder(Aabb {
297 min: (outer_pillar_pos - 2).with_z(base - 1),
298 max: (outer_pillar_pos + 2).with_z(base + top_platform_height),
299 })
300 .fill(sandstone.clone());
301
302 painter
303 .cylinder(Aabb {
304 min: (outer_pillar_pos - 5).with_z(base + top_platform_height),
305 max: (outer_pillar_pos + 5).with_z(base + top_platform_height + 1),
306 })
307 .fill(sandstone.clone());
308 }
309
310 for top_decor_pos in top_decor_positions {
311 for d in 0..4 {
312 painter
313 .cylinder(Aabb {
314 min: (top_decor_pos - 6 + d).with_z(base + top_platform_height + d),
315 max: (top_decor_pos + 6 - d).with_z(base + top_platform_height + 1 + d),
316 })
317 .fill(sandstone.clone());
318 }
319
320 let decay = (RandomField::new(0).get(top_decor_pos.with_z(base)) % 6) as i32;
321
322 if decay < 1 {
323 let decay_rand =
324 12.0 + (RandomField::new(0).get(top_decor_pos.with_z(base)) % 6) as f32;
325
326 painter
327 .sphere_with_radius(
328 top_decor_pos.with_z(base + top_platform_height + 10),
329 decay_rand,
330 )
331 .clear();
332 }
333 }
334
335 let entry_pos = self.arena_data.entry_pos;
337 let entry_pillar_pos_1 = Vec2::new(center.x, center.y + radius - 16);
338 let pillar_x_offset = [-12, 12];
339 let pillar_y_offset = [0, 29];
340
341 for pillar_x_dir in pillar_x_offset {
342 for pillar_y_dir in pillar_y_offset {
343 let pillar_pos = Vec2::new(
344 entry_pillar_pos_1.x + pillar_x_dir,
345 entry_pillar_pos_1.y + pillar_y_dir,
346 );
347
348 painter
349 .cylinder(Aabb {
350 min: (pillar_pos - 3).with_z(base - 2),
351 max: (pillar_pos + 3).with_z(base + 1),
352 })
353 .fill(sandstone.clone());
354
355 painter
356 .cylinder(Aabb {
357 min: (pillar_pos - 2).with_z(base + 1),
358 max: (pillar_pos + 2).with_z(base + platform_2_height),
359 })
360 .fill(sandstone.clone());
361 painter
362 .cylinder(Aabb {
363 min: (pillar_pos - 3).with_z(base + platform_2_height),
364 max: (pillar_pos + 3).with_z(base + platform_2_height + 1),
365 })
366 .fill(sandstone.clone());
367 }
368 }
369
370 painter
371 .vault(
372 Aabb {
373 min: Vec2::new(entry_pos.x - 15, entry_pos.y).with_z(base - 2),
374 max: Vec2::new(entry_pos.x + 15, entry_pos.y + 12)
375 .with_z(base + platform_2_height),
376 },
377 Dir::Y,
378 )
379 .fill(sandstone.clone());
380 painter
381 .vault(
382 Aabb {
383 min: Vec2::new(entry_pos.x - 10, entry_pos.y).with_z(base),
384 max: Vec2::new(entry_pos.x + 10, entry_pos.y + 12)
385 .with_z(base + platform_2_height - 5),
386 },
387 Dir::Y,
388 )
389 .clear();
390 let height_handle = 5;
391 painter
392 .aabb(Aabb {
393 min: Vec2::new(entry_pos.x - 15, entry_pos.y - 7)
394 .with_z(base + platform_2_height - 1 + height_handle),
395 max: Vec2::new(entry_pos.x + 15, entry_pos.y + 28)
396 .with_z(base + platform_2_height + height_handle),
397 })
398 .fill(sandstone.clone());
399
400 painter
401 .aabb(Aabb {
402 min: Vec2::new(entry_pos.x - 16, entry_pos.y - 8)
403 .with_z(base + platform_2_height - 2 + height_handle),
404 max: Vec2::new(entry_pos.x + 16, entry_pos.y + 29)
405 .with_z(base + platform_2_height - 1 + height_handle),
406 })
407 .fill(sandstone.clone());
408 painter
409 .aabb(Aabb {
410 min: Vec2::new(entry_pos.x - 15, entry_pos.y - 7)
411 .with_z(base + platform_2_height - 3 + height_handle),
412 max: Vec2::new(entry_pos.x + 15, entry_pos.y + 28)
413 .with_z(base + platform_2_height - 2 + height_handle),
414 })
415 .fill(sandstone.clone());
416 painter
417 .aabb(Aabb {
418 min: Vec2::new(entry_pos.x - 16, entry_pos.y - 8)
419 .with_z(base + platform_2_height - 4 + height_handle),
420 max: Vec2::new(entry_pos.x + 16, entry_pos.y + 29)
421 .with_z(base + platform_2_height - 3 + height_handle),
422 })
423 .fill(sandstone.clone());
424
425 painter
426 .gable(
427 Aabb {
428 min: Vec2::new(entry_pos.x - 18, entry_pos.y - 8)
429 .with_z(base + platform_2_height + height_handle),
430 max: Vec2::new(entry_pos.x + 18, entry_pos.y + 29)
431 .with_z(base + platform_2_height + 4 + height_handle),
432 },
433 16,
434 Dir::Y,
435 )
436 .fill(sandstone.clone());
437
438 painter
439 .gable(
440 Aabb {
441 min: Vec2::new(entry_pos.x - 16, entry_pos.y - 6)
442 .with_z(base + platform_2_height + height_handle),
443 max: Vec2::new(entry_pos.x + 16, entry_pos.y + 27)
444 .with_z(base + platform_2_height + 5 + height_handle),
445 },
446 16,
447 Dir::Y,
448 )
449 .fill(roof_color.clone());
450 for g in 0..8 {
451 painter
452 .gable(
453 Aabb {
454 min: Vec2::new(entry_pos.x - 17, entry_pos.y - 4 + (4 * g))
455 .with_z(base + platform_2_height + height_handle),
456 max: Vec2::new(entry_pos.x + 17, entry_pos.y - 3 + (4 * g))
457 .with_z(base + platform_2_height + 6 + height_handle),
458 },
459 16,
460 Dir::Y,
461 )
462 .fill(roof_color.clone());
463 }
464 painter
466 .vault(
467 Aabb {
468 min: Vec2::new(entry_pos.x - 3, entry_pos.y - 19).with_z(base - 20),
469 max: Vec2::new(entry_pos.x + 3, entry_pos.y + 5).with_z(base - 12),
470 },
471 Dir::Y,
472 )
473 .clear();
474 painter
475 .aabb(Aabb {
476 min: Vec2::new(entry_pos.x - 3, entry_pos.y - 1).with_z(base - 20),
477 max: Vec2::new(entry_pos.x + 3, entry_pos.y + 1).with_z(base),
478 })
479 .fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
480
481 painter
482 .aabb(Aabb {
483 min: Vec2::new(entry_pos.x - 1, entry_pos.y - 1).with_z(base - 18),
484 max: Vec2::new(entry_pos.x, entry_pos.y).with_z(base - 17),
485 })
486 .fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyhole)));
487
488 painter
489 .aabb(Aabb {
490 min: Vec2::new(entry_pos.x - 1, entry_pos.y + 4).with_z(base - 20),
491 max: Vec2::new(entry_pos.x + 1, entry_pos.y + 5).with_z(base - 19),
492 })
493 .fill(Fill::Block(Block::air(SpriteKind::DungeonChest4)));
494
495 let mob_positions_low = place_circular(center, (radius - 12) as f32, 20);
497 for npc_position in mob_positions_low {
498 let entities = [
499 "common.entity.dungeon.myrmidon.hoplite",
500 "common.entity.dungeon.myrmidon.marksman",
501 "common.entity.dungeon.myrmidon.strategian",
502 ];
503 let npc_pos = npc_position.with_z(base + 2);
504 let npc = entities[(RandomField::new(0).get(npc_pos) % entities.len() as u32) as usize];
505 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
506 npc,
507 &mut thread_rng,
508 None,
509 ));
510 }
511 let mob_positions_pit = place_circular(center, (radius / 3) as f32, 10);
512 for npc_position in mob_positions_pit {
513 let entities = [
514 "common.entity.dungeon.myrmidon.hoplite",
515 "common.entity.dungeon.myrmidon.marksman",
516 "common.entity.dungeon.myrmidon.strategian",
517 ];
518 let npc_pos = npc_position.with_z(base - 20);
519 let npc = entities[(RandomField::new(0).get(npc_pos) % entities.len() as u32) as usize];
520 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
521 npc,
522 &mut thread_rng,
523 None,
524 ));
525 }
526 let mob_positions_high = place_circular(center, radius as f32, 20);
527 for npc_position in mob_positions_high {
528 let npc_pos = npc_position.with_z(base + top_platform_height + 5);
529 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
530 "common.entity.dungeon.myrmidon.marksman",
531 &mut thread_rng,
532 None,
533 ));
534 }
535
536 let boss_pos = self.arena_data.boss_pos;
537 let npc_pos = Vec2::new(boss_pos.x - 50, boss_pos.y).with_z(base - 20);
538 painter
540 .aabb(Aabb {
541 min: (npc_pos - 12).with_z(base - 21),
542 max: (npc_pos + 12).with_z(base - 12),
543 })
544 .fill(sandstone_unbroken.clone());
545 painter
546 .vault(
547 Aabb {
548 min: Vec2::new(boss_pos.x - 4, boss_pos.y - 10).with_z(base - 22),
549 max: Vec2::new(boss_pos.x + 4, boss_pos.y + 18).with_z(base - 12),
550 },
551 Dir::Y,
552 )
553 .clear();
554 painter
555 .vault(
556 Aabb {
557 min: Vec2::new(boss_pos.x - 52, boss_pos.y - 4).with_z(base - 22),
558 max: Vec2::new(boss_pos.x, boss_pos.y + 4).with_z(base - 12),
559 },
560 Dir::X,
561 )
562 .clear();
563 painter
564 .vault(
565 Aabb {
566 min: Vec2::new(boss_pos.x - 4, boss_pos.y - 4).with_z(base - 22),
567 max: Vec2::new(boss_pos.x - 3, boss_pos.y + 4).with_z(base - 12),
568 },
569 Dir::X,
570 )
571 .fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
572 painter
573 .aabb(Aabb {
574 min: Vec2::new(boss_pos.x - 4, boss_pos.y + 4).with_z(base - 18),
575 max: Vec2::new(boss_pos.x - 3, boss_pos.y + 17).with_z(base - 17),
576 })
577 .fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
578 painter
579 .vault(
580 Aabb {
581 min: Vec2::new(boss_pos.x - 4, boss_pos.y + 17).with_z(base - 20),
582 max: Vec2::new(boss_pos.x + 4, boss_pos.y + 18).with_z(base - 12),
583 },
584 Dir::Y,
585 )
586 .fill(Fill::Block(Block::air(SpriteKind::MyrmidonKeyDoor)));
587 painter
588 .vault(
589 Aabb {
590 min: Vec2::new(boss_pos.x, boss_pos.y + 17).with_z(base - 18),
591 max: Vec2::new(boss_pos.x + 1, boss_pos.y + 18).with_z(base - 17),
592 },
593 Dir::Y,
594 )
595 .fill(Fill::Block(Block::air(SpriteKind::MinotaurKeyhole)));
596
597 let npc_pos = Vec2::new(boss_pos.x - 50, boss_pos.y).with_z(base - 20);
598
599 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
600 "common.entity.dungeon.myrmidon.minotaur",
601 &mut thread_rng,
602 None,
603 ));
604
605 painter
607 .cylinder(Aabb {
608 min: (center - 3 * (radius / 4)).with_z(base - 79),
609 max: (center + 3 * (radius / 4)).with_z(base - 21),
610 })
611 .fill(weak_sandstone.clone());
612 painter
614 .cylinder(Aabb {
615 min: (center - 5).with_z(base - 79),
616 max: (center + 1).with_z(base - 18),
617 })
618 .fill(sandstone_unbroken.clone());
619 painter
620 .cylinder(Aabb {
621 min: (center - 4).with_z(base - 28),
622 max: center.with_z(base - 14),
623 })
624 .fill(sandstone_unbroken.clone());
625 painter
626 .cylinder(Aabb {
627 min: (center - 4).with_z(base - 14),
628 max: center.with_z(base - 13),
629 })
630 .fill(Fill::Block(Block::air(SpriteKind::IronSpike)));
631 painter
632 .cylinder(Aabb {
633 min: (center - 3).with_z(base - 77),
634 max: (center - 1).with_z(base - 13),
635 })
636 .clear();
637 painter
638 .aabb(Aabb {
639 min: (center - 3).with_z(base - 43),
640 max: (center + 5).with_z(base - 33),
641 })
642 .clear();
643 painter
644 .cylinder(Aabb {
645 min: (center - 10).with_z(base - 79),
646 max: (center + 5).with_z(base - 77),
647 })
648 .clear();
649
650 for dir in NEIGHBORS {
651 for r in 1..=3 {
652 let room_pos = center + dir * ((radius / 6) * r);
653
654 for s in 0..3 {
655 let room_var = RandomField::new(0).get(room_pos.with_z(base + r + s)) % 6;
656 let room_dir = if room_var < 3 { Dir::Y } else { Dir::X };
657 let room = painter.vault(
658 Aabb {
659 min: (room_pos - (radius / 8)).with_z(base - 79 + (18 * s)),
660 max: (room_pos + (radius / 8)).with_z(base - 64 + (18 * s)),
661 },
662 room_dir,
663 );
664
665 match room_var {
666 0 => room.fill(weak_sandstone.clone()),
667 _ => room.clear(),
668 };
669
670 if room_var > 3 {
672 painter
673 .vault(
674 Aabb {
675 min: (room_pos - (radius / 8)).with_z(base - 79 + (18 * s)),
676 max: (room_pos + (radius / 8)).with_z(base - 59 + (18 * s)),
677 },
678 room_dir,
679 )
680 .clear();
681 }
682 }
683 }
684 }
685 let cyclops_pos_high = Vec2::new(center.x, center.y - 14).with_z(base - 40);
686 painter
687 .vault(
688 Aabb {
689 min: (cyclops_pos_high - (radius / 8)).with_z(base - 40),
690 max: (cyclops_pos_high + (radius / 8)).with_z(base - 25),
691 },
692 Dir::X,
693 )
694 .clear();
695 painter.spawn(EntityInfo::at(cyclops_pos_high.as_()).with_asset_expect(
696 "common.entity.dungeon.myrmidon.cyclops_key",
697 &mut thread_rng,
698 None,
699 ));
700 let cyclops_pos_low = Vec2::new(center.x, center.y + 14).with_z(base - 79);
701 painter
702 .vault(
703 Aabb {
704 min: (cyclops_pos_low - (radius / 8)).with_z(base - 79),
705 max: (cyclops_pos_low + (radius / 8)).with_z(base - 64),
706 },
707 Dir::X,
708 )
709 .clear();
710 painter.spawn(EntityInfo::at(cyclops_pos_low.as_()).with_asset_expect(
711 "common.entity.dungeon.myrmidon.cyclops",
712 &mut thread_rng,
713 None,
714 ));
715 let mob_positions_cellar = place_circular(center, 2.0, 3);
716 for npc_position in mob_positions_cellar {
717 let npc_pos = npc_position.with_z(base - 79);
718 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
719 "common.entity.dungeon.myrmidon.marksman",
720 &mut thread_rng,
721 None,
722 ));
723 }
724 }
725}