veloren_world/site2/plot/
coastal_airship_dock.rs

1use super::*;
2use crate::{
3    Land,
4    site2::gen::place_circular,
5    util::{CARDINALS, RandomField, Sampler},
6};
7use common::{
8    generation::SpecialEntity,
9    terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::sync::Arc;
13use vek::*;
14
15/// Represents house data generated by the `generate()` method
16pub struct CoastalAirshipDock {
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    base: i32,
22    pub center: Vec2<i32>,
23    size: i32,
24    bldg_height: i32,
25    diameter: i32,
26    pub docking_positions: Vec<Vec3<i32>>,
27}
28
29impl CoastalAirshipDock {
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 = 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 diameter = (bounds.max.x - bounds.min.x).min(bounds.max.y - bounds.min.y);
45        let alt = alt.unwrap_or_else(|| {
46            land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
47        }) + 2;
48        let size = 20;
49        let bldg_height = 12;
50        let base = alt + 1;
51        let center = bounds.center();
52        let mut docking_positions = vec![];
53        let top_floor = base + (bldg_height * 6) - 3;
54        for dir in CARDINALS {
55            let docking_pos = center + dir * (size - 1);
56            docking_positions.push(docking_pos.with_z(top_floor - 1));
57        }
58        Self {
59            door_tile: door_tile_pos,
60            alt,
61            base,
62            center,
63            size,
64            bldg_height,
65            diameter,
66            docking_positions,
67        }
68    }
69
70    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
71        SpawnRules {
72            trees: {
73                // dock is 3 tiles = 18 blocks in radius
74                // airships are 20 blocks wide.
75                // Leave extra space for tree width (at lease 15 extra).
76                // Don't allow trees within 18 + 20 + 15 = 53 blocks of the dock center
77                const AIRSHIP_MIN_TREE_DIST2: i32 = 53i32.pow(2);
78                wpos.distance_squared(self.center) > AIRSHIP_MIN_TREE_DIST2
79            },
80            waypoints: false,
81            ..SpawnRules::default()
82        }
83    }
84}
85
86impl Structure for CoastalAirshipDock {
87    #[cfg(feature = "use-dyn-lib")]
88    const UPDATE_FN: &'static [u8] = b"render_coastal_airship_dock\0";
89
90    #[cfg_attr(
91        feature = "be-dyn-lib",
92        unsafe(export_name = "render_coastal_airship_dock")
93    )]
94    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
95        let base = self.base;
96        let center = self.center;
97        let white = Fill::Sampling(Arc::new(|center| {
98            Some(match (RandomField::new(0).get(center)) % 37 {
99                0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
100                9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
101                18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
102                27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
103                _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
104            })
105        }));
106        let blue_broken = Fill::Sampling(Arc::new(|center| {
107            Some(match (RandomField::new(0).get(center)) % 20 {
108                0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
109                _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
110            })
111        }));
112
113        let length = self.diameter / 2;
114        let width = (self.diameter / 2) - 1;
115        let height = 15;
116        // fence, blue gates
117        painter
118            .aabb(Aabb {
119                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
120                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
121            })
122            .fill(blue_broken.clone());
123
124        for dir in CARDINALS {
125            let frame_pos = Vec2::new(
126                center.x + dir.x * (length + 5),
127                center.y + dir.y * (width + 5),
128            );
129            painter
130                .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
131                .fill(blue_broken.clone());
132        }
133        // foundation
134        painter
135            .aabb(Aabb {
136                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
137                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
138            })
139            .fill(white.clone());
140        for f in 0..8 {
141            painter
142                .aabb(Aabb {
143                    min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
144                        .with_z(base - 3 - f),
145                    max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
146                        .with_z(base - 2 - f),
147                })
148                .fill(white.clone());
149        }
150        // clear yard
151        painter
152            .aabb(Aabb {
153                min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
154                max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
155            })
156            .clear();
157        // clear entries
158        for dir in CARDINALS {
159            let clear_pos = Vec2::new(
160                center.x + dir.x * (length + 7),
161                center.y + dir.y * (width + 7),
162            );
163            painter
164                .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
165                .clear();
166        }
167
168        // rooms
169        let size = self.size;
170        let room_offset = size / 6;
171        let bldg_height = self.bldg_height;
172        for r in 0..=4 {
173            let bldg_size = size - (room_offset * r);
174            let bldg_base = base + ((bldg_height + 2) * r);
175            if r == 4 {
176                painter
177                    .cylinder(Aabb {
178                        min: (center - bldg_size - 2).with_z(bldg_base + bldg_height - 1),
179                        max: (center + bldg_size + 2).with_z(bldg_base + bldg_height),
180                    })
181                    .fill(white.clone());
182                painter
183                    .cylinder(Aabb {
184                        min: (center - bldg_size - 2).with_z(bldg_base + bldg_height),
185                        max: (center + bldg_size + 2).with_z(bldg_base + bldg_height + 1),
186                    })
187                    .fill(blue_broken.clone());
188                painter
189                    .cylinder(Aabb {
190                        min: (center - bldg_size - 1).with_z(bldg_base + bldg_height),
191                        max: (center + bldg_size + 1).with_z(bldg_base + bldg_height + 1),
192                    })
193                    .clear();
194
195                let cargo_pos = Vec2::new(center.x, center.y + 5);
196                for dir in CARDINALS {
197                    let sprite_pos = cargo_pos + dir;
198                    let rows = 1 + (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
199                    for r in 0..rows {
200                        painter
201                            .aabb(Aabb {
202                                min: (sprite_pos).with_z(bldg_base + bldg_height + r),
203                                max: (sprite_pos + 1).with_z(bldg_base + bldg_height + 1 + r),
204                            })
205                            .fill(Fill::Block(Block::air(
206                                match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
207                                    as i32
208                                {
209                                    0 => SpriteKind::Barrel,
210                                    _ => SpriteKind::CrateBlock,
211                                },
212                            )));
213                        if r > 1 {
214                            painter.owned_resource_sprite(
215                                sprite_pos.with_z(bldg_base + bldg_height + 1 + r),
216                                SpriteKind::Crate,
217                                0,
218                            );
219                        }
220                    }
221
222                    // docks
223                    let gangway_pos = center + dir * (size / 2);
224                    let dock_pos = center + dir * (size - 4);
225                    painter
226                        .aabb(Aabb {
227                            min: (gangway_pos - 3).with_z(bldg_base + bldg_height - 1),
228                            max: (gangway_pos + 3).with_z(bldg_base + bldg_height),
229                        })
230                        .fill(white.clone());
231                    painter
232                        .cylinder(Aabb {
233                            min: (dock_pos - 4).with_z(bldg_base + bldg_height),
234                            max: (dock_pos + 4).with_z(bldg_base + bldg_height + 1),
235                        })
236                        .fill(blue_broken.clone());
237                    painter
238                        .cylinder(Aabb {
239                            min: (dock_pos - 3).with_z(bldg_base + bldg_height - 1),
240                            max: (dock_pos + 3).with_z(bldg_base + bldg_height + 1),
241                        })
242                        .fill(white.clone());
243                }
244                // campfire
245                let campfire_pos = center.with_z(bldg_base + bldg_height);
246                painter.spawn(
247                    EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
248                        .into_special(SpecialEntity::Waypoint),
249                );
250            }
251            painter
252                .cylinder(Aabb {
253                    min: (center - bldg_size).with_z(bldg_base - 2),
254                    max: (center + bldg_size).with_z(bldg_base + bldg_height),
255                })
256                .fill(white.clone());
257        }
258        for r in 0..=4 {
259            let bldg_size = size - (room_offset * r);
260            let bldg_base = base + ((bldg_height + 2) * r);
261
262            let step_positions = place_circular(center, (bldg_size - 1) as f32, 14);
263            for (s, step_pos) in step_positions.enumerate() {
264                let step_size = (size / 3) - r;
265
266                painter
267                    .cylinder(Aabb {
268                        min: (step_pos - step_size).with_z(bldg_base - 2 + s as i32),
269                        max: (step_pos + step_size).with_z(bldg_base + 4 + s as i32),
270                    })
271                    .clear();
272                painter
273                    .cylinder(Aabb {
274                        min: (step_pos - step_size).with_z(bldg_base - 3 + s as i32),
275                        max: (step_pos + step_size).with_z(bldg_base - 2 + s as i32),
276                    })
277                    .fill(blue_broken.clone());
278                painter
279                    .cylinder(Aabb {
280                        min: (step_pos - step_size + 1).with_z(bldg_base - 4 + s as i32),
281                        max: (step_pos + step_size - 1).with_z(bldg_base - 2 + s as i32),
282                    })
283                    .fill(white.clone());
284            }
285            let lamp_positions = place_circular(center, (bldg_size + 1) as f32, 14);
286            for (l, lamp_pos) in lamp_positions.enumerate() {
287                if (RandomField::new(0).get(lamp_pos.with_z(base)) % 4) < 1 {
288                    painter
289                        .aabb(Aabb {
290                            min: (lamp_pos - 1).with_z(bldg_base - 3 + l as i32),
291                            max: (lamp_pos + 1).with_z(bldg_base - 2 + l as i32),
292                        })
293                        .fill(blue_broken.clone());
294
295                    painter.sprite(
296                        lamp_pos.with_z(bldg_base - 2 + l as i32),
297                        SpriteKind::FireBowlGround,
298                    );
299                }
300            }
301        }
302    }
303}