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(feature = "be-dyn-lib", export_name = "render_coastal_airship_dock")]
91    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
92        let base = self.base;
93        let center = self.center;
94        let white = Fill::Sampling(Arc::new(|center| {
95            Some(match (RandomField::new(0).get(center)) % 37 {
96                0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
97                9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
98                18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
99                27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
100                _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
101            })
102        }));
103        let blue_broken = Fill::Sampling(Arc::new(|center| {
104            Some(match (RandomField::new(0).get(center)) % 20 {
105                0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
106                _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
107            })
108        }));
109
110        let length = self.diameter / 2;
111        let width = (self.diameter / 2) - 1;
112        let height = 15;
113        // fence, blue gates
114        painter
115            .aabb(Aabb {
116                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
117                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
118            })
119            .fill(blue_broken.clone());
120
121        for dir in CARDINALS {
122            let frame_pos = Vec2::new(
123                center.x + dir.x * (length + 5),
124                center.y + dir.y * (width + 5),
125            );
126            painter
127                .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
128                .fill(blue_broken.clone());
129        }
130        // foundation
131        painter
132            .aabb(Aabb {
133                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
134                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
135            })
136            .fill(white.clone());
137        for f in 0..8 {
138            painter
139                .aabb(Aabb {
140                    min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
141                        .with_z(base - 3 - f),
142                    max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
143                        .with_z(base - 2 - f),
144                })
145                .fill(white.clone());
146        }
147        // clear yard
148        painter
149            .aabb(Aabb {
150                min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
151                max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
152            })
153            .clear();
154        // clear entries
155        for dir in CARDINALS {
156            let clear_pos = Vec2::new(
157                center.x + dir.x * (length + 7),
158                center.y + dir.y * (width + 7),
159            );
160            painter
161                .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
162                .clear();
163        }
164
165        // rooms
166        let size = self.size;
167        let room_offset = size / 6;
168        let bldg_height = self.bldg_height;
169        for r in 0..=4 {
170            let bldg_size = size - (room_offset * r);
171            let bldg_base = base + ((bldg_height + 2) * r);
172            if r == 4 {
173                painter
174                    .cylinder(Aabb {
175                        min: (center - bldg_size - 2).with_z(bldg_base + bldg_height - 1),
176                        max: (center + bldg_size + 2).with_z(bldg_base + bldg_height),
177                    })
178                    .fill(white.clone());
179                painter
180                    .cylinder(Aabb {
181                        min: (center - bldg_size - 2).with_z(bldg_base + bldg_height),
182                        max: (center + bldg_size + 2).with_z(bldg_base + bldg_height + 1),
183                    })
184                    .fill(blue_broken.clone());
185                painter
186                    .cylinder(Aabb {
187                        min: (center - bldg_size - 1).with_z(bldg_base + bldg_height),
188                        max: (center + bldg_size + 1).with_z(bldg_base + bldg_height + 1),
189                    })
190                    .clear();
191
192                let cargo_pos = Vec2::new(center.x, center.y + 5);
193                for dir in CARDINALS {
194                    let sprite_pos = cargo_pos + dir;
195                    let rows = 1 + (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
196                    for r in 0..rows {
197                        painter
198                            .aabb(Aabb {
199                                min: (sprite_pos).with_z(bldg_base + bldg_height + r),
200                                max: (sprite_pos + 1).with_z(bldg_base + bldg_height + 1 + r),
201                            })
202                            .fill(Fill::Block(Block::air(
203                                match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
204                                    as i32
205                                {
206                                    0 => SpriteKind::Barrel,
207                                    _ => SpriteKind::CrateBlock,
208                                },
209                            )));
210                        if r > 1 {
211                            painter.owned_resource_sprite(
212                                sprite_pos.with_z(bldg_base + bldg_height + 1 + r),
213                                SpriteKind::Crate,
214                                0,
215                            );
216                        }
217                    }
218
219                    // docks
220                    let gangway_pos = center + dir * (size / 2);
221                    let dock_pos = center + dir * (size - 4);
222                    painter
223                        .aabb(Aabb {
224                            min: (gangway_pos - 3).with_z(bldg_base + bldg_height - 1),
225                            max: (gangway_pos + 3).with_z(bldg_base + bldg_height),
226                        })
227                        .fill(white.clone());
228                    painter
229                        .cylinder(Aabb {
230                            min: (dock_pos - 4).with_z(bldg_base + bldg_height),
231                            max: (dock_pos + 4).with_z(bldg_base + bldg_height + 1),
232                        })
233                        .fill(blue_broken.clone());
234                    painter
235                        .cylinder(Aabb {
236                            min: (dock_pos - 3).with_z(bldg_base + bldg_height - 1),
237                            max: (dock_pos + 3).with_z(bldg_base + bldg_height + 1),
238                        })
239                        .fill(white.clone());
240                }
241                // campfire
242                let campfire_pos = center.with_z(bldg_base + bldg_height);
243                painter.spawn(
244                    EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
245                        .into_special(SpecialEntity::Waypoint),
246                );
247            }
248            painter
249                .cylinder(Aabb {
250                    min: (center - bldg_size).with_z(bldg_base - 2),
251                    max: (center + bldg_size).with_z(bldg_base + bldg_height),
252                })
253                .fill(white.clone());
254        }
255        for r in 0..=4 {
256            let bldg_size = size - (room_offset * r);
257            let bldg_base = base + ((bldg_height + 2) * r);
258
259            let step_positions = place_circular(center, (bldg_size - 1) as f32, 14);
260            for (s, step_pos) in step_positions.enumerate() {
261                let step_size = (size / 3) - r;
262
263                painter
264                    .cylinder(Aabb {
265                        min: (step_pos - step_size).with_z(bldg_base - 2 + s as i32),
266                        max: (step_pos + step_size).with_z(bldg_base + 4 + s as i32),
267                    })
268                    .clear();
269                painter
270                    .cylinder(Aabb {
271                        min: (step_pos - step_size).with_z(bldg_base - 3 + s as i32),
272                        max: (step_pos + step_size).with_z(bldg_base - 2 + s as i32),
273                    })
274                    .fill(blue_broken.clone());
275                painter
276                    .cylinder(Aabb {
277                        min: (step_pos - step_size + 1).with_z(bldg_base - 4 + s as i32),
278                        max: (step_pos + step_size - 1).with_z(bldg_base - 2 + s as i32),
279                    })
280                    .fill(white.clone());
281            }
282            let lamp_positions = place_circular(center, (bldg_size + 1) as f32, 14);
283            for (l, lamp_pos) in lamp_positions.enumerate() {
284                if (RandomField::new(0).get(lamp_pos.with_z(base)) % 4) < 1 {
285                    painter
286                        .aabb(Aabb {
287                            min: (lamp_pos - 1).with_z(bldg_base - 3 + l as i32),
288                            max: (lamp_pos + 1).with_z(bldg_base - 2 + l as i32),
289                        })
290                        .fill(blue_broken.clone());
291
292                    painter.sprite(
293                        lamp_pos.with_z(bldg_base - 2 + l as i32),
294                        SpriteKind::FireBowlGround,
295                    );
296                }
297            }
298        }
299    }
300}