veloren_world/site2/plot/
coastal_workshop.rs

1use super::*;
2use crate::{
3    Land,
4    util::{CARDINALS, RandomField, Sampler},
5};
6use common::{
7    generation::SpecialEntity,
8    terrain::{BlockKind, SpriteKind},
9};
10use rand::prelude::*;
11use std::sync::Arc;
12use vek::*;
13
14/// Represents house data generated by the `generate()` method
15pub struct CoastalWorkshop {
16    /// Tile position of the door tile
17    pub door_tile: Vec2<i32>,
18    /// Axis aligned bounding region for the house
19    bounds: Aabr<i32>,
20    /// Approximate altitude of the door tile
21    pub(crate) alt: i32,
22}
23
24impl CoastalWorkshop {
25    pub fn generate(
26        land: &Land,
27        _rng: &mut impl Rng,
28        site: &Site,
29        door_tile: Vec2<i32>,
30        door_dir: Vec2<i32>,
31        tile_aabr: Aabr<i32>,
32        alt: Option<i32>,
33    ) -> Self {
34        let door_tile_pos = site.tile_center_wpos(door_tile);
35        let bounds = Aabr {
36            min: site.tile_wpos(tile_aabr.min),
37            max: site.tile_wpos(tile_aabr.max),
38        };
39        Self {
40            door_tile: door_tile_pos,
41            bounds,
42            alt: alt.unwrap_or_else(|| {
43                land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
44            }) + 2,
45        }
46    }
47}
48
49impl Structure for CoastalWorkshop {
50    #[cfg(feature = "use-dyn-lib")]
51    const UPDATE_FN: &'static [u8] = b"render_coastalworkshop\0";
52
53    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_coastalworkshop"))]
54    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
55        let base = self.alt + 1;
56        let center = self.bounds.center();
57        let white = Fill::Sampling(Arc::new(|center| {
58            Some(match (RandomField::new(0).get(center)) % 37 {
59                0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
60                9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
61                18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
62                27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
63                _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
64            })
65        }));
66        let blue_broken = Fill::Sampling(Arc::new(|center| {
67            Some(match (RandomField::new(0).get(center)) % 20 {
68                0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
69                _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
70            })
71        }));
72        let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
73        let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
74        let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
75
76        // fence, blue gates
77        painter
78            .aabb(Aabb {
79                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
80                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
81            })
82            .fill(blue_broken.clone());
83
84        for dir in CARDINALS {
85            let frame_pos = Vec2::new(
86                center.x + dir.x * (length + 5),
87                center.y + dir.y * (width + 5),
88            );
89            painter
90                .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
91                .fill(blue_broken.clone());
92        }
93        // foundation
94        painter
95            .aabb(Aabb {
96                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
97                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
98            })
99            .fill(white.clone());
100        for f in 0..8 {
101            painter
102                .aabb(Aabb {
103                    min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
104                        .with_z(base - 3 - f),
105                    max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
106                        .with_z(base - 2 - f),
107                })
108                .fill(white.clone());
109        }
110        // clear yard
111        painter
112            .aabb(Aabb {
113                min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
114                max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
115            })
116            .clear();
117        // clear entries
118        for dir in CARDINALS {
119            let clear_pos = Vec2::new(
120                center.x + dir.x * (length + 7),
121                center.y + dir.y * (width + 7),
122            );
123            painter
124                .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
125                .clear();
126        }
127        // roof terrace
128        painter
129            .aabb(Aabb {
130                min: Vec2::new(center.x - length - 3, center.y - width - 3)
131                    .with_z(base - 3 + height),
132                max: Vec2::new(center.x + length + 2, center.y + width + 2)
133                    .with_z(base - 2 + height),
134            })
135            .fill(white.clone());
136        painter
137            .aabb(Aabb {
138                min: Vec2::new(center.x - length - 3, center.y - width - 3)
139                    .with_z(base - 2 + height),
140                max: Vec2::new(center.x + length + 2, center.y + width + 2)
141                    .with_z(base - 1 + height),
142            })
143            .fill(blue_broken.clone());
144        painter
145            .aabb(Aabb {
146                min: Vec2::new(center.x - length - 2, center.y - width - 2)
147                    .with_z(base - 2 + height),
148                max: Vec2::new(center.x + length + 1, center.y + width + 1)
149                    .with_z(base - 1 + height),
150            })
151            .clear();
152        // room
153        painter
154            .aabb(Aabb {
155                min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
156                max: Vec2::new(center.x + length, center.y + width).with_z(base - 1),
157            })
158            .fill(blue_broken.clone());
159        painter
160            .aabb(Aabb {
161                min: Vec2::new(center.x - length + 1, center.y - width + 1).with_z(base - 2),
162                max: Vec2::new(center.x + length - 1, center.y + width - 1)
163                    .with_z(base - 1 + height - 1),
164            })
165            .fill(white.clone());
166
167        // entries
168        let entry_limit = painter.aabb(Aabb {
169            min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
170            max: Vec2::new(center.x + length, center.y + width).with_z(base - 1 + height - 1),
171        });
172        painter
173            .line(
174                Vec2::new(center.x, center.y + 1 - width).with_z(base - 1),
175                Vec2::new(center.x, center.y - 2 + width).with_z(base - 1),
176                8.0,
177            )
178            .intersect(entry_limit)
179            .fill(blue_broken.clone());
180        painter
181            .line(
182                Vec2::new(center.x, center.y - width).with_z(base - 1),
183                Vec2::new(center.x, center.y + width).with_z(base - 1),
184                7.0,
185            )
186            .intersect(entry_limit)
187            .clear();
188        painter
189            .line(
190                Vec2::new(center.x + 1 - length, center.y).with_z(base - 1),
191                Vec2::new(center.x - 2 + length, center.y).with_z(base - 1),
192                8.0,
193            )
194            .intersect(entry_limit)
195            .fill(blue_broken.clone());
196        painter
197            .line(
198                Vec2::new(center.x - length, center.y).with_z(base - 1),
199                Vec2::new(center.x + length, center.y).with_z(base - 1),
200                7.0,
201            )
202            .intersect(entry_limit)
203            .clear();
204        // clear room
205        painter
206            .aabb(Aabb {
207                min: Vec2::new(center.x - length + 2, center.y - width + 2).with_z(base - 2),
208                max: Vec2::new(center.x + length - 2, center.y + width - 2)
209                    .with_z(base - 2 + height - 1),
210            })
211            .clear();
212
213        // room floors
214        painter
215            .aabb(Aabb {
216                min: Vec2::new(center.x - length + 5, center.y - width + 5).with_z(base - 3),
217                max: Vec2::new(center.x + length - 5, center.y + width - 5).with_z(base - 2),
218            })
219            .fill(blue_broken.clone());
220        painter
221            .aabb(Aabb {
222                min: Vec2::new(center.x - length + 6, center.y - width + 6).with_z(base - 3),
223                max: Vec2::new(center.x + length - 6, center.y + width - 6).with_z(base - 2),
224            })
225            .fill(white.clone());
226
227        // wall lamps
228        for d in 0..2 {
229            let door_lamp_pos =
230                Vec2::new(center.x - length + 2 + (d * ((2 * (length)) - 5)), center.y)
231                    .with_z(base + 6);
232            painter.rotated_sprite(
233                door_lamp_pos,
234                SpriteKind::WallLampSmall,
235                2 + ((d * 4) as u8),
236            );
237
238            let lamp_pos = Vec2::new(center.x, center.y - width + 2 + (d * ((2 * (width)) - 5)))
239                .with_z(base + 6);
240            painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
241        }
242        for d in 0..2 {
243            let door_lamp_pos =
244                Vec2::new(center.x - length - 1 + (d * ((2 * (length)) + 1)), center.y)
245                    .with_z(base + 6);
246            painter.rotated_sprite(
247                door_lamp_pos,
248                SpriteKind::WallLampSmall,
249                6 + ((d * 4) as u8),
250            );
251
252            let lamp_pos = Vec2::new(center.x, center.y - width - 1 + (d * ((2 * (width)) + 1)))
253                .with_z(base + 6);
254            painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 8 - ((d * 4) as u8));
255        }
256
257        // chimney
258        painter
259            .cylinder(Aabb {
260                min: (center - 4).with_z(base + height - 4),
261                max: (center + 2).with_z(base - 2 + height + (height / 2)),
262            })
263            .fill(blue_broken);
264
265        let top_limit = painter.aabb(Aabb {
266            min: Vec2::new(center.x - length, center.y - width).with_z(base + height - 2),
267            max: Vec2::new(center.x + length, center.y + width)
268                .with_z(base - 2 + height + (height / 2)),
269        });
270        painter
271            .superquadric(
272                Aabb {
273                    min: Vec2::new(center.x - length - 1, center.y - width - 1)
274                        .with_z(base + height - (height / 2)),
275                    max: Vec2::new(center.x + length, center.y + width)
276                        .with_z(base - 2 + height + (height / 2)),
277                },
278                1.5,
279            )
280            .intersect(top_limit)
281            .fill(white.clone());
282        // clear chimney
283        painter
284            .cylinder(Aabb {
285                min: (center - 3).with_z(base + height - 4),
286                max: (center + 1).with_z(base - 2 + height + (height / 2)),
287            })
288            .clear();
289
290        painter
291            .cylinder(Aabb {
292                min: (center - 3).with_z(base - 2),
293                max: (center + 1).with_z(base - 1),
294            })
295            .fill(white);
296        painter
297            .aabb(Aabb {
298                min: (center - 2).with_z(base - 2),
299                max: (center).with_z(base - 1),
300            })
301            .clear();
302        painter
303            .aabb(Aabb {
304                min: (center - 2).with_z(base - 3),
305                max: (center).with_z(base - 2),
306            })
307            .fill(Fill::Block(Block::air(SpriteKind::Ember)));
308
309        let mut stations = vec![
310            SpriteKind::CraftingBench,
311            SpriteKind::Forge,
312            SpriteKind::SpinningWheel,
313            SpriteKind::TanningRack,
314            SpriteKind::CookingPot,
315            SpriteKind::Cauldron,
316            SpriteKind::Loom,
317            SpriteKind::Anvil,
318            SpriteKind::DismantlingBench,
319            SpriteKind::RepairBench,
320        ];
321        'outer: for d in 0..3 {
322            for dir in CARDINALS {
323                if stations.is_empty() {
324                    break 'outer;
325                }
326                let position = center + dir * (4 + d * 2);
327                let cr_station = stations.swap_remove(
328                    RandomField::new(0).get(position.with_z(base)) as usize % stations.len(),
329                );
330                painter.sprite(position.with_z(base - 2), cr_station);
331            }
332        }
333
334        painter.spawn(
335            EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5))
336                .into_special(SpecialEntity::Waypoint),
337        );
338    }
339}