veloren_world/site/plot/
savannah_airship_dock.rs

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