veloren_world/site/plot/
airship_dock.rs

1use super::*;
2use crate::{
3    Land,
4    site::gen::{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;
13use vek::*;
14
15/// Represents house data generated by the `generate()` method
16pub struct AirshipDock {
17    /// Approximate altitude of the door tile
18    pub(crate) alt: i32,
19    rotation: f32,
20    pub door_tile: Vec2<i32>,
21    pub center: Vec2<i32>,
22    base: i32,
23    height: i32,
24    pub docking_positions: Vec<Vec3<i32>>,
25    pub door_dir: Vec2<i32>,
26    campfire_pos: Vec3<i32>,
27}
28
29impl AirshipDock {
30    pub fn generate(
31        land: &Land,
32        _rng: &mut impl Rng,
33        site: &Site,
34        door_tile: Vec2<i32>,
35        door_dir: Vec2<i32>,
36        tile_aabr: Aabr<i32>,
37        alt: Option<i32>,
38    ) -> Self {
39        let door_tile_pos: Vec2<i32> = site.tile_center_wpos(door_tile);
40        let bounds = Aabr {
41            min: site.tile_wpos(tile_aabr.min),
42            max: site.tile_wpos(tile_aabr.max),
43        };
44        let center = bounds.center();
45        let alt = alt.unwrap_or_else(|| {
46            land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
47        });
48        let base = alt + 3;
49        let height = base + 28;
50        // negate the rotation angle because Mat3::rotation_z is CCW, but atan2 returns
51        // PI/2 for (1,0) which is a CW rotation.
52        let rotation = -f32::atan2(door_dir.x as f32, door_dir.y as f32);
53        let mut docking_positions = vec![];
54        for dir in CARDINALS {
55            let pos = (center + dir * 31).with_z(height + 9);
56            docking_positions.push(pos);
57        }
58        let campfire_dir = Mat2::rotation_z(-PI / 4.0) * door_dir.map(|i| i as f32);
59        let campfire_pos = (center.map(|i| i as f32) + (campfire_dir * 11.0))
60            .map(|f| f.round() as i32)
61            .with_z(height + 9);
62        Self {
63            door_tile: door_tile_pos,
64            alt,
65            rotation,
66            center,
67            base,
68            height,
69            docking_positions,
70            door_dir,
71            campfire_pos,
72        }
73    }
74
75    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
76        SpawnRules {
77            trees: {
78                // dock is 5 tiles = 30 blocks in radius
79                // airships are 37 blocks wide.
80                // Some trees are 20 to 30 blocks in radius.
81                // Leave extra space for tree width.
82                // Don't allow trees within 30 + 37 + 30 = 97 blocks of the dock center
83                const AIRSHIP_MIN_TREE_DIST2: i32 = 100;
84                !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST2)
85            },
86            waypoints: false,
87            ..SpawnRules::default()
88        }
89    }
90}
91
92impl Structure for AirshipDock {
93    #[cfg(feature = "use-dyn-lib")]
94    const UPDATE_FN: &'static [u8] = b"render_airshipdock\0";
95
96    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_airshipdock"))]
97    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
98        let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
99        let wood = Fill::Brick(BlockKind::Rock, Rgb::new(45, 28, 21), 24);
100        let woodalt = Fill::Brick(BlockKind::Rock, Rgb::new(30, 22, 15), 24);
101
102        let base = self.base;
103        let center = self.center;
104        let height = self.height;
105
106        // The lower doorway, outside bracing, and platform are outside the column that
107        // forms the main structure. Paint these first so that painting the main column
108        // will carve a hole inside them.
109
110        //lower doorway
111        painter
112            .cylinder_with_radius(
113                Vec2::new(center.x - 1, center.y + 12).with_z(base - 5),
114                4.5,
115                7.0,
116            )
117            .rotate_about_min(Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0))
118            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
119            .fill(wood.clone());
120
121        //bracing
122        painter
123            .cylinder_with_radius(center.with_z(height - 3), 7.0, 1.0)
124            .fill(wood.clone());
125        painter
126            .cylinder_with_radius(center.with_z(height + 6), 7.0, 1.0)
127            .fill(wood.clone());
128        painter
129            .cylinder_with_radius(center.with_z(height + 7), 8.0, 1.0)
130            .fill(wood.clone());
131
132        // platform
133        painter
134            .superquadric(
135                Aabb {
136                    min: (center - 8000).with_z(height + 7),
137                    max: (center + 8000).with_z(height + 11),
138                },
139                0.3,
140            )
141            .intersect(painter.aabb(Aabb {
142                min: (center - 31).with_z(height + 7),
143                max: (center + 31).with_z(height + 11),
144            }))
145            .fill(woodalt.clone());
146        painter
147            .cylinder_with_radius(center.with_z(height + 8), 19.0, 2.0)
148            .fill(woodalt.clone());
149        painter
150            .cylinder_with_radius(center.with_z(height + 8), 18.0, 2.0)
151            .fill(wood.clone());
152        painter
153            .cylinder_with_radius(center.with_z(height + 9), 18.0, 1.0)
154            .clear();
155        painter
156            .aabb(Aabb {
157                min: Vec2::new(center.x - 30, center.y - 2).with_z(height + 8),
158                max: Vec2::new(center.x + 30, center.y + 2).with_z(height + 10),
159            })
160            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
161            .fill(wood.clone());
162        painter
163            .aabb(Aabb {
164                min: Vec2::new(center.x - 30, center.y - 2).with_z(height + 9),
165                max: Vec2::new(center.x + 30, center.y + 2).with_z(height + 10),
166            })
167            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
168            .clear();
169        painter
170            .aabb(Aabb {
171                min: Vec2::new(center.x - 2, center.y - 30).with_z(height + 8),
172                max: Vec2::new(center.x + 2, center.y + 30).with_z(height + 10),
173            })
174            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
175            .fill(wood.clone());
176        painter
177            .aabb(Aabb {
178                min: Vec2::new(center.x - 2, center.y - 30).with_z(height + 9),
179                max: Vec2::new(center.x + 2, center.y + 30).with_z(height + 10),
180            })
181            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
182            .clear();
183
184        // Agent desk
185        painter
186            .cylinder_with_radius(center.with_z(height + 9), 9.0, 6.0)
187            .intersect(painter.aabb(Aabb {
188                min: (Vec2::new(center.x + 2, center.y - 2)).with_z(height + 9),
189                max: (Vec2::new(center.x + 40, center.y - 40)).with_z(height + 16),
190            }))
191            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
192            .fill(woodalt.clone());
193        // carve out the middle
194        painter
195            .cylinder_with_radius(center.with_z(height + 9), 9.0, 5.0)
196            .intersect(painter.aabb(Aabb {
197                min: (Vec2::new(center.x + 3, center.y - 3)).with_z(height + 9),
198                max: (Vec2::new(center.x + 40, center.y - 40)).with_z(height + 15),
199            }))
200            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
201            .clear();
202        // Desk radius
203        painter
204            .cylinder_with_radius(center.with_z(height + 9), 9.0, 1.0)
205            .intersect(painter.aabb(Aabb {
206                min: (Vec2::new(center.x + 3, center.y - 3)).with_z(height + 9),
207                max: (Vec2::new(center.x + 40, center.y - 40)).with_z(height + 11),
208            }))
209            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
210            .fill(woodalt.clone());
211        // Clear inner floor
212        painter
213            .cylinder_with_radius(center.with_z(height + 9), 8.0, 1.0)
214            .intersect(painter.aabb(Aabb {
215                min: (Vec2::new(center.x + 3, center.y - 3)).with_z(height + 9),
216                max: (Vec2::new(center.x + 40, center.y - 40)).with_z(height + 11),
217            }))
218            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
219            .clear();
220        // Clear extra wall extensions
221        painter
222            .line(
223                Vec2::new(center.x + 2, center.y - 9).with_z(height + 9),
224                Vec2::new(center.x + 2, center.y - 9).with_z(height + 16),
225                0.5,
226            )
227            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
228            .clear();
229        painter
230            .line(
231                Vec2::new(center.x + 8, center.y - 3).with_z(height + 9),
232                Vec2::new(center.x + 8, center.y - 3).with_z(height + 16),
233                0.5,
234            )
235            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
236            .clear();
237
238        // The main dock structure is a brick column.
239
240        //column
241        painter
242            .cylinder_with_radius(center.with_z(base), 6.0, 45.0)
243            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
244            .fill(brick.clone());
245
246        painter
247            .cylinder_with_radius(center.with_z(base), 7.0, 1.0)
248            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
249            .fill(brick.clone());
250
251        // ring at floor level outside the column
252        painter
253            .cylinder_with_radius(center.with_z(base + 35), 7.0, 2.0)
254            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
255            .fill(brick.clone());
256        // half ring with offset to exclude the agent area floor
257        painter
258            .cylinder_with_radius(center.with_z(height + 9), 7.0, 1.0)
259            .intersect(painter.aabb(Aabb {
260                min: (Vec2::new(center.x + 2, center.y - 8)).with_z(height + 9),
261                max: (Vec2::new(center.x - 8, center.y + 8)).with_z(height + 10),
262            }))
263            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
264            .fill(brick.clone());
265        // line under doorway
266        painter
267            .line(
268                Vec2::new(center.x + 6, center.y - 2).with_z(height + 9),
269                Vec2::new(center.x + 6, center.y + 2).with_z(height + 9),
270                0.5,
271            )
272            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
273            .fill(brick.clone());
274        // finally, clear the entire inside of the tower
275        painter
276            .cylinder_with_radius(center.with_z(base), 5.0, 45.0)
277            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
278            .clear();
279
280        //lower doorway cut
281        painter
282            .cylinder_with_radius(
283                Vec2::new(center.x - 1, center.y + 12).with_z(base - 5),
284                3.5,
285                7.0,
286            )
287            .rotate_about_min(Mat3::new(1, 0, 0, 0, 0, -1, 0, 1, 0))
288            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
289            .clear();
290        // Base
291        painter
292            .aabb(Aabb {
293                min: Vec2::new(center.x - 11, center.y - 11).with_z(base - 16),
294                max: Vec2::new(center.x + 11, center.y + 11).with_z(base),
295            })
296            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
297            .fill(brick.clone());
298
299        //stair cut
300        painter
301            .aabb(Aabb {
302                min: Vec2::new(center.x - 6, center.y + 9).with_z(base - 1),
303                max: Vec2::new(center.x + 4, center.y + 11).with_z(base),
304            })
305            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
306            .clear();
307        painter
308            .aabb(Aabb {
309                min: Vec2::new(center.x - 5, center.y + 10).with_z(base - 2),
310                max: Vec2::new(center.x + 3, center.y + 11).with_z(base - 1),
311            })
312            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
313            .clear();
314        //cone
315        painter
316            .cone_with_radius(center.with_z(base + 45), 8.0, 18.0)
317            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
318            .fill(wood.clone());
319        //remove 1/4 cyl
320        painter
321            .aabb(Aabb {
322                min: Vec2::new(center.x - 1, center.y + 1).with_z(height + 9),
323                max: Vec2::new(center.x + 6, center.y + 6).with_z(height + 17),
324            })
325            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
326            .fill(brick.clone());
327        painter
328            .aabb(Aabb {
329                min: Vec2::new(center.x, center.y + 2).with_z(height + 9),
330                max: Vec2::new(center.x + 6, center.y + 7).with_z(height + 17),
331            })
332            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
333            .clear();
334        //platform cleanup
335        painter
336            .aabb(Aabb {
337                min: Vec2::new(center.x - 2, center.y - 15).with_z(height + 8),
338                max: Vec2::new(center.x + 6, center.y + 9).with_z(height + 9),
339            })
340            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
341            .fill(wood.clone());
342
343        //upper door
344        painter
345            .aabb(Aabb {
346                min: Vec2::new(center.x + 5, center.y - 2).with_z(height + 10),
347                max: Vec2::new(center.x + 7, center.y + 2).with_z(height + 13),
348            })
349            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
350            .fill(brick.clone());
351        painter
352            .aabb(Aabb {
353                min: Vec2::new(center.x + 5, center.y - 1).with_z(height + 10),
354                max: Vec2::new(center.x + 7, center.y + 1).with_z(height + 15),
355            })
356            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
357            .fill(brick.clone());
358        painter
359            .aabb(Aabb {
360                min: Vec2::new(center.x + 5, center.y - 1).with_z(height + 10),
361                max: Vec2::new(center.x + 7, center.y + 1).with_z(height + 13),
362            })
363            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
364            .clear();
365        //door sprites
366
367        let door_rot = if self.rotation == 0.0 {
368            (2, 6)
369        } else if self.rotation == PI / 2.0 {
370            (4, 0)
371        } else if self.rotation == PI {
372            (6, 2) //good
373        } else {
374            (0, 4)
375        };
376        let sprite_fill = Fill::Block(Block::air(SpriteKind::Door).with_ori(door_rot.0).unwrap());
377        painter
378            .aabb(Aabb {
379                min: Vec3::new(center.x + 6, center.y - 1, height + 10),
380                max: Vec3::new(center.x + 7, center.y + 0, height + 11),
381            })
382            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
383            .fill(sprite_fill.clone());
384        let sprite_fill = Fill::Block(Block::air(SpriteKind::Door).with_ori(door_rot.1).unwrap());
385        painter
386            .aabb(Aabb {
387                min: Vec3::new(center.x + 6, center.y + 0, height + 10),
388                max: Vec3::new(center.x + 7, center.y + 1, height + 11),
389            })
390            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
391            .fill(sprite_fill.clone());
392
393        //bracing diagonal bits
394        painter
395            .line(
396                Vec2::new(center.x + 5, center.y - 3).with_z(height - 3),
397                Vec2::new(center.x + 17, center.y - 3).with_z(height + 8),
398                1.0,
399            )
400            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
401            .fill(wood.clone());
402        painter
403            .line(
404                Vec2::new(center.x + 5, center.y + 2).with_z(height - 3),
405                Vec2::new(center.x + 17, center.y + 2).with_z(height + 8),
406                1.0,
407            )
408            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
409            .fill(wood.clone());
410        //
411        painter
412            .line(
413                Vec2::new(center.x - 18, center.y - 3).with_z(height + 8),
414                Vec2::new(center.x - 6, center.y - 3).with_z(height - 3),
415                1.0,
416            )
417            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
418            .fill(wood.clone());
419        painter
420            .line(
421                Vec2::new(center.x - 18, center.y + 2).with_z(height + 8),
422                Vec2::new(center.x - 6, center.y + 2).with_z(height - 3),
423                1.0,
424            )
425            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
426            .fill(wood.clone());
427        //
428        painter
429            .line(
430                Vec2::new(center.x - 3, center.y - 18).with_z(height + 8),
431                Vec2::new(center.x - 3, center.y - 6).with_z(height - 3),
432                1.0,
433            )
434            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
435            .fill(wood.clone());
436        painter
437            .line(
438                Vec2::new(center.x + 2, center.y - 18).with_z(height + 8),
439                Vec2::new(center.x + 2, center.y - 6).with_z(height - 3),
440                1.0,
441            )
442            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
443            .fill(wood.clone());
444        //
445        painter
446            .line(
447                Vec2::new(center.x - 3, center.y + 5).with_z(height - 3),
448                Vec2::new(center.x - 3, center.y + 17).with_z(height + 8),
449                1.0,
450            )
451            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
452            .fill(wood.clone());
453        painter
454            .line(
455                Vec2::new(center.x + 2, center.y + 5).with_z(height - 3),
456                Vec2::new(center.x + 2, center.y + 17).with_z(height + 8),
457                1.0,
458            )
459            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
460            .fill(wood.clone());
461
462        //stairs
463        painter
464            .cylinder_with_radius(center.with_z(height + 8), 5.0, 1.0)
465            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
466            .clear();
467
468        let stairs_clear1 = painter.cylinder_with_radius(center.with_z(base), 5.0, 38.0);
469
470        painter
471            .prim(Primitive::sampling(
472                stairs_clear1,
473                spiral_staircase(center.with_z(base + 3), 6.0, 0.5, 9.0),
474            ))
475            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
476            .fill(wood.clone());
477
478        //clean up interface at top
479        painter
480            .aabb(Aabb {
481                min: Vec2::new(center.x + 1, center.y + 3).with_z(height + 8),
482                max: Vec2::new(center.x + 4, center.y + 5).with_z(height + 9),
483            })
484            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
485            .fill(wood.clone());
486        painter
487            .aabb(Aabb {
488                min: Vec2::new(center.x + 0, center.y + 2).with_z(height + 9),
489                max: Vec2::new(center.x + 6, center.y + 7).with_z(height + 10),
490            })
491            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
492            .fill(brick.clone());
493        painter
494            .aabb(Aabb {
495                min: Vec2::new(center.x + 1, center.y + 3).with_z(height + 9),
496                max: Vec2::new(center.x + 6, center.y + 7).with_z(height + 10),
497            })
498            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
499            .clear();
500        painter
501            .aabb(Aabb {
502                min: Vec2::new(center.x + 0, center.y + 2).with_z(height + 9),
503                max: Vec2::new(center.x + 1, center.y + 3).with_z(height + 17),
504            })
505            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
506            .fill(brick.clone());
507
508        let window_rot = if self.rotation == 0.0 || self.rotation == PI {
509            (2, 4)
510        } else {
511            (4, 2)
512        };
513        let sprite_fill = Fill::Block(
514            Block::air(SpriteKind::Window1)
515                .with_ori(window_rot.0)
516                .unwrap(),
517        );
518        //upper window
519        painter
520            .aabb(Aabb {
521                min: Vec2::new(center.x - 6, center.y - 1).with_z(height + 12),
522                max: Vec2::new(center.x - 5, center.y + 1).with_z(height + 15),
523            })
524            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
525            .fill(sprite_fill.clone());
526
527        //lower windows
528        painter
529            .aabb(Aabb {
530                min: Vec2::new(center.x - 6, center.y - 1).with_z(base + 19),
531                max: Vec2::new(center.x - 5, center.y + 1).with_z(base + 22),
532            })
533            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
534            .fill(sprite_fill.clone());
535        painter
536            .aabb(Aabb {
537                min: Vec2::new(center.x - 6, center.y - 1).with_z(base + 1),
538                max: Vec2::new(center.x - 5, center.y + 1).with_z(base + 4),
539            })
540            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
541            .fill(sprite_fill.clone());
542        painter
543            .aabb(Aabb {
544                min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 4),
545                max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 7),
546            })
547            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
548            .fill(sprite_fill.clone());
549        painter
550            .aabb(Aabb {
551                min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 22),
552                max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 25),
553            })
554            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
555            .fill(sprite_fill.clone());
556        painter
557            .aabb(Aabb {
558                min: Vec2::new(center.x + 5, center.y - 1).with_z(base + 30),
559                max: Vec2::new(center.x + 6, center.y + 1).with_z(base + 33),
560            })
561            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
562            .fill(sprite_fill.clone());
563
564        let sprite_fill = Fill::Block(
565            Block::air(SpriteKind::Window1)
566                .with_ori(window_rot.1)
567                .unwrap(),
568        );
569        //side windows
570        painter
571            .aabb(Aabb {
572                min: Vec2::new(center.x - 1, center.y + 5).with_z(base + 17),
573                max: Vec2::new(center.x + 1, center.y + 6).with_z(base + 20),
574            })
575            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
576            .fill(sprite_fill.clone());
577        painter
578            .aabb(Aabb {
579                min: Vec2::new(center.x - 1, center.y - 6).with_z(base + 13),
580                max: Vec2::new(center.x + 1, center.y - 5).with_z(base + 16),
581            })
582            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
583            .fill(sprite_fill.clone());
584
585        //lights
586        painter.rotated_sprite(
587            Vec2::new(center.x - 3, center.y + 5).with_z(base + 8),
588            SpriteKind::WallLampSmall,
589            4,
590        );
591        painter.rotated_sprite(
592            Vec2::new(center.x + 2, center.y + 5).with_z(base + 8),
593            SpriteKind::WallLampSmall,
594            4,
595        );
596        painter.rotated_sprite(
597            Vec2::new(center.x - 3, center.y + 5).with_z(base + 18),
598            SpriteKind::WallLampSmall,
599            4,
600        );
601        painter.rotated_sprite(
602            Vec2::new(center.x + 2, center.y + 5).with_z(base + 18),
603            SpriteKind::WallLampSmall,
604            4,
605        );
606        painter.rotated_sprite(
607            Vec2::new(center.x - 3, center.y - 6).with_z(base + 8),
608            SpriteKind::WallLampSmall,
609            0,
610        );
611        painter.rotated_sprite(
612            Vec2::new(center.x + 2, center.y - 6).with_z(base + 8),
613            SpriteKind::WallLampSmall,
614            0,
615        );
616        painter.rotated_sprite(
617            Vec2::new(center.x - 3, center.y - 6).with_z(base + 18),
618            SpriteKind::WallLampSmall,
619            0,
620        );
621        painter.rotated_sprite(
622            Vec2::new(center.x + 2, center.y - 6).with_z(base + 18),
623            SpriteKind::WallLampSmall,
624            0,
625        );
626
627        painter.rotated_sprite(
628            Vec2::new(center.x + 5, center.y - 3).with_z(base + 13),
629            SpriteKind::WallLampSmall,
630            2,
631        );
632        painter.rotated_sprite(
633            Vec2::new(center.x + 5, center.y + 2).with_z(base + 13),
634            SpriteKind::WallLampSmall,
635            2,
636        );
637        painter.rotated_sprite(
638            Vec2::new(center.x + 5, center.y - 3).with_z(base + 29),
639            SpriteKind::WallLampSmall,
640            2,
641        );
642        painter.rotated_sprite(
643            Vec2::new(center.x + 5, center.y + 2).with_z(base + 29),
644            SpriteKind::WallLampSmall,
645            2,
646        );
647        painter.rotated_sprite(
648            Vec2::new(center.x - 6, center.y - 3).with_z(base + 13),
649            SpriteKind::WallLampSmall,
650            6,
651        );
652        painter.rotated_sprite(
653            Vec2::new(center.x - 6, center.y + 2).with_z(base + 13),
654            SpriteKind::WallLampSmall,
655            6,
656        );
657        painter.rotated_sprite(
658            Vec2::new(center.x - 6, center.y - 3).with_z(base + 29),
659            SpriteKind::WallLampSmall,
660            6,
661        );
662        painter.rotated_sprite(
663            Vec2::new(center.x - 6, center.y + 2).with_z(base + 29),
664            SpriteKind::WallLampSmall,
665            6,
666        );
667        //upper lighting
668        for dir in DIAGONALS {
669            let pos = (center + dir * 12).with_z(height + 7);
670            painter.sprite(pos, SpriteKind::Lantern)
671        }
672        for dir in CARDINALS {
673            let pos = (center + dir * 24).with_z(height + 7);
674            painter.sprite(pos, SpriteKind::Lantern)
675        }
676        let sprite_fill = Fill::Block(Block::air(SpriteKind::Lantern).with_ori(2).unwrap());
677        //on cone lamps
678        painter
679            .aabb(Aabb {
680                min: Vec3::new(center.x - 6, center.y + 5, height + 16),
681                max: Vec3::new(center.x - 5, center.y + 6, height + 17),
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: Vec3::new(center.x + 5, center.y + 5, height + 16),
688                max: Vec3::new(center.x + 6, center.y + 6, height + 17),
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: Vec3::new(center.x - 6, center.y - 6, height + 16),
695                max: Vec3::new(center.x - 5, center.y - 5, height + 17),
696            })
697            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
698            .fill(sprite_fill.clone());
699
700        // laterns on walls of agent desk
701        painter
702            .aabb(Aabb {
703                min: Vec3::new(center.x + 7, center.y - 3, height + 13),
704                max: Vec3::new(center.x + 8, center.y - 4, height + 14),
705            })
706            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
707            .fill(Fill::Block(
708                Block::air(SpriteKind::LanternAirshipWallBrownS)
709                    .with_ori(4)
710                    .unwrap(),
711            ));
712        painter
713            .aabb(Aabb {
714                min: Vec3::new(center.x + 3, center.y - 7, height + 13),
715                max: Vec3::new(center.x + 4, center.y - 8, height + 14),
716            })
717            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
718            .fill(Fill::Block(
719                Block::air(SpriteKind::LanternAirshipWallBrownS)
720                    .with_ori(6)
721                    .unwrap(),
722            ));
723
724        //interior
725
726        painter
727            .aabb(Aabb {
728                min: Vec3::new(center.x - 2, center.y - 3, base + 6),
729                max: Vec3::new(center.x - 1, center.y + -2, base + 7),
730            })
731            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
732            .fill(sprite_fill.clone());
733        painter
734            .aabb(Aabb {
735                min: Vec3::new(center.x - 2, center.y - 3, base + 15),
736                max: Vec3::new(center.x - 1, center.y + -2, base + 16),
737            })
738            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
739            .fill(sprite_fill.clone());
740        painter
741            .aabb(Aabb {
742                min: Vec3::new(center.x - 2, center.y - 3, base + 24),
743                max: Vec3::new(center.x - 1, center.y + -2, base + 25),
744            })
745            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
746            .fill(sprite_fill.clone());
747        painter
748            .aabb(Aabb {
749                min: Vec3::new(center.x - 2, center.y - 3, base + 33),
750                max: Vec3::new(center.x - 1, center.y + -2, base + 34),
751            })
752            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
753            .fill(sprite_fill.clone());
754        painter
755            .aabb(Aabb {
756                min: Vec3::new(center.x - 2, center.y - 3, base + 44),
757                max: Vec3::new(center.x - 1, center.y + -2, base + 45),
758            })
759            .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
760            .fill(sprite_fill.clone());
761
762        // crate and barrel sprites
763        let mut sprite_positions = vec![];
764        for a in 0..5 {
765            sprite_positions.push(Vec2::new(center.x + 1 + a, center.y + 2));
766        }
767        for b in 0..=1 {
768            sprite_positions.push(Vec2::new(center.x, center.y + 3 + b));
769        }
770        for sprite_pos in sprite_positions {
771            let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
772            for r in 0..rows {
773                painter
774                    .aabb(Aabb {
775                        min: sprite_pos.with_z(height + 10 + r),
776                        max: (sprite_pos + 1).with_z(height + 11 + r),
777                    })
778                    .rotate_about(Mat3::rotation_z(self.rotation).as_(), center.with_z(base))
779                    .fill(Fill::Block(Block::air(
780                        match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32 {
781                            0 => SpriteKind::Barrel,
782                            _ => SpriteKind::CrateBlock,
783                        },
784                    )));
785            }
786        }
787
788        // campfire
789        painter.spawn(
790            EntityInfo::at(self.campfire_pos.map(|e| e as f32 + 0.5))
791                .into_special(SpecialEntity::Waypoint),
792        );
793    }
794}