veloren_world/site2/plot/
savannah_airship_dock.rs

1use super::*;
2use crate::{
3    Land,
4    site2::gen::{place_circular_as_vec, 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::TAU, sync::Arc};
13use vek::*;
14
15/// Represents house data generated by the `generate()` method
16pub struct SavannahAirshipDock {
17    /// Tile position of the door tile
18    pub door_tile: Vec2<i32>,
19    /// Approximate altitude of the door tile
20    pub(crate) alt: i32,
21    pub center: Vec2<i32>,
22    length: i32,
23    platform_height: i32,
24    pub docking_positions: Vec<Vec3<i32>>,
25}
26
27impl SavannahAirshipDock {
28    pub fn generate(
29        land: &Land,
30        _rng: &mut impl Rng,
31        site: &Site,
32        door_tile: Vec2<i32>,
33        door_dir: Vec2<i32>,
34        tile_aabr: Aabr<i32>,
35        alt: Option<i32>,
36    ) -> Self {
37        let door_tile_pos = site.tile_center_wpos(door_tile);
38        let bounds = Aabr {
39            min: site.tile_wpos(tile_aabr.min),
40            max: site.tile_wpos(tile_aabr.max),
41        };
42        let center = bounds.center();
43        let alt = alt.unwrap_or_else(|| {
44            land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
45        }) + 2;
46        let base = alt + 1;
47        let length = 18;
48        let platform_height = 40;
49        let mut docking_positions = vec![];
50        let top_floor = base + platform_height - 2;
51        for dir in CARDINALS {
52            let docking_pos = center + dir * (length + 5);
53            docking_positions.push(docking_pos.with_z(top_floor));
54        }
55
56        Self {
57            door_tile: door_tile_pos,
58            alt,
59            center,
60            length,
61            platform_height,
62            docking_positions,
63        }
64    }
65
66    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
67        SpawnRules {
68            trees: {
69                // dock is 4 tiles = 24 blocks in radius
70                // airships are 20 blocks wide.
71                // Leave extra space for tree width (at least 15 extra).
72                // Don't allow trees within 24 + 20 + 15 = 59 blocks of the dock center
73                const AIRSHIP_MIN_TREE_DIST2: i32 = 59i32.pow(2);
74                wpos.distance_squared(self.center) > AIRSHIP_MIN_TREE_DIST2
75            },
76            waypoints: false,
77            ..SpawnRules::default()
78        }
79    }
80}
81
82impl Structure for SavannahAirshipDock {
83    #[cfg(feature = "use-dyn-lib")]
84    const UPDATE_FN: &'static [u8] = b"render_savannah_airship_dock\0";
85
86    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_savannah_airship_dock")]
87    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
88        let base = self.alt + 1;
89        let center = self.center;
90        let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
91        let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
92        let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
93        let color = Fill::Sampling(Arc::new(|center| {
94            Some(match (RandomField::new(0).get(center)) % 7 {
95                0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
96                1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
97                2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
98                3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
99                4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
100                5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
101                _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
102            })
103        }));
104        let length = self.length;
105        let height = length / 2;
106        let platform_height = self.platform_height;
107        let storeys = 1;
108        let radius = length + (length / 3);
109        let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
110        let reed_parts = 36_f32 + reed_var;
111        let phi = TAU / reed_parts;
112
113        // foundation
114        painter
115            .cylinder(Aabb {
116                min: (center - length).with_z(base - 3),
117                max: (center + length + 1).with_z(base - 2),
118            })
119            .fill(clay.clone());
120        painter
121            .cylinder(Aabb {
122                min: (center - length - 1).with_z(base - 4),
123                max: (center + length + 2).with_z(base - 3),
124            })
125            .fill(clay.clone());
126        painter
127            .cylinder(Aabb {
128                min: (center - length - 2).with_z(base - 5),
129                max: (center + length + 3).with_z(base - 4),
130            })
131            .fill(clay.clone());
132        painter
133            .cylinder(Aabb {
134                min: (center - length - 3).with_z(base - height),
135                max: (center + length + 4).with_z(base - 5),
136            })
137            .fill(clay.clone());
138        // platform
139        painter
140            .cylinder(Aabb {
141                min: (center - (2 * (length / 3)) - 1).with_z(base + platform_height - 4),
142                max: (center + (2 * (length / 3)) + 1).with_z(base + platform_height - 3),
143            })
144            .fill(color.clone());
145        painter
146            .cylinder(Aabb {
147                min: (center - (2 * (length / 3))).with_z(base + platform_height - 4),
148                max: (center + (2 * (length / 3))).with_z(base + platform_height - 3),
149            })
150            .fill(clay.clone());
151        painter
152            .cylinder(Aabb {
153                min: (center - length - 2).with_z(base + platform_height - 3),
154                max: (center + length + 2).with_z(base + platform_height - 2),
155            })
156            .fill(color.clone());
157        painter
158            .cylinder(Aabb {
159                min: (center - length - 1).with_z(base + platform_height - 3),
160                max: (center + length + 1).with_z(base + platform_height - 2),
161            })
162            .fill(clay.clone());
163        // docks
164        for dir in CARDINALS {
165            let dock_pos = center + dir * (length + 2);
166            painter
167                .cylinder(Aabb {
168                    min: (dock_pos - 5).with_z(base + platform_height - 3),
169                    max: (dock_pos + 5).with_z(base + platform_height - 2),
170                })
171                .fill(color.clone());
172            painter
173                .cylinder(Aabb {
174                    min: (dock_pos - 4).with_z(base + platform_height - 3),
175                    max: (dock_pos + 4).with_z(base + platform_height - 2),
176                })
177                .fill(wood_dark.clone());
178        }
179
180        // lanterns, crates & barrels
181        for dir in CARDINALS {
182            let lantern_pos = center + (dir * length);
183
184            painter.sprite(
185                lantern_pos.with_z(base + platform_height - 2),
186                SpriteKind::Lantern,
187            );
188        }
189        for dir in DIAGONALS {
190            let cargo_pos = center + (dir * ((length / 2) - 1));
191            for dir in CARDINALS {
192                let sprite_pos = cargo_pos + dir;
193                let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
194                for r in 0..rows {
195                    painter
196                        .aabb(Aabb {
197                            min: (sprite_pos).with_z(base + platform_height - 2 + r),
198                            max: (sprite_pos + 1).with_z(base + platform_height - 1 + r),
199                        })
200                        .fill(Fill::Block(Block::air(
201                            match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32
202                            {
203                                0 => SpriteKind::Barrel,
204                                _ => SpriteKind::CrateBlock,
205                            },
206                        )));
207                    if r > 0 {
208                        painter.owned_resource_sprite(
209                            sprite_pos.with_z(base + platform_height - 1 + r),
210                            SpriteKind::Crate,
211                            0,
212                        );
213                    }
214                }
215            }
216        }
217        // campfire
218        let campfire_pos = (center - (2 * (length / 3)) - 1).with_z(base + platform_height);
219        painter.spawn(
220            EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
221                .into_special(SpecialEntity::Waypoint),
222        );
223        for b in 0..2 {
224            let base = base + (b * platform_height);
225            let radius = radius - (b * (radius / 3));
226            let length = length - (b * (length / 3));
227            // roof cone
228            painter
229                .cone(Aabb {
230                    min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
231                    max: (center + radius)
232                        .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
233                })
234                .fill(reed.clone());
235            painter
236                .cone(Aabb {
237                    min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
238                    max: (center + radius)
239                        .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
240                })
241                .clear();
242            // room
243            for s in 0..storeys {
244                let room = painter.cylinder(Aabb {
245                    min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
246                    max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
247                });
248                room.fill(clay.clone());
249                // decor inlays
250                for dir in DIAGONALS {
251                    let decor_pos = center + dir * (length - 2 - s);
252                    let decor = painter
253                        .line(
254                            center.with_z(base - 1 + (s * (height + 2))),
255                            decor_pos.with_z(base - 1 + (s * (height + 2))),
256                            5.0,
257                        )
258                        .intersect(room);
259                    decor.fill(color.clone());
260                    painter
261                        .line(
262                            center.with_z(base - 1 + (s * (height + 2))),
263                            decor_pos.with_z(base - 1 + (s * (height + 2))),
264                            4.0,
265                        )
266                        .intersect(decor)
267                        .fill(clay.clone());
268                }
269            }
270            // clear rooms
271            painter
272                .cylinder(Aabb {
273                    min: (center - length + 4).with_z(base - 2),
274                    max: (center + 1 + length - 4).with_z(base + (storeys * height)),
275                })
276                .clear();
277            // wood decor
278            painter
279                .cylinder(Aabb {
280                    min: (center - length + 4).with_z(base - 1),
281                    max: (center + 1 + length - 4).with_z(base),
282                })
283                .fill(wood_dark.clone());
284            painter
285                .cylinder(Aabb {
286                    min: (center - length + 4).with_z(base + (storeys * height) - 1),
287                    max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
288                })
289                .fill(wood_dark.clone());
290
291            for s in 0..storeys {
292                // entries, windows
293                for dir in CARDINALS {
294                    let frame_pos = center + dir * (length - 2 - s);
295                    let clear_pos = center + dir * (length + 2 - s);
296
297                    painter
298                        .line(
299                            center.with_z(base - 1 + (s * (height + 2))),
300                            frame_pos.with_z(base - 1 + (s * (height + 2))),
301                            3.0,
302                        )
303                        .fill(color.clone());
304                    painter
305                        .line(
306                            center.with_z(base - 1 + (s * (height + 2))),
307                            clear_pos.with_z(base - 1 + (s * (height + 2))),
308                            2.0,
309                        )
310                        .clear();
311                }
312            }
313            // re clear room
314            painter
315                .cylinder(Aabb {
316                    min: (center - length + 5).with_z(base - 2),
317                    max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
318                })
319                .clear();
320            // floor
321            painter
322                .cylinder(Aabb {
323                    min: (center - (length / 2) - 1).with_z(base - 3),
324                    max: (center + (length / 2) + 1).with_z(base - 2),
325                })
326                .fill(color.clone());
327            painter
328                .cylinder(Aabb {
329                    min: (center - (length / 2) + 1).with_z(base - 3),
330                    max: (center + (length / 2) - 1).with_z(base - 2),
331                })
332                .fill(clay.clone());
333
334            // reed roof lines
335            for n in 1..=reed_parts as i32 {
336                let pos = Vec2::new(
337                    center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
338                    center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
339                );
340                painter
341                    .line(
342                        pos.with_z(base + (storeys * height) - (height / 2)),
343                        center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
344                        1.0,
345                    )
346                    .fill(reed.clone());
347            }
348        }
349
350        // tower
351        let beams_low = place_circular_as_vec(center, (2 * (length / 3)) as f32, 10);
352        let beams_high = place_circular_as_vec(center, (2 * (length / 4)) as f32, 10);
353
354        for b in 0..beams_low.len() {
355            painter
356                .cylinder(Aabb {
357                    min: (beams_low[b] - 4).with_z(base + height - 1),
358                    max: (beams_low[b] + 4).with_z(base + height),
359                })
360                .fill(wood_dark.clone());
361
362            painter
363                .line(
364                    beams_low[b].with_z(base + height),
365                    beams_high[b].with_z(base + platform_height - 4),
366                    1.5,
367                )
368                .fill(wood_dark.clone());
369        }
370        //stairs
371        painter
372            .cylinder(Aabb {
373                min: (center - (length / 3)).with_z(base),
374                max: (center + (length / 3)).with_z(base + platform_height),
375            })
376            .clear();
377
378        let stairs = painter.cylinder(Aabb {
379            min: (center - (length / 3)).with_z(base - 3),
380            max: (center + (length / 3)).with_z(base + platform_height - 2),
381        });
382
383        stairs
384            .sample(spiral_staircase(
385                center.with_z(base - 3),
386                ((length / 3) + 1) as f32,
387                0.5,
388                (platform_height / 4) as f32,
389            ))
390            .fill(clay.clone());
391    }
392}