veloren_world/site2/plot/
airship_dock.rs

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