1use super::*;
2use crate::{
3 Land,
4 site2::gen::place_circular,
5 util::{CARDINALS, NEIGHBORS, RandomField, Sampler},
6};
7use common::generation::EntityInfo;
8use rand::prelude::*;
9use std::sync::Arc;
10use vek::*;
11
12pub struct MyrmidonHouse {
14 bounds: Aabr<i32>,
16 pub(crate) alt: i32,
18}
19
20impl MyrmidonHouse {
21 pub fn generate(
22 land: &Land,
23 _rng: &mut impl Rng,
24 site: &Site,
25 tile_aabr: Aabr<i32>,
26 alt: Option<i32>,
27 ) -> Self {
28 let bounds = Aabr {
29 min: site.tile_wpos(tile_aabr.min),
30 max: site.tile_wpos(tile_aabr.max),
31 };
32 Self {
33 bounds,
34 alt: alt.unwrap_or_else(|| {
35 land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
36 as i32
37 + 2
38 }),
39 }
40 }
41
42 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
43 SpawnRules {
44 waypoints: false,
45 trees: wpos.distance_squared(self.bounds.center()) > (85_i32).pow(2),
46 ..SpawnRules::default()
47 }
48 }
49}
50
51impl Structure for MyrmidonHouse {
52 #[cfg(feature = "use-dyn-lib")]
53 const UPDATE_FN: &'static [u8] = b"render_myrmidon_house\0";
54
55 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_myrmidon_house")]
56 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
57 let base = self.alt + 3;
58 let center = self.bounds.center();
59 let mut thread_rng = thread_rng();
60 let sandstone_unbroken = Fill::Sampling(Arc::new(|center| {
61 Some(match (RandomField::new(0).get(center)) % 37 {
62 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
63 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
64 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
65 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
66 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
67 })
68 }));
69 let sandstone = Fill::Sampling(Arc::new(|center| {
70 Some(match (RandomField::new(0).get(center)) % 42 {
71 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
72 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
73 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
74 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
75 36..=37 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
76 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
77 })
78 }));
79 let roof_color = Fill::Brick(BlockKind::Sand, Rgb::new(115, 32, 2), 12);
80 let diameter =
81 (self.bounds.max.x - self.bounds.min.x).min(self.bounds.max.y - self.bounds.min.y);
82 let bldg_var = RandomField::new(0).get(center.with_z(base + 5)) % 4;
83
84 if bldg_var < 1 {
85 let circle_radius = 2 * (diameter / 5);
87 painter
88 .cylinder(Aabb {
89 min: (center - circle_radius).with_z(base - 30),
90 max: (center + circle_radius).with_z(base - 1),
91 })
92 .fill(sandstone_unbroken.clone());
93 painter
94 .cylinder(Aabb {
95 min: (center - circle_radius + 1).with_z(base - 1),
96 max: (center + circle_radius - 1).with_z(base),
97 })
98 .fill(sandstone.clone());
99 painter
100 .cylinder(Aabb {
101 min: (center - circle_radius + 2).with_z(base - 1),
102 max: (center + circle_radius - 2).with_z(base + 15),
103 })
104 .clear();
105
106 for dir in CARDINALS {
107 let clear_pos = center + dir * circle_radius;
108 let clear_rand = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
109 if clear_rand < 1 {
110 painter
111 .cylinder(Aabb {
112 min: (clear_pos - 6).with_z(base - 1),
113 max: (clear_pos + 6).with_z(base),
114 })
115 .clear();
116 }
117 }
118 let pillars = 8 + (RandomField::new(0).get(center.with_z(base + 2)) % 6) as i32;
119 let pillar_positions = place_circular(center, (circle_radius - 5) as f32, pillars);
120 for pillar in pillar_positions {
121 let pillar_rand = RandomField::new(0).get(pillar.with_z(base)) % 5;
122 if pillar_rand > 0 {
123 let pillar_heigth = 10 + pillar_rand as i32;
124 painter
125 .cylinder(Aabb {
126 min: (pillar - 3).with_z(base - 1),
127 max: (pillar + 3).with_z(base),
128 })
129 .fill(sandstone.clone());
130 painter
131 .cylinder(Aabb {
132 min: (pillar - 2).with_z(base),
133 max: (pillar + 2).with_z(base + pillar_heigth),
134 })
135 .fill(sandstone.clone());
136 for dir in CARDINALS {
137 let clear_pos = pillar + dir * 3;
138 let clear_var = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
139
140 if clear_var > 0 {
141 painter
142 .sphere_with_radius(clear_pos.with_z(base + pillar_heigth), 3.0)
143 .clear();
144 }
145 }
146 }
147 }
148 } else {
149 painter
151 .aabb(Aabb {
152 min: (center - (diameter / 2)).with_z(base - 30),
153 max: (center + (diameter / 2)).with_z(base - 2),
154 })
155 .fill(sandstone_unbroken.clone());
156 painter
158 .aabb(Aabb {
159 min: Vec2::new(center.x - (diameter / 2), center.y - (diameter / 2))
160 .with_z(base - 2),
161 max: Vec2::new(center.x + (diameter / 2), center.y + (diameter / 2))
162 .with_z(base - 1),
163 })
164 .fill(sandstone.clone());
165 painter
166 .aabb(Aabb {
167 min: Vec2::new(center.x - (diameter / 2) + 1, center.y - (diameter / 2) + 1)
168 .with_z(base - 2),
169 max: Vec2::new(center.x + (diameter / 2) - 1, center.y + (diameter / 2) - 1)
170 .with_z(base + 15),
171 })
172 .clear();
173 for dir in CARDINALS {
174 let gate_pos = center + dir * (diameter / 2);
175 painter
176 .cylinder(Aabb {
177 min: (gate_pos - 6).with_z(base - 2),
178 max: (gate_pos + 6).with_z(base + 6),
179 })
180 .clear();
181 }
182
183 let rand = RandomField::new(0).get(center.with_z(base)) % 2;
185 let heigth_rand = (RandomField::new(0).get(center.with_z(base)) % 10) as i32;
186 let bldg_length = (diameter / 2) - 5;
187 let bldg_width = diameter / 3;
188 let bldg_height = (diameter / 4) + heigth_rand;
189 let (x_axis, y_axis) = if rand > 0 {
190 (bldg_length, bldg_width)
191 } else {
192 (bldg_width, bldg_length)
193 };
194
195 painter
198 .aabb(Aabb {
199 min: Vec2::new(center.x - x_axis, center.y - y_axis).with_z(base + bldg_height),
200 max: Vec2::new(center.x + x_axis, center.y + y_axis)
201 .with_z(base + bldg_height + 1),
202 })
203 .fill(sandstone.clone());
204 painter
205 .aabb(Aabb {
206 min: Vec2::new(center.x - x_axis + 1, center.y - y_axis + 1)
207 .with_z(base + bldg_height + 1),
208 max: Vec2::new(center.x + x_axis - 1, center.y + y_axis - 1)
209 .with_z(base + bldg_height + 2),
210 })
211 .fill(sandstone.clone());
212 painter
213 .aabb(Aabb {
214 min: Vec2::new(center.x - x_axis, center.y - y_axis)
215 .with_z(base + bldg_height + 2),
216 max: Vec2::new(center.x + x_axis, center.y + y_axis)
217 .with_z(base + bldg_height + 3),
218 })
219 .fill(sandstone.clone());
220 painter
221 .aabb(Aabb {
222 min: Vec2::new(center.x - x_axis + 1, center.y - y_axis + 1)
223 .with_z(base + bldg_height + 3),
224 max: Vec2::new(center.x + x_axis - 1, center.y + y_axis - 1)
225 .with_z(base + bldg_height + 4),
226 })
227 .fill(sandstone.clone());
228 let roof_dir = if rand > 0 { Dir::X } else { Dir::Y };
229 painter
230 .gable(
231 Aabb {
232 min: Vec2::new(center.x - x_axis, center.y - y_axis)
233 .with_z(base + bldg_height + 4),
234 max: Vec2::new(center.x + x_axis, center.y + y_axis)
235 .with_z(base + bldg_height + 8),
236 },
237 4 * (x_axis / 5),
238 roof_dir,
239 )
240 .fill(sandstone.clone());
241
242 painter
243 .gable(
244 Aabb {
245 min: Vec2::new(center.x - x_axis + 1, center.y - y_axis + 2)
246 .with_z(base + bldg_height + 5),
247 max: Vec2::new(center.x + x_axis - 1, center.y + y_axis - 2)
248 .with_z(base + bldg_height + 9),
249 },
250 4 * (x_axis / 5),
251 roof_dir,
252 )
253 .fill(roof_color.clone());
254 if rand > 0 {
255 for r in 0..((x_axis / 2) - 1) {
256 painter
257 .gable(
258 Aabb {
259 min: Vec2::new(
260 center.x - x_axis + 4 + (4 * r),
261 center.y - y_axis - 1,
262 )
263 .with_z(base + bldg_height + 6),
264 max: Vec2::new(
265 center.x - x_axis + 5 + (4 * r),
266 center.y + y_axis + 1,
267 )
268 .with_z(base + bldg_height + 10),
269 },
270 4 * (x_axis / 5),
271 roof_dir,
272 )
273 .fill(roof_color.clone());
274 }
275 } else {
276 for r in 0..((y_axis / 2) - 1) {
277 painter
278 .gable(
279 Aabb {
280 min: Vec2::new(
281 center.x - x_axis - 1,
282 center.y - y_axis + 4 + (4 * r),
283 )
284 .with_z(base + bldg_height + 6),
285 max: Vec2::new(
286 center.x + x_axis + 1,
287 center.y - y_axis + 5 + (4 * r),
288 )
289 .with_z(base + bldg_height + 10),
290 },
291 4 * (x_axis / 5),
292 roof_dir,
293 )
294 .fill(roof_color.clone());
295 }
296 }
297 let pillar_x_axis = 3 * (x_axis / 4);
299 let pillar_y_axis = 3 * (y_axis / 4);
300 for dir in NEIGHBORS {
301 let pillar_pos = Vec2::new(
302 center.x + dir.x * pillar_x_axis,
303 center.y + dir.y * pillar_y_axis,
304 );
305
306 painter
307 .cylinder(Aabb {
308 min: (pillar_pos - 3).with_z(base - 2),
309 max: (pillar_pos + 3).with_z(base - 1),
310 })
311 .fill(sandstone.clone());
312 painter
313 .cylinder(Aabb {
314 min: (pillar_pos - 2).with_z(base - 1),
315 max: (pillar_pos + 2).with_z(base + bldg_height - 2),
316 })
317 .fill(sandstone.clone());
318 for p in 0..3 {
319 painter
320 .cylinder(Aabb {
321 min: (pillar_pos - 3 - p).with_z(base + bldg_height - 3 + p),
322 max: (pillar_pos + 3 + p).with_z(base + bldg_height - 2 + p),
323 })
324 .fill(sandstone.clone());
325 }
326
327 let decay = (RandomField::new(0).get(pillar_pos.with_z(base)) % 6) as i32;
328
329 if decay < 1 {
330 let decay_rand =
331 12.0 + (RandomField::new(0).get(pillar_pos.with_z(base)) % 6) as f32;
332
333 painter
334 .sphere_with_radius(pillar_pos.with_z(base + bldg_height + 8), decay_rand)
335 .clear();
336 for dir in CARDINALS {
337 let clear_pos = pillar_pos + dir * 3;
338 let clear_var = RandomField::new(0).get(clear_pos.with_z(base)) % 2;
339
340 if clear_var > 0 {
341 painter
342 .sphere_with_radius(clear_pos.with_z(base + bldg_height - 4), 3.0)
343 .clear();
344 }
345 }
346 }
347 }
348 }
349
350 let amount = (RandomField::new(0).get(center.with_z(base + 3)) % 5) as i32;
352 for n in 0..amount {
353 let entities = [
354 "common.entity.dungeon.myrmidon.hoplite",
355 "common.entity.dungeon.myrmidon.marksman",
356 "common.entity.dungeon.myrmidon.strategian",
357 ];
358 let npc_pos = (center + n).with_z(base);
359 let npc = entities[(RandomField::new(0).get(npc_pos) % entities.len() as u32) as usize];
360 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
361 npc,
362 &mut thread_rng,
363 None,
364 ));
365 }
366 if amount < 1 {
367 if bldg_var > 0 {
368 painter
370 .aabb(Aabb {
371 min: (center - (diameter / 2) + 1).with_z(base - 29),
372 max: (center + (diameter / 2) - 1).with_z(base - 3),
373 })
374 .fill(sandstone.clone());
375
376 for dir in NEIGHBORS {
377 for r in 1..=3 {
378 let room_pos = center + dir * ((diameter / 7) * r);
379 for s in 0..3 {
380 let room_var =
381 RandomField::new(0).get(room_pos.with_z(base + r + s)) % 6;
382 let room_dir = if room_var < 3 { Dir::Y } else { Dir::X };
383
384 let room = painter.vault(
385 Aabb {
386 min: (room_pos - (diameter / 10)).with_z(base - 29 + (8 * s)),
387 max: (room_pos + (diameter / 10)).with_z(base - 23 + (8 * s)),
388 },
389 room_dir,
390 );
391
392 let chest_var =
393 RandomField::new(0).get(room_pos.with_z(base + r + s)) % 10;
394
395 match room_var {
396 0 => room.fill(sandstone.clone()),
397 _ => room.clear(),
398 };
399
400 if room_var > 3 {
402 let carve_heigth = if s < 2 { 12 } else { 8 };
403 painter
404 .vault(
405 Aabb {
406 min: (room_pos - (diameter / 10))
407 .with_z(base - 29 + (8 * s)),
408 max: (room_pos + (diameter / 10))
409 .with_z(base - 29 + carve_heigth + (8 * s)),
410 },
411 room_dir,
412 )
413 .clear();
414 }
415 if s < 1 && room_var > 1 && chest_var > 8 {
416 painter
417 .sprite(room_pos.with_z(base - 29), SpriteKind::DungeonChest4);
418 }
419 }
420 }
421 }
422 painter
423 .aabb(Aabb {
424 min: (center - (diameter / 10)).with_z(base - 6),
425 max: (center + (diameter / 10)).with_z(base + 2),
426 })
427 .clear();
428 }
429 let npc_pos = (center - 8).with_z(base);
430
431 painter.spawn(EntityInfo::at(npc_pos.as_()).with_asset_expect(
432 "common.entity.dungeon.myrmidon.cyclops",
433 &mut thread_rng,
434 None,
435 ));
436 }
437 }
438}