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
155impl Structure for AirshipDock {
156 #[cfg(feature = "dyn-lib")]
157 #[unsafe(export_name = "as_dyn_structure_airshipdock")]
158 fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
159 Some((Self::as_dyn_impl(self), "as_dyn_structure_airshipdock"))
160 }
161
162 fn spawn_rules_inner(
163 &self,
164 spawn_rules: &mut SpawnRules,
165 _land: &Land,
166 wpos: Vec2<i32>,
167 _weight: f32,
168 ) {
169 const AIRSHIP_MIN_TREE_DIST2: i32 = 100;
175 spawn_rules.trees &= !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST2);
176
177 const MIN_FLAT_DIST: f32 = 12.0;
178 const SLOPE_LENGTH: f32 = 16.0;
179 let dist = wpos.as_::<f32>().distance(self.center.as_());
180 let weight = (1.0 - (dist - MIN_FLAT_DIST).max(0.0) / SLOPE_LENGTH).max(0.0);
181 spawn_rules.prefer_alt(self.alt as f32, weight);
182 }
183
184 fn airship_dock_info(&self) -> Option<AirshipDockInfo<'_>> {
185 Some(AirshipDockInfo {
186 door_tile: self.door_tile,
187 center: self.center,
188 docking_positions: &self.docking_positions,
189 })
190 }
191
192 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
193 let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
194 let wood = Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24);
195 let woodalt = Fill::Brick(BlockKind::Rock, Rgb::new(30, 22, 15), 24);
196 let grass = {
197 let surface_colors = self.surface_colors.clone();
198 let num_colors = surface_colors.len() as u32;
199 Fill::Sampling(Arc::new(move |wpos| {
200 let index = if num_colors > 0 {
201 (RandomField::new(0).get(wpos) % num_colors) as usize
202 } else {
203 0usize
204 };
205 Some(Block::new(
206 BlockKind::Earth,
207 surface_colors
208 .get(index)
209 .cloned()
210 .unwrap_or(Rgb::new(55, 25, 8)),
211 ))
212 }))
213 };
214 let dirt = {
215 let sub_surface_colors = self.sub_surface_colors.clone();
216 let num_colors = sub_surface_colors.len() as u32;
217 let avg_color = if num_colors > 0 {
218 let mut r_total: f32 = 0.0;
219 let mut g_total: f32 = 0.0;
220 let mut b_total: f32 = 0.0;
221 for color in &sub_surface_colors {
222 r_total += color.r as f32;
223 g_total += color.g as f32;
224 b_total += color.b as f32;
225 }
226 Rgb::new(
227 r_total / num_colors as f32,
228 g_total / num_colors as f32,
229 b_total / num_colors as f32,
230 )
231 } else {
232 Rgb::new(55.0, 25.0, 8.0)
233 };
234 Fill::Sampling(Arc::new(move |wpos| {
235 let color = if num_colors > 0 {
236 sub_surface_colors
237 .get((RandomField::new(0).get(wpos) % num_colors) as usize)
238 .copied()
239 .unwrap_or(Rgb::new(55, 25, 8))
240 .map(|u| u as f32)
241 } else {
242 avg_color
243 };
244 let lerp_factor = (RandomField::new(1).get(wpos) % 51 + 25) as f32 / 100.0;
247 let block_color = Rgb::new(
248 Lerp::lerp(avg_color.r, color.r, lerp_factor),
249 Lerp::lerp(avg_color.g, color.g, lerp_factor),
250 Lerp::lerp(avg_color.b, color.b, lerp_factor),
251 )
252 .map(|f| f.clamp(0.0, 255.0) as u8);
253 Some(Block::new(BlockKind::Earth, block_color))
254 }))
255 };
256 let base = self.base;
257 let center = self.center;
258 let upper_alt = self.upper_alt;
259 let min_foundation_alt = self.min_foundation_alt;
260
261 painter
267 .cylinder_with_radius(center.with_z(upper_alt - 3), 7.0, 1.0)
268 .fill(wood.clone());
269 painter
270 .cylinder_with_radius(center.with_z(upper_alt + 6), 7.0, 1.0)
271 .fill(wood.clone());
272 painter
273 .cylinder_with_radius(center.with_z(upper_alt + 7), 8.0, 1.0)
274 .fill(wood.clone());
275
276 painter
278 .superquadric(
279 Aabb {
280 min: (center - 8000).with_z(upper_alt + 7),
281 max: (center + 8000).with_z(upper_alt + 11),
282 },
283 0.3,
284 )
285 .intersect(painter.aabb(Aabb {
286 min: (center - 31).with_z(upper_alt + 7),
287 max: (center + 31).with_z(upper_alt + 11),
288 }))
289 .fill(woodalt.clone());
290 painter
291 .cylinder_with_radius(center.with_z(upper_alt + 8), 19.0, 2.0)
292 .fill(woodalt.clone());
293 painter
294 .cylinder_with_radius(center.with_z(upper_alt + 8), 18.0, 2.0)
295 .fill(wood.clone());
296 painter
297 .cylinder_with_radius(center.with_z(upper_alt + 9), 18.0, 1.0)
298 .clear();
299 painter
300 .aabb(Aabb {
301 min: Vec2::new(center.x - 30, center.y - 2).with_z(upper_alt + 8),
302 max: Vec2::new(center.x + 30, center.y + 2).with_z(upper_alt + 10),
303 })
304 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
305 .fill(wood.clone());
306 painter
307 .aabb(Aabb {
308 min: Vec2::new(center.x - 30, center.y - 2).with_z(upper_alt + 9),
309 max: Vec2::new(center.x + 30, center.y + 2).with_z(upper_alt + 10),
310 })
311 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
312 .clear();
313 painter
314 .aabb(Aabb {
315 min: Vec2::new(center.x - 2, center.y - 30).with_z(upper_alt + 8),
316 max: Vec2::new(center.x + 2, center.y + 30).with_z(upper_alt + 10),
317 })
318 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
319 .fill(wood.clone());
320 painter
321 .aabb(Aabb {
322 min: Vec2::new(center.x - 2, center.y - 30).with_z(upper_alt + 9),
323 max: Vec2::new(center.x + 2, center.y + 30).with_z(upper_alt + 10),
324 })
325 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
326 .clear();
327
328 painter
330 .cylinder_with_radius(center.with_z(upper_alt + 9), 9.0, 6.0)
331 .intersect(painter.aabb(Aabb {
332 min: (Vec2::new(center.x + 2, center.y - 2)).with_z(upper_alt + 9),
333 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 16),
334 }))
335 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
336 .fill(woodalt.clone());
337 painter
339 .cylinder_with_radius(center.with_z(upper_alt + 9), 9.0, 5.0)
340 .intersect(painter.aabb(Aabb {
341 min: (Vec2::new(center.x + 3, center.y - 3)).with_z(upper_alt + 9),
342 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 15),
343 }))
344 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
345 .clear();
346 painter
348 .cylinder_with_radius(center.with_z(upper_alt + 9), 9.0, 1.0)
349 .intersect(painter.aabb(Aabb {
350 min: (Vec2::new(center.x + 3, center.y - 3)).with_z(upper_alt + 9),
351 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 11),
352 }))
353 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
354 .fill(woodalt.clone());
355 painter
357 .cylinder_with_radius(center.with_z(upper_alt + 9), 8.0, 1.0)
358 .intersect(painter.aabb(Aabb {
359 min: (Vec2::new(center.x + 3, center.y - 3)).with_z(upper_alt + 9),
360 max: (Vec2::new(center.x + 40, center.y - 40)).with_z(upper_alt + 11),
361 }))
362 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
363 .clear();
364 painter
366 .line(
367 Vec2::new(center.x + 2, center.y - 9).with_z(upper_alt + 9),
368 Vec2::new(center.x + 2, center.y - 9).with_z(upper_alt + 16),
369 0.5,
370 )
371 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
372 .clear();
373 painter
374 .line(
375 Vec2::new(center.x + 8, center.y - 3).with_z(upper_alt + 9),
376 Vec2::new(center.x + 8, center.y - 3).with_z(upper_alt + 16),
377 0.5,
378 )
379 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
380 .clear();
381
382 let foundation_radius: f32 = 17.0;
383
384 painter
387 .cylinder_with_radius(center.with_z(base - 1), foundation_radius, 2.0)
388 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
389 .fill(brick.clone());
390 painter
391 .cylinder_with_radius(center.with_z(base - 1), foundation_radius - 1.0, 2.0)
392 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
393 .clear();
394
395 painter
397 .cylinder_with_radius(
398 Vec2::new(center.x - 1, center.y + 12).with_z(base - 5),
399 4.5,
400 7.0,
401 )
402 .rotate_about_min(Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0))
403 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
404 .fill(wood.clone());
405
406 painter
410 .cylinder_with_radius(center.with_z(base), 6.0, 45.0)
411 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
412 .fill(brick.clone());
413
414 painter
416 .cylinder_with_radius(center.with_z(base + 35), 8.0, 2.0)
417 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
418 .fill(brick.clone());
419 painter
421 .cylinder_with_radius(center.with_z(upper_alt + 9), 7.0, 1.0)
422 .intersect(painter.aabb(Aabb {
423 min: (Vec2::new(center.x + 2, center.y - 8)).with_z(upper_alt + 9),
424 max: (Vec2::new(center.x - 8, center.y + 8)).with_z(upper_alt + 10),
425 }))
426 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
427 .fill(brick.clone());
428 painter
430 .line(
431 Vec2::new(center.x + 6, center.y - 2).with_z(upper_alt + 9),
432 Vec2::new(center.x + 6, center.y + 2).with_z(upper_alt + 9),
433 0.5,
434 )
435 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
436 .fill(brick.clone());
437
438 painter
440 .cylinder_with_radius(
441 Vec2::new(center.x - 1, center.y + 12).with_z(base - 5),
442 3.5,
443 7.0,
444 )
445 .rotate_about_min(Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0))
446 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
447 .clear();
448
449 painter
451 .cylinder_with_radius(center.with_z(base), 5.0, 45.0)
452 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
453 .clear();
454
455 painter
459 .cone_with_radius(center.with_z(base + 45), 8.0, 18.0)
460 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
461 .fill(wood.clone());
462 painter
464 .aabb(Aabb {
465 min: Vec2::new(center.x - 1, center.y + 1).with_z(upper_alt + 9),
466 max: Vec2::new(center.x + 6, center.y + 6).with_z(upper_alt + 17),
467 })
468 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
469 .fill(brick.clone());
470 painter
471 .aabb(Aabb {
472 min: Vec2::new(center.x, center.y + 2).with_z(upper_alt + 9),
473 max: Vec2::new(center.x + 6, center.y + 7).with_z(upper_alt + 17),
474 })
475 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
476 .clear();
477 painter
479 .aabb(Aabb {
480 min: Vec2::new(center.x - 2, center.y - 15).with_z(upper_alt + 8),
481 max: Vec2::new(center.x + 6, center.y + 9).with_z(upper_alt + 9),
482 })
483 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
484 .fill(wood.clone());
485
486 painter
488 .aabb(Aabb {
489 min: Vec2::new(center.x + 5, center.y - 2).with_z(upper_alt + 10),
490 max: Vec2::new(center.x + 7, center.y + 2).with_z(upper_alt + 13),
491 })
492 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
493 .fill(brick.clone());
494 painter
495 .aabb(Aabb {
496 min: Vec2::new(center.x + 5, center.y - 1).with_z(upper_alt + 10),
497 max: Vec2::new(center.x + 7, center.y + 1).with_z(upper_alt + 15),
498 })
499 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
500 .fill(brick.clone());
501 painter
502 .aabb(Aabb {
503 min: Vec2::new(center.x + 5, center.y - 1).with_z(upper_alt + 10),
504 max: Vec2::new(center.x + 7, center.y + 1).with_z(upper_alt + 13),
505 })
506 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
507 .clear();
508 let door_rot = if self.rotation == 0.0 {
511 (2, 6)
512 } else if self.rotation == PI / 2.0 {
513 (4, 0)
514 } else if self.rotation == PI {
515 (6, 2) } else {
517 (0, 4)
518 };
519 let sprite_fill = Fill::Block(Block::air(SpriteKind::Door).with_ori(door_rot.0).unwrap());
520 painter
521 .aabb(Aabb {
522 min: Vec3::new(center.x + 6, center.y - 1, upper_alt + 10),
523 max: Vec3::new(center.x + 7, center.y + 0, upper_alt + 11),
524 })
525 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
526 .fill(sprite_fill.clone());
527 let sprite_fill = Fill::Block(Block::air(SpriteKind::Door).with_ori(door_rot.1).unwrap());
528 painter
529 .aabb(Aabb {
530 min: Vec3::new(center.x + 6, center.y + 0, upper_alt + 10),
531 max: Vec3::new(center.x + 7, center.y + 1, upper_alt + 11),
532 })
533 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
534 .fill(sprite_fill.clone());
535
536 painter
538 .line(
539 Vec2::new(center.x + 5, center.y - 3).with_z(upper_alt - 3),
540 Vec2::new(center.x + 17, center.y - 3).with_z(upper_alt + 8),
541 1.0,
542 )
543 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
544 .fill(wood.clone());
545 painter
546 .line(
547 Vec2::new(center.x + 5, center.y + 2).with_z(upper_alt - 3),
548 Vec2::new(center.x + 17, center.y + 2).with_z(upper_alt + 8),
549 1.0,
550 )
551 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
552 .fill(wood.clone());
553 painter
555 .line(
556 Vec2::new(center.x - 18, center.y - 3).with_z(upper_alt + 8),
557 Vec2::new(center.x - 6, center.y - 3).with_z(upper_alt - 3),
558 1.0,
559 )
560 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
561 .fill(wood.clone());
562 painter
563 .line(
564 Vec2::new(center.x - 18, center.y + 2).with_z(upper_alt + 8),
565 Vec2::new(center.x - 6, center.y + 2).with_z(upper_alt - 3),
566 1.0,
567 )
568 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
569 .fill(wood.clone());
570 painter
572 .line(
573 Vec2::new(center.x - 3, center.y - 18).with_z(upper_alt + 8),
574 Vec2::new(center.x - 3, center.y - 6).with_z(upper_alt - 3),
575 1.0,
576 )
577 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
578 .fill(wood.clone());
579 painter
580 .line(
581 Vec2::new(center.x + 2, center.y - 18).with_z(upper_alt + 8),
582 Vec2::new(center.x + 2, center.y - 6).with_z(upper_alt - 3),
583 1.0,
584 )
585 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
586 .fill(wood.clone());
587 painter
589 .line(
590 Vec2::new(center.x - 3, center.y + 5).with_z(upper_alt - 3),
591 Vec2::new(center.x - 3, center.y + 17).with_z(upper_alt + 8),
592 1.0,
593 )
594 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
595 .fill(wood.clone());
596 painter
597 .line(
598 Vec2::new(center.x + 2, center.y + 5).with_z(upper_alt - 3),
599 Vec2::new(center.x + 2, center.y + 17).with_z(upper_alt + 8),
600 1.0,
601 )
602 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
603 .fill(wood.clone());
604
605 painter
607 .cylinder_with_radius(center.with_z(upper_alt + 8), 5.0, 1.0)
608 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
609 .clear();
610
611 let stairs_clear1 = painter.cylinder_with_radius(center.with_z(base), 5.0, 38.0);
612
613 painter
614 .prim(Primitive::sampling(
615 stairs_clear1,
616 spiral_staircase(center.with_z(base + 3), 6.0, 0.5, 9.0),
617 ))
618 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
619 .fill(wood.clone());
620
621 painter
623 .aabb(Aabb {
624 min: Vec2::new(center.x + 1, center.y + 3).with_z(upper_alt + 8),
625 max: Vec2::new(center.x + 4, center.y + 5).with_z(upper_alt + 9),
626 })
627 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
628 .fill(wood.clone());
629 painter
630 .aabb(Aabb {
631 min: Vec2::new(center.x + 0, center.y + 2).with_z(upper_alt + 9),
632 max: Vec2::new(center.x + 6, center.y + 7).with_z(upper_alt + 10),
633 })
634 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
635 .fill(brick.clone());
636 painter
637 .aabb(Aabb {
638 min: Vec2::new(center.x + 1, center.y + 3).with_z(upper_alt + 9),
639 max: Vec2::new(center.x + 6, center.y + 7).with_z(upper_alt + 10),
640 })
641 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
642 .clear();
643 painter
644 .aabb(Aabb {
645 min: Vec2::new(center.x + 0, center.y + 2).with_z(upper_alt + 9),
646 max: Vec2::new(center.x + 1, center.y + 3).with_z(upper_alt + 17),
647 })
648 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
649 .fill(brick.clone());
650
651 let window_rot = if self.rotation == 0.0 || self.rotation == PI {
652 (2, 4)
653 } else {
654 (4, 2)
655 };
656 let sprite_fill = Fill::Block(
657 Block::air(SpriteKind::Window1)
658 .with_ori(window_rot.0)
659 .unwrap(),
660 );
661 painter
663 .aabb(Aabb {
664 min: Vec2::new(center.x - 6, center.y - 1).with_z(upper_alt + 12),
665 max: Vec2::new(center.x - 5, center.y + 1).with_z(upper_alt + 15),
666 })
667 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
668 .fill(sprite_fill.clone());
669
670 painter
672 .aabb(Aabb {
673 min: Vec2::new(center.x - 6, center.y - 1).with_z(base + 19),
674 max: Vec2::new(center.x - 5, center.y + 1).with_z(base + 22),
675 })
676 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
677 .fill(sprite_fill.clone());
678 painter
679 .aabb(Aabb {
680 min: Vec2::new(center.x - 6, center.y - 1).with_z(base + 1),
681 max: Vec2::new(center.x - 5, center.y + 1).with_z(base + 4),
682 })
683 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
684 .fill(sprite_fill.clone());
685 painter
686 .aabb(Aabb {
687 min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 4),
688 max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 7),
689 })
690 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
691 .fill(sprite_fill.clone());
692 painter
693 .aabb(Aabb {
694 min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 22),
695 max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 25),
696 })
697 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
698 .fill(sprite_fill.clone());
699 painter
700 .aabb(Aabb {
701 min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 30),
702 max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 33),
703 })
704 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
705 .fill(sprite_fill.clone());
706
707 let sprite_fill = Fill::Block(
708 Block::air(SpriteKind::Window1)
709 .with_ori(window_rot.1)
710 .unwrap(),
711 );
712 painter
714 .aabb(Aabb {
715 min: Vec2::new(center.x - 1, center.y + 5).with_z(base + 17),
716 max: Vec2::new(center.x + 1, center.y + 6).with_z(base + 20),
717 })
718 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
719 .fill(sprite_fill.clone());
720 painter
721 .aabb(Aabb {
722 min: Vec2::new(center.x - 1, center.y - 6).with_z(base + 13),
723 max: Vec2::new(center.x + 1, center.y - 5).with_z(base + 16),
724 })
725 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
726 .fill(sprite_fill.clone());
727
728 painter.rotated_sprite(
730 Vec2::new(center.x - 3, center.y + 5).with_z(base + 8),
731 SpriteKind::WallLampSmall,
732 4,
733 );
734 painter.rotated_sprite(
735 Vec2::new(center.x + 2, center.y + 5).with_z(base + 8),
736 SpriteKind::WallLampSmall,
737 4,
738 );
739 painter.rotated_sprite(
740 Vec2::new(center.x - 3, center.y + 5).with_z(base + 18),
741 SpriteKind::WallLampSmall,
742 4,
743 );
744 painter.rotated_sprite(
745 Vec2::new(center.x + 2, center.y + 5).with_z(base + 18),
746 SpriteKind::WallLampSmall,
747 4,
748 );
749 painter.rotated_sprite(
750 Vec2::new(center.x - 3, center.y - 6).with_z(base + 8),
751 SpriteKind::WallLampSmall,
752 0,
753 );
754 painter.rotated_sprite(
755 Vec2::new(center.x + 2, center.y - 6).with_z(base + 8),
756 SpriteKind::WallLampSmall,
757 0,
758 );
759 painter.rotated_sprite(
760 Vec2::new(center.x - 3, center.y - 6).with_z(base + 18),
761 SpriteKind::WallLampSmall,
762 0,
763 );
764 painter.rotated_sprite(
765 Vec2::new(center.x + 2, center.y - 6).with_z(base + 18),
766 SpriteKind::WallLampSmall,
767 0,
768 );
769
770 painter.rotated_sprite(
771 Vec2::new(center.x + 5, center.y - 3).with_z(base + 13),
772 SpriteKind::WallLampSmall,
773 2,
774 );
775 painter.rotated_sprite(
776 Vec2::new(center.x + 5, center.y + 2).with_z(base + 13),
777 SpriteKind::WallLampSmall,
778 2,
779 );
780 painter.rotated_sprite(
781 Vec2::new(center.x + 5, center.y - 3).with_z(base + 29),
782 SpriteKind::WallLampSmall,
783 2,
784 );
785 painter.rotated_sprite(
786 Vec2::new(center.x + 5, center.y + 2).with_z(base + 29),
787 SpriteKind::WallLampSmall,
788 2,
789 );
790 painter.rotated_sprite(
791 Vec2::new(center.x - 6, center.y - 3).with_z(base + 13),
792 SpriteKind::WallLampSmall,
793 6,
794 );
795 painter.rotated_sprite(
796 Vec2::new(center.x - 6, center.y + 2).with_z(base + 13),
797 SpriteKind::WallLampSmall,
798 6,
799 );
800 painter.rotated_sprite(
801 Vec2::new(center.x - 6, center.y - 3).with_z(base + 29),
802 SpriteKind::WallLampSmall,
803 6,
804 );
805 painter.rotated_sprite(
806 Vec2::new(center.x - 6, center.y + 2).with_z(base + 29),
807 SpriteKind::WallLampSmall,
808 6,
809 );
810 for dir in DIAGONALS {
812 let pos = (center + dir * 12).with_z(upper_alt + 7);
813 painter.sprite(pos, SpriteKind::Lantern)
814 }
815 for dir in CARDINALS {
816 let pos = (center + dir * 24).with_z(upper_alt + 7);
817 painter.sprite(pos, SpriteKind::Lantern)
818 }
819 let sprite_fill = Fill::Block(Block::air(SpriteKind::Lantern).with_ori(2).unwrap());
820 painter
822 .aabb(Aabb {
823 min: Vec3::new(center.x - 6, center.y + 5, upper_alt + 16),
824 max: Vec3::new(center.x - 5, center.y + 6, upper_alt + 17),
825 })
826 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
827 .fill(sprite_fill.clone());
828 painter
829 .aabb(Aabb {
830 min: Vec3::new(center.x + 5, center.y + 5, upper_alt + 16),
831 max: Vec3::new(center.x + 6, center.y + 6, upper_alt + 17),
832 })
833 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
834 .fill(sprite_fill.clone());
835 painter
836 .aabb(Aabb {
837 min: Vec3::new(center.x - 6, center.y - 6, upper_alt + 16),
838 max: Vec3::new(center.x - 5, center.y - 5, upper_alt + 17),
839 })
840 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
841 .fill(sprite_fill.clone());
842
843 let agent_lamp1_ori = (((self.rotation / std::f32::consts::FRAC_PI_2) as u8 * 2) + 6) % 8;
845 painter
846 .aabb(Aabb {
847 min: Vec3::new(center.x + 7, center.y - 3, upper_alt + 13),
848 max: Vec3::new(center.x + 8, center.y - 4, upper_alt + 14),
849 })
850 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
851 .fill(Fill::Block(
852 Block::air(SpriteKind::LanternAirshipWallBrownS)
853 .with_ori(agent_lamp1_ori)
854 .unwrap(),
855 ));
856 let agent_lamp2_ori = (self.rotation / std::f32::consts::FRAC_PI_2) as u8 * 2;
857 painter
858 .aabb(Aabb {
859 min: Vec3::new(center.x + 3, center.y - 7, upper_alt + 13),
860 max: Vec3::new(center.x + 4, center.y - 8, upper_alt + 14),
861 })
862 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
863 .fill(Fill::Block(
864 Block::air(SpriteKind::LanternAirshipWallBrownS)
865 .with_ori(agent_lamp2_ori)
866 .unwrap(),
867 ));
868
869 painter
872 .aabb(Aabb {
873 min: Vec3::new(center.x - 2, center.y - 3, base + 6),
874 max: Vec3::new(center.x - 1, center.y + -2, base + 7),
875 })
876 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
877 .fill(sprite_fill.clone());
878 painter
879 .aabb(Aabb {
880 min: Vec3::new(center.x - 2, center.y - 3, base + 15),
881 max: Vec3::new(center.x - 1, center.y + -2, base + 16),
882 })
883 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
884 .fill(sprite_fill.clone());
885 painter
886 .aabb(Aabb {
887 min: Vec3::new(center.x - 2, center.y - 3, base + 24),
888 max: Vec3::new(center.x - 1, center.y + -2, base + 25),
889 })
890 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
891 .fill(sprite_fill.clone());
892 painter
893 .aabb(Aabb {
894 min: Vec3::new(center.x - 2, center.y - 3, base + 33),
895 max: Vec3::new(center.x - 1, center.y + -2, base + 34),
896 })
897 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
898 .fill(sprite_fill.clone());
899 painter
900 .aabb(Aabb {
901 min: Vec3::new(center.x - 2, center.y - 3, base + 44),
902 max: Vec3::new(center.x - 1, center.y + -2, base + 45),
903 })
904 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
905 .fill(sprite_fill.clone());
906
907 let mut sprite_positions = vec![];
909 for a in 0..5 {
910 sprite_positions.push(Vec2::new(center.x + 1 + a, center.y + 2));
911 }
912 for b in 0..=1 {
913 sprite_positions.push(Vec2::new(center.x, center.y + 3 + b));
914 }
915 for sprite_pos in sprite_positions {
916 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
917 for r in 0..rows {
918 painter
919 .aabb(Aabb {
920 min: sprite_pos.with_z(upper_alt + 10 + r),
921 max: (sprite_pos + 1).with_z(upper_alt + 11 + r),
922 })
923 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
924 .fill(Fill::Block(Block::air(
925 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32 {
926 0 => SpriteKind::Barrel,
927 _ => SpriteKind::CrateBlock,
928 },
929 )));
930 }
931 }
932
933 painter.spawn(
935 EntityInfo::at(self.campfire_pos.map(|e| e as f32 + 0.5))
936 .into_special(SpecialEntity::Waypoint),
937 );
938
939 painter
941 .cylinder_with_radius(center.with_z(base - 1), foundation_radius - 1.0, 1.0)
942 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
943 .fill(grass.clone());
944 painter
945 .cylinder_with_radius(center.with_z(base - 1), 10.0, 1.0)
946 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
947 .fill(brick.clone());
948
949 painter
950 .cylinder_with_radius(
951 center.with_z(min_foundation_alt),
952 foundation_radius,
953 (base - min_foundation_alt - 1) as f32,
954 )
955 .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
956 .fill(dirt.clone());
957
958 let stair_height = 8;
959 let stair_width = 3;
960 let stair_levels = (base - min_foundation_alt - 1) / (stair_height + 1);
961 let stair_drop = 4;
962 let stair_landing_space = stair_height + 3;
963 let stairtop = base - 1;
964 let edge_clear = aabb(
998 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
999 .with_z(min_foundation_alt),
1000 Vec2::new(center.x + 4, center.y + foundation_radius as i32).with_z(base + 1),
1001 );
1002 let edge_fill = aabb(
1003 Vec2::new(center.x - 7, center.y + foundation_radius as i32 - 2).with_z(stairtop),
1004 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 2).with_z(stairtop),
1005 );
1006 let stair = aabb(
1007 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
1008 .with_z(stairtop - stair_height),
1009 Vec2::new(
1010 center.x + 3,
1011 center.y + foundation_radius as i32 + stair_width - 2,
1012 )
1013 .with_z(stairtop - 1),
1014 );
1015 let middirt = aabb(
1016 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
1017 .with_z(stairtop - stair_height - 1),
1018 Vec2::new(
1019 center.x + 3,
1020 center.y + foundation_radius as i32 + stair_width - 2,
1021 )
1022 .with_z(stairtop - stair_height - (stair_drop + 1)),
1023 );
1024 let stair_landing = aabb(
1025 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 1).with_z(stairtop),
1026 Vec2::new(
1027 center.x + 4,
1028 center.y + foundation_radius as i32 + stair_width - 2,
1029 )
1030 .with_z(stairtop),
1031 );
1032 let stair_column = aabb(
1033 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 1).with_z(stairtop - 1),
1034 Vec2::new(
1035 center.x + 4,
1036 center.y + foundation_radius as i32 + stair_width - 2,
1037 )
1038 .with_z(stairtop - (stair_height + stair_drop)),
1039 );
1040
1041 let slice = painter.aabb(edge_clear);
1042 let edge = painter.aabb(edge_fill);
1043 let stair_cap_even =
1044 painter
1045 .ramp(stair, Dir2::X)
1046 .intersect(painter.ramp(stair, Dir2::X).rotate_about(
1047 Mat3::new(-1, 0, 0, 0, 1, 0, 0, 0, -1),
1048 center.with_z(stairtop - 4),
1049 ));
1050 let stair_cap_odd =
1051 stair_cap_even.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1), stair.center());
1052 let stair_base_even = painter
1053 .ramp(stair, Dir2::X)
1054 .intersect(painter.ramp(stair, Dir2::X).translate(Vec3::new(1, 0, 0)))
1055 .union(painter.aabb(middirt))
1056 .union(
1057 painter
1058 .ramp(stair, Dir2::X)
1059 .rotate_about(
1060 Mat3::new(-1, 0, 0, 0, 1, 0, 0, 0, -1),
1061 center.with_z(stairtop - 4),
1062 )
1063 .translate(Vec3::new(0, 0, -(stair_height + stair_drop))),
1064 );
1065 let stair_base_odd =
1066 stair_base_even.rotate_about(Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1), stair.center());
1067 let stair_column_cap = painter.aabb(stair_landing);
1068 let stair_column_base = painter.aabb(stair_column);
1069
1070 for side in 0..4 {
1072 let rot: f32 = (self.rotation + side as f32 * std::f32::consts::FRAC_PI_2)
1073 .rem_euclid(std::f32::consts::TAU);
1074
1075 painter.fill(
1077 slice.rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1078 Fill::Block(Block::empty()),
1079 );
1080 painter.fill(
1081 edge.rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1082 brick.clone(),
1083 );
1084 painter.fill(
1085 edge.translate(Vec3::new(0, 0, 1))
1086 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1087 Fill::Block(Block::empty()),
1088 );
1089 let picketx = 3;
1092 for i in 0..3 {
1093 painter
1094 .line(
1095 Vec2::new(
1096 center.x + picketx - i * 4,
1097 center.y + foundation_radius as i32 - 2,
1098 )
1099 .with_z(base),
1100 Vec2::new(
1101 center.x + picketx - i * 4 - 2,
1102 center.y + foundation_radius as i32 - 2,
1103 )
1104 .with_z(base),
1105 0.5,
1106 )
1107 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1108 .fill(brick.clone());
1109 }
1110 painter
1111 .aabb(aabb(
1112 Vec2::new(center.x + 4, center.y + 4).with_z(base - 1),
1113 Vec2::new(center.x + 6, center.y + foundation_radius as i32 - 2)
1114 .with_z(base - 1),
1115 ))
1116 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1117 .fill(brick.clone());
1118
1119 if stair_levels == 0 {
1121 painter
1122 .aabb(aabb(
1123 Vec2::new(center.x - 4, center.y + foundation_radius as i32 - 1)
1124 .with_z(base - 7),
1125 Vec2::new(center.x + 4, center.y + foundation_radius as i32)
1126 .with_z(base - 2),
1127 ))
1128 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1129 .fill(dirt.clone());
1130 continue;
1131 }
1132
1133 for level in 0..stair_levels {
1135 let y_off = level % 2 * stair_width;
1136 let stair_y_off = if level % 2 > 0 && stair_width % 2 > 0 {
1137 y_off + 1
1138 } else {
1139 y_off
1140 };
1141 if level % 2 == 0 {
1142 painter.fill(
1143 stair_cap_even
1144 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1145 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1146 brick.clone(),
1147 );
1148 painter.fill(
1149 stair_base_even
1150 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1151 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1152 dirt.clone(),
1153 );
1154 } else {
1155 painter.fill(
1156 stair_cap_odd
1157 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1158 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1159 brick.clone(),
1160 );
1161 painter.fill(
1162 stair_base_odd
1163 .translate(Vec3::new(0, stair_y_off, level * -(stair_height + 1)))
1164 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1165 dirt.clone(),
1166 );
1167 }
1168 painter.fill(
1169 stair_column_cap
1170 .translate(Vec3::new(
1171 level % 2 * -stair_landing_space,
1172 y_off,
1173 level * -(stair_height + 1),
1174 ))
1175 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1176 brick.clone(),
1177 );
1178 painter.fill(
1179 stair_column_base
1180 .translate(Vec3::new(
1181 level % 2 * -stair_landing_space,
1182 y_off,
1183 level * -(stair_height + 1),
1184 ))
1185 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1186 dirt.clone(),
1187 );
1188 painter.fill(
1189 stair_column_cap
1190 .translate(Vec3::new(
1191 (level + 1) % 2 * -stair_landing_space,
1192 y_off,
1193 (level + 1) * -(stair_height + 1),
1194 ))
1195 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1196 brick.clone(),
1197 );
1198 painter.fill(
1199 stair_column_base
1200 .translate(Vec3::new(
1201 (level + 1) % 2 * -stair_landing_space,
1202 y_off,
1203 (level + 1) * -(stair_height + 1),
1204 ))
1205 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base)),
1206 dirt.clone(),
1207 );
1208 let lamp_pos = Vec2::new(
1209 center.x - 5 + level % 2 * (stair_height + 1),
1210 center.y + foundation_radius as i32 - 1,
1211 )
1212 .with_z(base - 5 - level * (stair_height + 1));
1213 let lamp_ori = (((rot / std::f32::consts::FRAC_PI_2) as u8 * 2) + 10) % 8;
1214 painter
1215 .aabb(aabb(lamp_pos, lamp_pos))
1216 .rotate_about(Mat3::rotation_z(rot).as_(), center.with_z(base))
1217 .fill(Fill::sprite_ori(
1218 SpriteKind::LanternAirshipWallBrownS,
1219 lamp_ori,
1220 ));
1221 }
1222 }
1223 }
1224}
1225
1226fn aabb(min: Vec3<i32>, max: Vec3<i32>) -> Aabb<i32> {
1227 let aabb = Aabb { min, max }.made_valid();
1228 Aabb {
1229 min: aabb.min,
1230 max: aabb.max + 1,
1231 }
1232}