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(
87        feature = "be-dyn-lib",
88        unsafe(export_name = "render_savannah_airship_dock")
89    )]
90    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
91        let base = self.alt + 1;
92        let center = self.center;
93        let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
94        let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
95        let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
96        let color = Fill::Sampling(Arc::new(|center| {
97            Some(match (RandomField::new(0).get(center)) % 7 {
98                0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
99                1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
100                2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
101                3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
102                4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
103                5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
104                _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
105            })
106        }));
107        let length = self.length;
108        let height = length / 2;
109        let platform_height = self.platform_height;
110        let storeys = 1;
111        let radius = length + (length / 3);
112        let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
113        let reed_parts = 36_f32 + reed_var;
114        let phi = TAU / reed_parts;
115
116        // foundation
117        painter
118            .cylinder(Aabb {
119                min: (center - length).with_z(base - 3),
120                max: (center + length + 1).with_z(base - 2),
121            })
122            .fill(clay.clone());
123        painter
124            .cylinder(Aabb {
125                min: (center - length - 1).with_z(base - 4),
126                max: (center + length + 2).with_z(base - 3),
127            })
128            .fill(clay.clone());
129        painter
130            .cylinder(Aabb {
131                min: (center - length - 2).with_z(base - 5),
132                max: (center + length + 3).with_z(base - 4),
133            })
134            .fill(clay.clone());
135        painter
136            .cylinder(Aabb {
137                min: (center - length - 3).with_z(base - height),
138                max: (center + length + 4).with_z(base - 5),
139            })
140            .fill(clay.clone());
141        // platform
142        painter
143            .cylinder(Aabb {
144                min: (center - (2 * (length / 3)) - 1).with_z(base + platform_height - 4),
145                max: (center + (2 * (length / 3)) + 1).with_z(base + platform_height - 3),
146            })
147            .fill(color.clone());
148        painter
149            .cylinder(Aabb {
150                min: (center - (2 * (length / 3))).with_z(base + platform_height - 4),
151                max: (center + (2 * (length / 3))).with_z(base + platform_height - 3),
152            })
153            .fill(clay.clone());
154        painter
155            .cylinder(Aabb {
156                min: (center - length - 2).with_z(base + platform_height - 3),
157                max: (center + length + 2).with_z(base + platform_height - 2),
158            })
159            .fill(color.clone());
160        painter
161            .cylinder(Aabb {
162                min: (center - length - 1).with_z(base + platform_height - 3),
163                max: (center + length + 1).with_z(base + platform_height - 2),
164            })
165            .fill(clay.clone());
166        // docks
167        for dir in CARDINALS {
168            let dock_pos = center + dir * (length + 2);
169            painter
170                .cylinder(Aabb {
171                    min: (dock_pos - 5).with_z(base + platform_height - 3),
172                    max: (dock_pos + 5).with_z(base + platform_height - 2),
173                })
174                .fill(color.clone());
175            painter
176                .cylinder(Aabb {
177                    min: (dock_pos - 4).with_z(base + platform_height - 3),
178                    max: (dock_pos + 4).with_z(base + platform_height - 2),
179                })
180                .fill(wood_dark.clone());
181        }
182
183        // lanterns, crates & barrels
184        for dir in CARDINALS {
185            let lantern_pos = center + (dir * length);
186
187            painter.sprite(
188                lantern_pos.with_z(base + platform_height - 2),
189                SpriteKind::Lantern,
190            );
191        }
192        for dir in DIAGONALS {
193            let cargo_pos = center + (dir * ((length / 2) - 1));
194            for dir in CARDINALS {
195                let sprite_pos = cargo_pos + dir;
196                let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
197                for r in 0..rows {
198                    painter
199                        .aabb(Aabb {
200                            min: (sprite_pos).with_z(base + platform_height - 2 + r),
201                            max: (sprite_pos + 1).with_z(base + platform_height - 1 + r),
202                        })
203                        .fill(Fill::Block(Block::air(
204                            match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32
205                            {
206                                0 => SpriteKind::Barrel,
207                                _ => SpriteKind::CrateBlock,
208                            },
209                        )));
210                    if r > 0 {
211                        painter.owned_resource_sprite(
212                            sprite_pos.with_z(base + platform_height - 1 + r),
213                            SpriteKind::Crate,
214                            0,
215                        );
216                    }
217                }
218            }
219        }
220        // campfire
221        let campfire_pos = (center - (2 * (length / 3)) - 1).with_z(base + platform_height);
222        painter.spawn(
223            EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
224                .into_special(SpecialEntity::Waypoint),
225        );
226        for b in 0..2 {
227            let base = base + (b * platform_height);
228            let radius = radius - (b * (radius / 3));
229            let length = length - (b * (length / 3));
230            // roof cone
231            painter
232                .cone(Aabb {
233                    min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
234                    max: (center + radius)
235                        .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
236                })
237                .fill(reed.clone());
238            painter
239                .cone(Aabb {
240                    min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
241                    max: (center + radius)
242                        .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
243                })
244                .clear();
245            // room
246            for s in 0..storeys {
247                let room = painter.cylinder(Aabb {
248                    min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
249                    max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
250                });
251                room.fill(clay.clone());
252                // decor inlays
253                for dir in DIAGONALS {
254                    let decor_pos = center + dir * (length - 2 - s);
255                    let decor = painter
256                        .line(
257                            center.with_z(base - 1 + (s * (height + 2))),
258                            decor_pos.with_z(base - 1 + (s * (height + 2))),
259                            5.0,
260                        )
261                        .intersect(room);
262                    decor.fill(color.clone());
263                    painter
264                        .line(
265                            center.with_z(base - 1 + (s * (height + 2))),
266                            decor_pos.with_z(base - 1 + (s * (height + 2))),
267                            4.0,
268                        )
269                        .intersect(decor)
270                        .fill(clay.clone());
271                }
272            }
273            // clear rooms
274            painter
275                .cylinder(Aabb {
276                    min: (center - length + 4).with_z(base - 2),
277                    max: (center + 1 + length - 4).with_z(base + (storeys * height)),
278                })
279                .clear();
280            // wood decor
281            painter
282                .cylinder(Aabb {
283                    min: (center - length + 4).with_z(base - 1),
284                    max: (center + 1 + length - 4).with_z(base),
285                })
286                .fill(wood_dark.clone());
287            painter
288                .cylinder(Aabb {
289                    min: (center - length + 4).with_z(base + (storeys * height) - 1),
290                    max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
291                })
292                .fill(wood_dark.clone());
293
294            for s in 0..storeys {
295                // entries, windows
296                for dir in CARDINALS {
297                    let frame_pos = center + dir * (length - 2 - s);
298                    let clear_pos = center + dir * (length + 2 - s);
299
300                    painter
301                        .line(
302                            center.with_z(base - 1 + (s * (height + 2))),
303                            frame_pos.with_z(base - 1 + (s * (height + 2))),
304                            3.0,
305                        )
306                        .fill(color.clone());
307                    painter
308                        .line(
309                            center.with_z(base - 1 + (s * (height + 2))),
310                            clear_pos.with_z(base - 1 + (s * (height + 2))),
311                            2.0,
312                        )
313                        .clear();
314                }
315            }
316            // re clear room
317            painter
318                .cylinder(Aabb {
319                    min: (center - length + 5).with_z(base - 2),
320                    max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
321                })
322                .clear();
323            // floor
324            painter
325                .cylinder(Aabb {
326                    min: (center - (length / 2) - 1).with_z(base - 3),
327                    max: (center + (length / 2) + 1).with_z(base - 2),
328                })
329                .fill(color.clone());
330            painter
331                .cylinder(Aabb {
332                    min: (center - (length / 2) + 1).with_z(base - 3),
333                    max: (center + (length / 2) - 1).with_z(base - 2),
334                })
335                .fill(clay.clone());
336
337            // reed roof lines
338            for n in 1..=reed_parts as i32 {
339                let pos = Vec2::new(
340                    center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
341                    center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
342                );
343                painter
344                    .line(
345                        pos.with_z(base + (storeys * height) - (height / 2)),
346                        center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
347                        1.0,
348                    )
349                    .fill(reed.clone());
350            }
351        }
352
353        // tower
354        let beams_low = place_circular_as_vec(center, (2 * (length / 3)) as f32, 10);
355        let beams_high = place_circular_as_vec(center, (2 * (length / 4)) as f32, 10);
356
357        for b in 0..beams_low.len() {
358            painter
359                .cylinder(Aabb {
360                    min: (beams_low[b] - 4).with_z(base + height - 1),
361                    max: (beams_low[b] + 4).with_z(base + height),
362                })
363                .fill(wood_dark.clone());
364
365            painter
366                .line(
367                    beams_low[b].with_z(base + height),
368                    beams_high[b].with_z(base + platform_height - 4),
369                    1.5,
370                )
371                .fill(wood_dark.clone());
372        }
373        //stairs
374        painter
375            .cylinder(Aabb {
376                min: (center - (length / 3)).with_z(base),
377                max: (center + (length / 3)).with_z(base + platform_height),
378            })
379            .clear();
380
381        let stairs = painter.cylinder(Aabb {
382            min: (center - (length / 3)).with_z(base - 3),
383            max: (center + (length / 3)).with_z(base + platform_height - 2),
384        });
385
386        stairs
387            .sample(spiral_staircase(
388                center.with_z(base - 3),
389                ((length / 3) + 1) as f32,
390                0.5,
391                (platform_height / 4) as f32,
392            ))
393            .fill(clay.clone());
394    }
395}