Skip to main content

veloren_world/site/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 = "dyn-lib")]
51    #[unsafe(export_name = "as_dyn_structure_coastalworkshop")]
52    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
53        Some((Self::as_dyn_impl(self), "as_dyn_structure_coastalworkshop"))
54    }
55
56    fn spawn_rules_inner(
57        &self,
58        spawn_rules: &mut SpawnRules,
59        _land: &Land,
60        _wpos: Vec2<i32>,
61        weight: f32,
62    ) {
63        spawn_rules.prefer_alt(self.alt as f32, weight + weight);
64    }
65
66    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
67        let base = self.alt + 1;
68        let center = self.bounds.center();
69        let white = Fill::Sampling(Arc::new(|center| {
70            Some(match (RandomField::new(0).get(center)) % 37 {
71                0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
72                9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
73                18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
74                27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
75                _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
76            })
77        }));
78        let blue_broken = Fill::Sampling(Arc::new(|center| {
79            Some(match (RandomField::new(0).get(center)) % 20 {
80                0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
81                _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
82            })
83        }));
84        let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
85        let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
86        let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
87
88        // fence, blue gates
89        painter
90            .aabb(Aabb {
91                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
92                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
93            })
94            .fill(blue_broken.clone());
95
96        for dir in CARDINALS {
97            let frame_pos = Vec2::new(
98                center.x + dir.x * (length + 5),
99                center.y + dir.y * (width + 5),
100            );
101            painter
102                .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
103                .fill(blue_broken.clone());
104        }
105        // foundation
106        painter
107            .aabb(Aabb {
108                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
109                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
110            })
111            .fill(white.clone());
112        for f in 0..8 {
113            painter
114                .aabb(Aabb {
115                    min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
116                        .with_z(base - 3 - f),
117                    max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
118                        .with_z(base - 2 - f),
119                })
120                .fill(white.clone());
121        }
122        // clear yard
123        painter
124            .aabb(Aabb {
125                min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
126                max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
127            })
128            .clear();
129        // clear entries
130        for dir in CARDINALS {
131            let clear_pos = Vec2::new(
132                center.x + dir.x * (length + 7),
133                center.y + dir.y * (width + 7),
134            );
135            painter
136                .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
137                .clear();
138        }
139        // roof terrace
140        painter
141            .aabb(Aabb {
142                min: Vec2::new(center.x - length - 3, center.y - width - 3)
143                    .with_z(base - 3 + height),
144                max: Vec2::new(center.x + length + 2, center.y + width + 2)
145                    .with_z(base - 2 + height),
146            })
147            .fill(white.clone());
148        painter
149            .aabb(Aabb {
150                min: Vec2::new(center.x - length - 3, center.y - width - 3)
151                    .with_z(base - 2 + height),
152                max: Vec2::new(center.x + length + 2, center.y + width + 2)
153                    .with_z(base - 1 + height),
154            })
155            .fill(blue_broken.clone());
156        painter
157            .aabb(Aabb {
158                min: Vec2::new(center.x - length - 2, center.y - width - 2)
159                    .with_z(base - 2 + height),
160                max: Vec2::new(center.x + length + 1, center.y + width + 1)
161                    .with_z(base - 1 + height),
162            })
163            .clear();
164        // room
165        painter
166            .aabb(Aabb {
167                min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
168                max: Vec2::new(center.x + length, center.y + width).with_z(base - 1),
169            })
170            .fill(blue_broken.clone());
171        painter
172            .aabb(Aabb {
173                min: Vec2::new(center.x - length + 1, center.y - width + 1).with_z(base - 2),
174                max: Vec2::new(center.x + length - 1, center.y + width - 1)
175                    .with_z(base - 1 + height - 1),
176            })
177            .fill(white.clone());
178
179        // entries
180        let entry_limit = painter.aabb(Aabb {
181            min: Vec2::new(center.x - length, center.y - width).with_z(base - 2),
182            max: Vec2::new(center.x + length, center.y + width).with_z(base - 1 + height - 1),
183        });
184        painter
185            .line(
186                Vec2::new(center.x, center.y + 1 - width).with_z(base - 1),
187                Vec2::new(center.x, center.y - 2 + width).with_z(base - 1),
188                8.0,
189            )
190            .intersect(entry_limit)
191            .fill(blue_broken.clone());
192        painter
193            .line(
194                Vec2::new(center.x, center.y - width).with_z(base - 1),
195                Vec2::new(center.x, center.y + width).with_z(base - 1),
196                7.0,
197            )
198            .intersect(entry_limit)
199            .clear();
200        painter
201            .line(
202                Vec2::new(center.x + 1 - length, center.y).with_z(base - 1),
203                Vec2::new(center.x - 2 + length, center.y).with_z(base - 1),
204                8.0,
205            )
206            .intersect(entry_limit)
207            .fill(blue_broken.clone());
208        painter
209            .line(
210                Vec2::new(center.x - length, center.y).with_z(base - 1),
211                Vec2::new(center.x + length, center.y).with_z(base - 1),
212                7.0,
213            )
214            .intersect(entry_limit)
215            .clear();
216        // clear room
217        painter
218            .aabb(Aabb {
219                min: Vec2::new(center.x - length + 2, center.y - width + 2).with_z(base - 2),
220                max: Vec2::new(center.x + length - 2, center.y + width - 2)
221                    .with_z(base - 2 + height - 1),
222            })
223            .clear();
224
225        // room floors
226        painter
227            .aabb(Aabb {
228                min: Vec2::new(center.x - length + 5, center.y - width + 5).with_z(base - 3),
229                max: Vec2::new(center.x + length - 5, center.y + width - 5).with_z(base - 2),
230            })
231            .fill(blue_broken.clone());
232        painter
233            .aabb(Aabb {
234                min: Vec2::new(center.x - length + 6, center.y - width + 6).with_z(base - 3),
235                max: Vec2::new(center.x + length - 6, center.y + width - 6).with_z(base - 2),
236            })
237            .fill(white.clone());
238
239        // wall lamps
240        for d in 0..2 {
241            let door_lamp_pos =
242                Vec2::new(center.x - length + 2 + (d * ((2 * (length)) - 5)), center.y)
243                    .with_z(base + 6);
244            painter.rotated_sprite(
245                door_lamp_pos,
246                SpriteKind::WallLampSmall,
247                2 + ((d * 4) as u8),
248            );
249
250            let lamp_pos = Vec2::new(center.x, center.y - width + 2 + (d * ((2 * (width)) - 5)))
251                .with_z(base + 6);
252            painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
253        }
254        for d in 0..2 {
255            let door_lamp_pos =
256                Vec2::new(center.x - length - 1 + (d * ((2 * (length)) + 1)), center.y)
257                    .with_z(base + 6);
258            painter.rotated_sprite(
259                door_lamp_pos,
260                SpriteKind::WallLampSmall,
261                (6 + ((d * 4) as u8)) % 8,
262            );
263
264            let lamp_pos = Vec2::new(center.x, center.y - width - 1 + (d * ((2 * (width)) + 1)))
265                .with_z(base + 6);
266            painter.rotated_sprite(
267                lamp_pos,
268                SpriteKind::WallLampSmall,
269                (8 - ((d * 4) as u8)) % 8,
270            );
271        }
272
273        // chimney
274        painter
275            .cylinder(Aabb {
276                min: (center - 4).with_z(base + height - 4),
277                max: (center + 2).with_z(base - 2 + height + (height / 2)),
278            })
279            .fill(blue_broken);
280
281        let top_limit = painter.aabb(Aabb {
282            min: Vec2::new(center.x - length, center.y - width).with_z(base + height - 2),
283            max: Vec2::new(center.x + length, center.y + width)
284                .with_z(base - 2 + height + (height / 2)),
285        });
286        painter
287            .superquadric(
288                Aabb {
289                    min: Vec2::new(center.x - length - 1, center.y - width - 1)
290                        .with_z(base + height - (height / 2)),
291                    max: Vec2::new(center.x + length, center.y + width)
292                        .with_z(base - 2 + height + (height / 2)),
293                },
294                1.5,
295            )
296            .intersect(top_limit)
297            .fill(white.clone());
298        // clear chimney
299        painter
300            .cylinder(Aabb {
301                min: (center - 3).with_z(base + height - 4),
302                max: (center + 1).with_z(base - 2 + height + (height / 2)),
303            })
304            .clear();
305
306        painter
307            .cylinder(Aabb {
308                min: (center - 3).with_z(base - 2),
309                max: (center + 1).with_z(base - 1),
310            })
311            .fill(white);
312        painter
313            .aabb(Aabb {
314                min: (center - 2).with_z(base - 2),
315                max: (center).with_z(base - 1),
316            })
317            .clear();
318        painter
319            .aabb(Aabb {
320                min: (center - 2).with_z(base - 3),
321                max: (center).with_z(base - 2),
322            })
323            .fill(Fill::Block(Block::air(SpriteKind::Ember)));
324
325        let mut stations = vec![
326            SpriteKind::CraftingBench,
327            SpriteKind::Forge,
328            SpriteKind::SpinningWheel,
329            SpriteKind::TanningRack,
330            SpriteKind::CookingPot,
331            SpriteKind::Cauldron,
332            SpriteKind::Loom,
333            SpriteKind::Anvil,
334            SpriteKind::DismantlingBench,
335            SpriteKind::RepairBench,
336        ];
337        'outer: for d in 0..3 {
338            for dir in CARDINALS {
339                if stations.is_empty() {
340                    break 'outer;
341                }
342                let position = center + dir * (4 + d * 2);
343                let cr_station = stations.swap_remove(
344                    RandomField::new(0).get(position.with_z(base)) as usize % stations.len(),
345                );
346                painter.sprite(position.with_z(base - 2), cr_station);
347            }
348        }
349
350        painter.spawn(
351            EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5))
352                .into_special(SpecialEntity::Waypoint),
353        );
354    }
355}