1use super::*;
2use crate::{
3 Land,
4 site::generation::{PrimitiveTransform, spiral_staircase},
5 util::{CARDINALS, DIAGONALS, RandomField, Sampler, within_distance},
6};
7use common::{
8 generation::SpecialEntity,
9 terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::{f32::consts::PI, sync::Arc};
13use vek::{ops::Lerp, *};
14
15pub struct AirshipDock {
17 pub(crate) alt: i32,
19 rotation: f32,
20 pub door_tile: Vec2<i32>,
21 pub center: Vec2<i32>,
22 base: i32,
23 upper_alt: i32,
24 min_foundation_alt: i32,
25 surface_colors: Vec<Rgb<u8>>,
26 sub_surface_colors: Vec<Rgb<u8>>,
27 pub docking_positions: Vec<Vec3<i32>>,
28 pub door_dir: Vec2<i32>,
29 campfire_pos: Vec3<i32>,
30}
31
32impl AirshipDock {
33 pub fn generate(
34 land: &Land,
35 index: IndexRef,
36 _rng: &mut impl Rng,
37 site: &Site,
38 door_tile: Vec2<i32>,
39 door_dir: Vec2<i32>,
40 tile_aabr: Aabr<i32>,
41 ) -> Self {
42 let door_tile_pos: Vec2<i32> = site.tile_center_wpos(door_tile);
49 let bounds = Aabr {
50 min: site.tile_wpos(tile_aabr.min),
51 max: site.tile_wpos(tile_aabr.max),
52 };
53 let center = bounds.center();
54 let halfspan = 66;
66 let min_clearance_x = center.x - halfspan;
67 let max_clearance_x = center.x + halfspan;
68 let min_clearance_y = center.y - halfspan;
69 let max_clearance_y = center.y + halfspan;
70 let mut max_surface_alt = i32::MIN;
71 let mut surface_colors = vec![];
72 let mut sub_surface_colors = vec![];
73 for x in (min_clearance_x..=max_clearance_x).step_by(22) {
77 for y in (min_clearance_y..=max_clearance_y).step_by(22) {
78 let pos = Vec2::new(x, y);
79 let alt = land.get_surface_alt_approx(pos) as i32;
80 if alt > max_surface_alt {
81 max_surface_alt = alt;
82 }
83 }
84 }
85
86 let min_foundation_x = center.x - 20;
90 let max_foundation_x = center.x + 20;
91 let min_foundation_y = center.y - 20;
92 let max_foundation_y = center.y + 20;
93 let mut max_foundation_alt = i32::MIN;
94 let mut min_foundation_alt = i32::MAX;
95 let color_component_to_u8 =
96 |compf32: f32| -> u8 { (compf32.clamp(0.0, 1.0) * 255.0).floor() as u8 };
97
98 for x in (min_foundation_x..=max_foundation_x).step_by(10) {
102 for y in (min_foundation_y..=max_foundation_y).step_by(10) {
103 let pos = Vec2::new(x, y);
104 let alt = land.get_surface_alt_approx(pos) as i32;
105 if alt > max_foundation_alt {
106 max_foundation_alt = alt;
107 }
108 if alt < min_foundation_alt {
109 min_foundation_alt = alt;
110 }
111 if let Some(sample) = land.column_sample(pos, index) {
112 surface_colors.push(sample.surface_color.map(color_component_to_u8));
113 sub_surface_colors.push(sample.sub_surface_color.map(color_component_to_u8));
114 }
115 }
116 }
117 let min_base = max_surface_alt - 36;
123 let base = min_base.max(max_foundation_alt);
124
125 let upper_alt = base + 28;
126 let rotation = -f32::atan2(door_dir.x as f32, door_dir.y as f32);
129 let mut docking_positions = vec![];
130 for dir in CARDINALS {
131 let pos = (center + dir * 31).with_z(upper_alt + 9);
132 docking_positions.push(pos);
133 }
134 let campfire_dir = Mat2::rotation_z(-PI / 4.0) * door_dir.map(|i| i as f32);
135 let campfire_pos = (center.map(|i| i as f32) + (campfire_dir * 11.0))
136 .map(|f| f.round() as i32)
137 .with_z(upper_alt + 9);
138 Self {
139 door_tile: door_tile_pos,
140 alt: base,
141 rotation,
142 center,
143 base,
144 upper_alt,
145 min_foundation_alt,
146 surface_colors,
147 sub_surface_colors,
148 docking_positions,
149 door_dir,
150 campfire_pos,
151 }
152 }
153
154 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
155 SpawnRules {
156 trees: {
157 const AIRSHIP_MIN_TREE_DIST2: i32 = 100;
163 !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST2)
164 },
165 waypoints: false,
166 ..SpawnRules::default()
167 }
168 }
169}
170
171impl Structure for AirshipDock {
172 #[cfg(feature = "use-dyn-lib")]
173 const UPDATE_FN: &'static [u8] = b"render_airshipdock\0";
174
175 #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_airshipdock"))]
176 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
177 let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
178 let wood = Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24);
179 let woodalt = Fill::Brick(BlockKind::Rock, Rgb::new(30, 22, 15), 24);
180 let grass = {
181 let surface_colors = self.surface_colors.clone();
182 let num_colors = surface_colors.len() as u32;
183 Fill::Sampling(Arc::new(move |wpos| {
184 let index = if num_colors > 0 {
185 (RandomField::new(0).get(wpos) % num_colors) as usize
186 } else {
187 0usize
188 };
189 Some(Block::new(
190 BlockKind::Earth,
191 surface_colors
192 .get(index)
193 .cloned()
194 .unwrap_or(Rgb::new(55, 25, 8)),
195 ))
196 }))
197 };
198 let dirt = {
199 let sub_surface_colors = self.sub_surface_colors.clone();
200 let num_colors = sub_surface_colors.len() as u32;
201 let avg_color = if num_colors > 0 {
202 let mut r_total: f32 = 0.0;
203 let mut g_total: f32 = 0.0;
204 let mut b_total: f32 = 0.0;
205 for color in &sub_surface_colors {
206 r_total += color.r as f32;
207 g_total += color.g as f32;
208 b_total += color.b as f32;
209 }
210 Rgb::new(
211 r_total / num_colors as f32,
212 g_total / num_colors as f32,
213 b_total / num_colors as f32,
214 )
215 } else {
216 Rgb::new(55.0, 25.0, 8.0)
217 };
218 Fill::Sampling(Arc::new(move |wpos| {
219 let color = if num_colors > 0 {
220 sub_surface_colors
221 .get((RandomField::new(0).get(wpos) % num_colors) as usize)
222 .copied()
223 .unwrap_or(Rgb::new(55, 25, 8))
224 .map(|u| u as f32)
225 } else {
226 avg_color
227 };
228 let lerp_factor = (RandomField::new(1).get(wpos) % 51 + 25) as f32 / 100.0;
231 let block_color = Rgb::new(
232 Lerp::lerp(avg_color.r, color.r, lerp_factor),
233 Lerp::lerp(avg_color.g, color.g, lerp_factor),
234 Lerp::lerp(avg_color.b, color.b, lerp_factor),
235 )
236 .map(|f| f.clamp(0.0, 255.0) as u8);
237 Some(Block::new(BlockKind::Earth, block_color))
238 }))
239 };
240 let base = self.base;
241 let center = self.center;
242 let upper_alt = self.upper_alt;
243 let min_foundation_alt = self.min_foundation_alt;
244
245 painter
251 .cylinder_with_radius(center.with_z(upper_alt - 3), 7.0, 1.0)
252 .fill(wood.clone());
253 painter
254 .cylinder_with_radius(center.with_z(upper_alt + 6), 7.0, 1.0)
255 .fill(wood.clone());
256 painter
257 .cylinder_with_radius(center.with_z(upper_alt + 7), 8.0, 1.0)
258 .fill(wood.clone());
259
260 painter
262 .superquadric(
263 Aabb {
264 min: (center - 8000).with_z(upper_alt + 7),
265 max: (center + 8000).with_z(upper_alt + 11),
266 },
267 0.3,
268 )
269 .intersect(painter.aabb(Aabb {
270 min: (center - 31).with_z(upper_alt + 7),
271 max: (center + 31).with_z(upper_alt + 11),
272 }))
273 .fill(woodalt.clone());
274 painter
275 .cylinder_with_radius(center.with_z(upper_alt + 8), 19.0, 2.0)
276 .fill(woodalt.clone());
277 painter
278 .cylinder_with_radius(center.with_z(upper_alt + 8), 18.0, 2.0)
279 .fill(wood.clone());
280 painter
281 .cylinder_with_radius(center.with_z(upper_alt + 9), 18.0, 1.0)
282 .clear();
283 painter
284 .aabb(Aabb {
285 min: Vec2::new(center.x - 30, center.y - 2).with_z(upper_alt + 8),
286 max: Vec2::new(center.x + 30, center.y + 2).with_z(upper_alt + 10),
287 })
288 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
289 .fill(wood.clone());
290 painter
291 .aabb(Aabb {
292 min: Vec2::new(center.x - 30, center.y - 2).with_z(upper_alt + 9),
293 max: Vec2::new(center.x + 30, center.y + 2).with_z(upper_alt + 10),
294 })
295 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
296 .clear();
297 painter
298 .aabb(Aabb {
299 min: Vec2::new(center.x - 2, center.y - 30).with_z(upper_alt + 8),
300 max: Vec2::new(center.x + 2, center.y + 30).with_z(upper_alt + 10),
301 })
302 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
303 .fill(wood.clone());
304 painter
305 .aabb(Aabb {
306 min: Vec2::new(center.x - 2, center.y - 30).with_z(upper_alt + 9),
307 max: Vec2::new(center.x + 2, center.y + 30).with_z(upper_alt + 10),
308 })
309 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
310 .clear();
311
312 painter
314 .cylinder_with_radius(center.with_z(upper_alt + 9), 9.0, 6.0)
315 .intersect(painter.aabb(Aabb {
316 min: (Vec2::new(center.x + 2, center.y - 2)).with_z(upper_alt + 9),
317 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 16),
318 }))
319 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
320 .fill(woodalt.clone());
321 painter
323 .cylinder_with_radius(center.with_z(upper_alt + 9), 9.0, 5.0)
324 .intersect(painter.aabb(Aabb {
325 min: (Vec2::new(center.x + 3, center.y - 3)).with_z(upper_alt + 9),
326 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 15),
327 }))
328 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
329 .clear();
330 painter
332 .cylinder_with_radius(center.with_z(upper_alt + 9), 9.0, 1.0)
333 .intersect(painter.aabb(Aabb {
334 min: (Vec2::new(center.x + 3, center.y - 3)).with_z(upper_alt + 9),
335 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 11),
336 }))
337 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
338 .fill(woodalt.clone());
339 painter
341 .cylinder_with_radius(center.with_z(upper_alt + 9), 8.0, 1.0)
342 .intersect(painter.aabb(Aabb {
343 min: (Vec2::new(center.x + 3, center.y - 3)).with_z(upper_alt + 9),
344 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 11),
345 }))
346 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
347 .clear();
348 painter
350 .line(
351 Vec2::new(center.x + 2, center.y - 9).with_z(upper_alt + 9),
352 Vec2::new(center.x + 2, center.y - 9).with_z(upper_alt + 16),
353 0.5,
354 )
355 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
356 .clear();
357 painter
358 .line(
359 Vec2::new(center.x + 8, center.y - 3).with_z(upper_alt + 9),
360 Vec2::new(center.x + 8, center.y - 3).with_z(upper_alt + 16),
361 0.5,
362 )
363 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
364 .clear();
365
366 let foundation_radius: f32 = 17.0;
367
368 painter
371 .cylinder_with_radius(center.with_z(base - 1), foundation_radius, 2.0)
372 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
373 .fill(brick.clone());
374 painter
375 .cylinder_with_radius(center.with_z(base - 1), foundation_radius - 1.0, 2.0)
376 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
377 .clear();
378
379 painter
381 .cylinder_with_radius(
382 Vec2::new(center.x - 1, center.y + 12).with_z(base - 5),
383 4.5,
384 7.0,
385 )
386 .rotate_about_min(Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0))
387 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
388 .fill(wood.clone());
389
390 painter
394 .cylinder_with_radius(center.with_z(base), 6.0, 45.0)
395 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
396 .fill(brick.clone());
397
398 painter
400 .cylinder_with_radius(center.with_z(base + 35), 8.0, 2.0)
401 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
402 .fill(brick.clone());
403 painter
405 .cylinder_with_radius(center.with_z(upper_alt + 9), 7.0, 1.0)
406 .intersect(painter.aabb(Aabb {
407 min: (Vec2::new(center.x + 2, center.y - 8)).with_z(upper_alt + 9),
408 max: (Vec2::new(center.x - 8, center.y + 8)).with_z(upper_alt + 10),
409 }))
410 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
411 .fill(brick.clone());
412 painter
414 .line(
415 Vec2::new(center.x + 6, center.y - 2).with_z(upper_alt + 9),
416 Vec2::new(center.x + 6, center.y + 2).with_z(upper_alt + 9),
417 0.5,
418 )
419 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
420 .fill(brick.clone());
421
422 painter
424 .cylinder_with_radius(
425 Vec2::new(center.x - 1, center.y + 12).with_z(base - 5),
426 3.5,
427 7.0,
428 )
429 .rotate_about_min(Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0))
430 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
431 .clear();
432
433 painter
435 .cylinder_with_radius(center.with_z(base), 5.0, 45.0)
436 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
437 .clear();
438
439 painter
443 .cone_with_radius(center.with_z(base + 45), 8.0, 18.0)
444 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
445 .fill(wood.clone());
446 painter
448 .aabb(Aabb {
449 min: Vec2::new(center.x - 1, center.y + 1).with_z(upper_alt + 9),
450 max: Vec2::new(center.x + 6, center.y + 6).with_z(upper_alt + 17),
451 })
452 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
453 .fill(brick.clone());
454 painter
455 .aabb(Aabb {
456 min: Vec2::new(center.x, center.y + 2).with_z(upper_alt + 9),
457 max: Vec2::new(center.x + 6, center.y + 7).with_z(upper_alt + 17),
458 })
459 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
460 .clear();
461 painter
463 .aabb(Aabb {
464 min: Vec2::new(center.x - 2, center.y - 15).with_z(upper_alt + 8),
465 max: Vec2::new(center.x + 6, center.y + 9).with_z(upper_alt + 9),
466 })
467 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
468 .fill(wood.clone());
469
470 painter
472 .aabb(Aabb {
473 min: Vec2::new(center.x + 5, center.y - 2).with_z(upper_alt + 10),
474 max: Vec2::new(center.x + 7, center.y + 2).with_z(upper_alt + 13),
475 })
476 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
477 .fill(brick.clone());
478 painter
479 .aabb(Aabb {
480 min: Vec2::new(center.x + 5, center.y - 1).with_z(upper_alt + 10),
481 max: Vec2::new(center.x + 7, center.y + 1).with_z(upper_alt + 15),
482 })
483 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
484 .fill(brick.clone());
485 painter
486 .aabb(Aabb {
487 min: Vec2::new(center.x + 5, center.y - 1).with_z(upper_alt + 10),
488 max: Vec2::new(center.x + 7, center.y + 1).with_z(upper_alt + 13),
489 })
490 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
491 .clear();
492 let door_rot = if self.rotation == 0.0 {
495 (2, 6)
496 } else if self.rotation == PI / 2.0 {
497 (4, 0)
498 } else if self.rotation == PI {
499 (6, 2) } else {
501 (0, 4)
502 };
503 let sprite_fill = Fill::Block(Block::air(SpriteKind::Door).with_ori(door_rot.0).unwrap());
504 painter
505 .aabb(Aabb {
506 min: Vec3::new(center.x + 6, center.y - 1, upper_alt + 10),
507 max: Vec3::new(center.x + 7, center.y + 0, upper_alt + 11),
508 })
509 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
510 .fill(sprite_fill.clone());
511 let sprite_fill = Fill::Block(Block::air(SpriteKind::Door).with_ori(door_rot.1).unwrap());
512 painter
513 .aabb(Aabb {
514 min: Vec3::new(center.x + 6, center.y + 0, upper_alt + 10),
515 max: Vec3::new(center.x + 7, center.y + 1, upper_alt + 11),
516 })
517 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
518 .fill(sprite_fill.clone());
519
520 painter
522 .line(
523 Vec2::new(center.x + 5, center.y - 3).with_z(upper_alt - 3),
524 Vec2::new(center.x + 17, center.y - 3).with_z(upper_alt + 8),
525 1.0,
526 )
527 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
528 .fill(wood.clone());
529 painter
530 .line(
531 Vec2::new(center.x + 5, center.y + 2).with_z(upper_alt - 3),
532 Vec2::new(center.x + 17, center.y + 2).with_z(upper_alt + 8),
533 1.0,
534 )
535 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
536 .fill(wood.clone());
537 painter
539 .line(
540 Vec2::new(center.x - 18, center.y - 3).with_z(upper_alt + 8),
541 Vec2::new(center.x - 6, center.y - 3).with_z(upper_alt - 3),
542 1.0,
543 )
544 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
545 .fill(wood.clone());
546 painter
547 .line(
548 Vec2::new(center.x - 18, center.y + 2).with_z(upper_alt + 8),
549 Vec2::new(center.x - 6, center.y + 2).with_z(upper_alt - 3),
550 1.0,
551 )
552 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
553 .fill(wood.clone());
554 painter
556 .line(
557 Vec2::new(center.x - 3, center.y - 18).with_z(upper_alt + 8),
558 Vec2::new(center.x - 3, center.y - 6).with_z(upper_alt - 3),
559 1.0,
560 )
561 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
562 .fill(wood.clone());
563 painter
564 .line(
565 Vec2::new(center.x + 2, center.y - 18).with_z(upper_alt + 8),
566 Vec2::new(center.x + 2, center.y - 6).with_z(upper_alt - 3),
567 1.0,
568 )
569 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
570 .fill(wood.clone());
571 painter
573 .line(
574 Vec2::new(center.x - 3, center.y + 5).with_z(upper_alt - 3),
575 Vec2::new(center.x - 3, center.y + 17).with_z(upper_alt + 8),
576 1.0,
577 )
578 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
579 .fill(wood.clone());
580 painter
581 .line(
582 Vec2::new(center.x + 2, center.y + 5).with_z(upper_alt - 3),
583 Vec2::new(center.x + 2, center.y + 17).with_z(upper_alt + 8),
584 1.0,
585 )
586 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
587 .fill(wood.clone());
588
589 painter
591 .cylinder_with_radius(center.with_z(upper_alt + 8), 5.0, 1.0)
592 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
593 .clear();
594
595 let stairs_clear1 = painter.cylinder_with_radius(center.with_z(base), 5.0, 38.0);
596
597 painter
598 .prim(Primitive::sampling(
599 stairs_clear1,
600 spiral_staircase(center.with_z(base + 3), 6.0, 0.5, 9.0),
601 ))
602 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
603 .fill(wood.clone());
604
605 painter
607 .aabb(Aabb {
608 min: Vec2::new(center.x + 1, center.y + 3).with_z(upper_alt + 8),
609 max: Vec2::new(center.x + 4, center.y + 5).with_z(upper_alt + 9),
610 })
611 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
612 .fill(wood.clone());
613 painter
614 .aabb(Aabb {
615 min: Vec2::new(center.x + 0, center.y + 2).with_z(upper_alt + 9),
616 max: Vec2::new(center.x + 6, center.y + 7).with_z(upper_alt + 10),
617 })
618 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
619 .fill(brick.clone());
620 painter
621 .aabb(Aabb {
622 min: Vec2::new(center.x + 1, center.y + 3).with_z(upper_alt + 9),
623 max: Vec2::new(center.x + 6, center.y + 7).with_z(upper_alt + 10),
624 })
625 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
626 .clear();
627 painter
628 .aabb(Aabb {
629 min: Vec2::new(center.x + 0, center.y + 2).with_z(upper_alt + 9),
630 max: Vec2::new(center.x + 1, center.y + 3).with_z(upper_alt + 17),
631 })
632 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
633 .fill(brick.clone());
634
635 let window_rot = if self.rotation == 0.0 || self.rotation == PI {
636 (2, 4)
637 } else {
638 (4, 2)
639 };
640 let sprite_fill = Fill::Block(
641 Block::air(SpriteKind::Window1)
642 .with_ori(window_rot.0)
643 .unwrap(),
644 );
645 painter
647 .aabb(Aabb {
648 min: Vec2::new(center.x - 6, center.y - 1).with_z(upper_alt + 12),
649 max: Vec2::new(center.x - 5, center.y + 1).with_z(upper_alt + 15),
650 })
651 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
652 .fill(sprite_fill.clone());
653
654 painter
656 .aabb(Aabb {
657 min: Vec2::new(center.x - 6, center.y - 1).with_z(base + 19),
658 max: Vec2::new(center.x - 5, center.y + 1).with_z(base + 22),
659 })
660 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
661 .fill(sprite_fill.clone());
662 painter
663 .aabb(Aabb {
664 min: Vec2::new(center.x - 6, center.y - 1).with_z(base + 1),
665 max: Vec2::new(center.x - 5, center.y + 1).with_z(base + 4),
666 })
667 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
668 .fill(sprite_fill.clone());
669 painter
670 .aabb(Aabb {
671 min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 4),
672 max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 7),
673 })
674 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
675 .fill(sprite_fill.clone());
676 painter
677 .aabb(Aabb {
678 min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 22),
679 max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 25),
680 })
681 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
682 .fill(sprite_fill.clone());
683 painter
684 .aabb(Aabb {
685 min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 30),
686 max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 33),
687 })
688 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
689 .fill(sprite_fill.clone());
690
691 let sprite_fill = Fill::Block(
692 Block::air(SpriteKind::Window1)
693 .with_ori(window_rot.1)
694 .unwrap(),
695 );
696 painter
698 .aabb(Aabb {
699 min: Vec2::new(center.x - 1, center.y + 5).with_z(base + 17),
700 max: Vec2::new(center.x + 1, center.y + 6).with_z(base + 20),
701 })
702 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
703 .fill(sprite_fill.clone());
704 painter
705 .aabb(Aabb {
706 min: Vec2::new(center.x - 1, center.y - 6).with_z(base + 13),
707 max: Vec2::new(center.x + 1, center.y - 5).with_z(base + 16),
708 })
709 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
710 .fill(sprite_fill.clone());
711
712 painter.rotated_sprite(
714 Vec2::new(center.x - 3, center.y + 5).with_z(base + 8),
715 SpriteKind::WallLampSmall,
716 4,
717 );
718 painter.rotated_sprite(
719 Vec2::new(center.x + 2, center.y + 5).with_z(base + 8),
720 SpriteKind::WallLampSmall,
721 4,
722 );
723 painter.rotated_sprite(
724 Vec2::new(center.x - 3, center.y + 5).with_z(base + 18),
725 SpriteKind::WallLampSmall,
726 4,
727 );
728 painter.rotated_sprite(
729 Vec2::new(center.x + 2, center.y + 5).with_z(base + 18),
730 SpriteKind::WallLampSmall,
731 4,
732 );
733 painter.rotated_sprite(
734 Vec2::new(center.x - 3, center.y - 6).with_z(base + 8),
735 SpriteKind::WallLampSmall,
736 0,
737 );
738 painter.rotated_sprite(
739 Vec2::new(center.x + 2, center.y - 6).with_z(base + 8),
740 SpriteKind::WallLampSmall,
741 0,
742 );
743 painter.rotated_sprite(
744 Vec2::new(center.x - 3, center.y - 6).with_z(base + 18),
745 SpriteKind::WallLampSmall,
746 0,
747 );
748 painter.rotated_sprite(
749 Vec2::new(center.x + 2, center.y - 6).with_z(base + 18),
750 SpriteKind::WallLampSmall,
751 0,
752 );
753
754 painter.rotated_sprite(
755 Vec2::new(center.x + 5, center.y - 3).with_z(base + 13),
756 SpriteKind::WallLampSmall,
757 2,
758 );
759 painter.rotated_sprite(
760 Vec2::new(center.x + 5, center.y + 2).with_z(base + 13),
761 SpriteKind::WallLampSmall,
762 2,
763 );
764 painter.rotated_sprite(
765 Vec2::new(center.x + 5, center.y - 3).with_z(base + 29),
766 SpriteKind::WallLampSmall,
767 2,
768 );
769 painter.rotated_sprite(
770 Vec2::new(center.x + 5, center.y + 2).with_z(base + 29),
771 SpriteKind::WallLampSmall,
772 2,
773 );
774 painter.rotated_sprite(
775 Vec2::new(center.x - 6, center.y - 3).with_z(base + 13),
776 SpriteKind::WallLampSmall,
777 6,
778 );
779 painter.rotated_sprite(
780 Vec2::new(center.x - 6, center.y + 2).with_z(base + 13),
781 SpriteKind::WallLampSmall,
782 6,
783 );
784 painter.rotated_sprite(
785 Vec2::new(center.x - 6, center.y - 3).with_z(base + 29),
786 SpriteKind::WallLampSmall,
787 6,
788 );
789 painter.rotated_sprite(
790 Vec2::new(center.x - 6, center.y + 2).with_z(base + 29),
791 SpriteKind::WallLampSmall,
792 6,
793 );
794 for dir in DIAGONALS {
796 let pos = (center + dir * 12).with_z(upper_alt + 7);
797 painter.sprite(pos, SpriteKind::Lantern)
798 }
799 for dir in CARDINALS {
800 let pos = (center + dir * 24).with_z(upper_alt + 7);
801 painter.sprite(pos, SpriteKind::Lantern)
802 }
803 let sprite_fill = Fill::Block(Block::air(SpriteKind::Lantern).with_ori(2).unwrap());
804 painter
806 .aabb(Aabb {
807 min: Vec3::new(center.x - 6, center.y + 5, upper_alt + 16),
808 max: Vec3::new(center.x - 5, center.y + 6, upper_alt + 17),
809 })
810 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
811 .fill(sprite_fill.clone());
812 painter
813 .aabb(Aabb {
814 min: Vec3::new(center.x + 5, center.y + 5, upper_alt + 16),
815 max: Vec3::new(center.x + 6, center.y + 6, upper_alt + 17),
816 })
817 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
818 .fill(sprite_fill.clone());
819 painter
820 .aabb(Aabb {
821 min: Vec3::new(center.x - 6, center.y - 6, upper_alt + 16),
822 max: Vec3::new(center.x - 5, center.y - 5, upper_alt + 17),
823 })
824 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
825 .fill(sprite_fill.clone());
826
827 let agent_lamp1_ori = (((self.rotation / std::f32::consts::FRAC_PI_2) as u8 * 2) + 6) % 8;
829 painter
830 .aabb(Aabb {
831 min: Vec3::new(center.x + 7, center.y - 3, upper_alt + 13),
832 max: Vec3::new(center.x + 8, center.y - 4, upper_alt + 14),
833 })
834 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
835 .fill(Fill::Block(
836 Block::air(SpriteKind::LanternAirshipWallBrownS)
837 .with_ori(agent_lamp1_ori)
838 .unwrap(),
839 ));
840 let agent_lamp2_ori = (self.rotation / std::f32::consts::FRAC_PI_2) as u8 * 2;
841 painter
842 .aabb(Aabb {
843 min: Vec3::new(center.x + 3, center.y - 7, upper_alt + 13),
844 max: Vec3::new(center.x + 4, center.y - 8, upper_alt + 14),
845 })
846 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
847 .fill(Fill::Block(
848 Block::air(SpriteKind::LanternAirshipWallBrownS)
849 .with_ori(agent_lamp2_ori)
850 .unwrap(),
851 ));
852
853 painter
856 .aabb(Aabb {
857 min: Vec3::new(center.x - 2, center.y - 3, base + 6),
858 max: Vec3::new(center.x - 1, center.y + -2, base + 7),
859 })
860 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
861 .fill(sprite_fill.clone());
862 painter
863 .aabb(Aabb {
864 min: Vec3::new(center.x - 2, center.y - 3, base + 15),
865 max: Vec3::new(center.x - 1, center.y + -2, base + 16),
866 })
867 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
868 .fill(sprite_fill.clone());
869 painter
870 .aabb(Aabb {
871 min: Vec3::new(center.x - 2, center.y - 3, base + 24),
872 max: Vec3::new(center.x - 1, center.y + -2, base + 25),
873 })
874 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
875 .fill(sprite_fill.clone());
876 painter
877 .aabb(Aabb {
878 min: Vec3::new(center.x - 2, center.y - 3, base + 33),
879 max: Vec3::new(center.x - 1, center.y + -2, base + 34),
880 })
881 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
882 .fill(sprite_fill.clone());
883 painter
884 .aabb(Aabb {
885 min: Vec3::new(center.x - 2, center.y - 3, base + 44),
886 max: Vec3::new(center.x - 1, center.y + -2, base + 45),
887 })
888 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
889 .fill(sprite_fill.clone());
890
891 let mut sprite_positions = vec![];
893 for a in 0..5 {
894 sprite_positions.push(Vec2::new(center.x + 1 + a, center.y + 2));
895 }
896 for b in 0..=1 {
897 sprite_positions.push(Vec2::new(center.x, center.y + 3 + b));
898 }
899 for sprite_pos in sprite_positions {
900 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
901 for r in 0..rows {
902 painter
903 .aabb(Aabb {
904 min: sprite_pos.with_z(upper_alt + 10 + r),
905 max: (sprite_pos + 1).with_z(upper_alt + 11 + r),
906 })
907 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
908 .fill(Fill::Block(Block::air(
909 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32 {
910 0 => SpriteKind::Barrel,
911 _ => SpriteKind::CrateBlock,
912 },
913 )));
914 }
915 }
916
917 painter.spawn(
919 EntityInfo::at(self.campfire_pos.map(|e| e as f32 + 0.5))
920 .into_special(SpecialEntity::Waypoint),
921 );
922
923 painter
925 .cylinder_with_radius(center.with_z(base - 1), foundation_radius - 1.0, 1.0)
926 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
927 .fill(grass.clone());
928 painter
929 .cylinder_with_radius(center.with_z(base - 1), 10.0, 1.0)
930 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
931 .fill(brick.clone());
932
933 painter
934 .cylinder_with_radius(
935 center.with_z(min_foundation_alt),
936 foundation_radius,
937 (base - min_foundation_alt - 1) as f32,
938 )
939 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
940 .fill(dirt.clone());
941
942 let stair_height = 8;
943 let stair_width = 3;
944 let stair_levels = (base - min_foundation_alt - 1) / (stair_height + 1);
945 let stair_drop = 4;
946 let stair_landing_space = stair_height + 3;
947 let stairtop = base - 1;
948 let edge_clear = aabb(
982 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
983 .with_z(min_foundation_alt),
984 Vec2::new(center.x + 4, center.y + foundation_radius as i32).with_z(base + 1),
985 );
986 let edge_fill = aabb(
987 Vec2::new(center.x - 7, center.y + foundation_radius as i32 - 2).with_z(stairtop),
988 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 2).with_z(stairtop),
989 );
990 let stair = aabb(
991 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
992 .with_z(stairtop - stair_height),
993 Vec2::new(
994 center.x + 3,
995 center.y + foundation_radius as i32 + stair_width - 2,
996 )
997 .with_z(stairtop - 1),
998 );
999 let middirt = aabb(
1000 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
1001 .with_z(stairtop - stair_height - 1),
1002 Vec2::new(
1003 center.x + 3,
1004 center.y + foundation_radius as i32 + stair_width - 2,
1005 )
1006 .with_z(stairtop - stair_height - (stair_drop + 1)),
1007 );
1008 let stair_landing = aabb(
1009 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 1).with_z(stairtop),
1010 Vec2::new(
1011 center.x + 4,
1012 center.y + foundation_radius as i32 + stair_width - 2,
1013 )
1014 .with_z(stairtop),
1015 );
1016 let stair_column = aabb(
1017 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 1).with_z(stairtop - 1),
1018 Vec2::new(
1019 center.x + 4,
1020 center.y + foundation_radius as i32 + stair_width - 2,
1021 )
1022 .with_z(stairtop - (stair_height + stair_drop)),
1023 );
1024
1025 let slice = painter.aabb(edge_clear);
1026 let edge = painter.aabb(edge_fill);
1027 let stair_cap_even =
1028 painter
1029 .ramp(stair, Dir::X)
1030 .intersect(painter.ramp(stair, Dir::X).rotate_about(
1031 Mat3::new(-1, 0, 0, 0, 1, 0, 0, 0, -1),
1032 center.with_z(stairtop - 4),
1033 ));
1034 let stair_cap_odd =
1035 stair_cap_even.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1), stair.center());
1036 let stair_base_even = painter
1037 .ramp(stair, Dir::X)
1038 .intersect(painter.ramp(stair, Dir::X).translate(Vec3::new(1, 0, 0)))
1039 .union(painter.aabb(middirt))
1040 .union(
1041 painter
1042 .ramp(stair, Dir::X)
1043 .rotate_about(
1044 Mat3::new(-1, 0, 0, 0, 1, 0, 0, 0, -1),
1045 center.with_z(stairtop - 4),
1046 )
1047 .translate(Vec3::new(0, 0, -(stair_height + stair_drop))),
1048 );
1049 let stair_base_odd =
1050 stair_base_even.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1), stair.center());
1051 let stair_column_cap = painter.aabb(stair_landing);
1052 let stair_column_base = painter.aabb(stair_column);
1053
1054 for side in 0..4 {
1056 let rot: f32 = (self.rotation + side as f32 * std::f32::consts::FRAC_PI_2)
1057 .rem_euclid(std::f32::consts::TAU);
1058
1059 painter.fill(
1061 slice.rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1062 Fill::Block(Block::empty()),
1063 );
1064 painter.fill(
1065 edge.rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1066 brick.clone(),
1067 );
1068 painter.fill(
1069 edge.translate(Vec3::new(0, 0, 1))
1070 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1071 Fill::Block(Block::empty()),
1072 );
1073 let picketx = 3;
1076 for i in 0..3 {
1077 painter
1078 .line(
1079 Vec2::new(
1080 center.x + picketx - i * 4,
1081 center.y + foundation_radius as i32 - 2,
1082 )
1083 .with_z(base),
1084 Vec2::new(
1085 center.x + picketx - i * 4 - 2,
1086 center.y + foundation_radius as i32 - 2,
1087 )
1088 .with_z(base),
1089 0.5,
1090 )
1091 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1092 .fill(brick.clone());
1093 }
1094 painter
1095 .aabb(aabb(
1096 Vec2::new(center.x + 4, center.y + 4).with_z(base - 1),
1097 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 2)
1098 .with_z(base - 1),
1099 ))
1100 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1101 .fill(brick.clone());
1102
1103 if stair_levels == 0 {
1105 painter
1106 .aabb(aabb(
1107 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
1108 .with_z(base - 7),
1109 Vec2::new(center.x + 4, center.y + foundation_radius as i32)
1110 .with_z(base - 2),
1111 ))
1112 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1113 .fill(dirt.clone());
1114 continue;
1115 }
1116
1117 for level in 0..stair_levels {
1119 let y_off = level % 2 * stair_width;
1120 let stair_y_off = if level % 2 > 0 && stair_width % 2 > 0 {
1121 y_off + 1
1122 } else {
1123 y_off
1124 };
1125 if level % 2 == 0 {
1126 painter.fill(
1127 stair_cap_even
1128 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1129 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1130 brick.clone(),
1131 );
1132 painter.fill(
1133 stair_base_even
1134 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1135 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1136 dirt.clone(),
1137 );
1138 } else {
1139 painter.fill(
1140 stair_cap_odd
1141 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1142 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1143 brick.clone(),
1144 );
1145 painter.fill(
1146 stair_base_odd
1147 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1148 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1149 dirt.clone(),
1150 );
1151 }
1152 painter.fill(
1153 stair_column_cap
1154 .translate(Vec3::new(
1155 level % 2 * -stair_landing_space,
1156 y_off,
1157 level * -(stair_height + 1),
1158 ))
1159 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1160 brick.clone(),
1161 );
1162 painter.fill(
1163 stair_column_base
1164 .translate(Vec3::new(
1165 level % 2 * -stair_landing_space,
1166 y_off,
1167 level * -(stair_height + 1),
1168 ))
1169 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1170 dirt.clone(),
1171 );
1172 painter.fill(
1173 stair_column_cap
1174 .translate(Vec3::new(
1175 (level + 1) % 2 * -stair_landing_space,
1176 y_off,
1177 (level + 1) * -(stair_height + 1),
1178 ))
1179 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1180 brick.clone(),
1181 );
1182 painter.fill(
1183 stair_column_base
1184 .translate(Vec3::new(
1185 (level + 1) % 2 * -stair_landing_space,
1186 y_off,
1187 (level + 1) * -(stair_height + 1),
1188 ))
1189 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1190 dirt.clone(),
1191 );
1192 let lamp_pos = Vec2::new(
1193 center.x - 5 + level % 2 * (stair_height + 1),
1194 center.y + foundation_radius as i32 - 1,
1195 )
1196 .with_z(base - 5 - level * (stair_height + 1));
1197 let lamp_ori = (((rot / std::f32::consts::FRAC_PI_2) as u8 * 2) + 10) % 8;
1198 painter
1199 .aabb(aabb(lamp_pos, lamp_pos))
1200 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1201 .fill(Fill::sprite_ori(
1202 SpriteKind::LanternAirshipWallBrownS,
1203 lamp_ori,
1204 ));
1205 }
1206 }
1207 }
1208}
1209
1210fn aabb(min: Vec3<i32>, max: Vec3<i32>) -> Aabb<i32> {
1211 let aabb = Aabb { min, max }.made_valid();
1212 Aabb {
1213 min: aabb.min,
1214 max: aabb.max + 1,
1215 }
1216}