Skip to main content

veloren_world/site/plot/
savannah_workshop.rs

1use super::*;
2use crate::{
3    Land,
4    util::{CARDINALS, DIAGONALS, RandomField, Sampler},
5};
6use common::{
7    generation::SpecialEntity,
8    terrain::{BlockKind, SpriteKind, sprite::Owned},
9};
10use rand::prelude::*;
11use std::{f32::consts::TAU, sync::Arc};
12use vek::*;
13
14/// Represents house data generated by the `generate()` method
15pub struct SavannahWorkshop {
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 SavannahWorkshop {
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            bounds,
41            door_tile: door_tile_pos,
42            alt: alt.unwrap_or_else(|| {
43                land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2
44            }),
45        }
46    }
47}
48
49impl Structure for SavannahWorkshop {
50    #[cfg(feature = "dyn-lib")]
51    #[unsafe(export_name = "as_dyn_structure_savannahworkshop")]
52    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
53        Some((Self::as_dyn_impl(self), "as_dyn_structure_savannahworkshop"))
54    }
55
56    fn door_tile(&self) -> Option<Vec2<i32>> { Some(self.door_tile) }
57
58    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
59        let base = self.alt + 1;
60        let center = self.bounds.center();
61        let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
62            Some(match (RandomField::new(0).get(wpos)) % 25 {
63                0 => Block::air(SpriteKind::Bowl).with_attr(Owned(true)).unwrap(),
64                1 => Block::air(SpriteKind::VialEmpty)
65                    .with_attr(Owned(true))
66                    .unwrap(),
67                2 => Block::air(SpriteKind::Lantern),
68                3 => Block::air(SpriteKind::JugArabic),
69                4 => Block::air(SpriteKind::Crate)
70                    .with_attr(Owned(true))
71                    .unwrap(),
72                _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
73            })
74        }));
75        let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
76        let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
77        let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
78        let color = Fill::Sampling(Arc::new(|center| {
79            Some(match (RandomField::new(0).get(center)) % 7 {
80                0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
81                1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
82                2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
83                3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
84                4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
85                5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
86                _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
87            })
88        }));
89        let length = (10 + RandomField::new(0).get(center.with_z(base)) % 6) as i32;
90        let height = 2 * length / 3;
91        let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
92        let radius = length + (length / 3);
93        let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
94        let reed_parts = 36_f32 + reed_var;
95        let phi = TAU / reed_parts;
96        // roof cone
97        painter
98            .cone(Aabb {
99                min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
100                max: (center + radius)
101                    .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
102            })
103            .fill(reed.clone());
104        painter
105            .cone(Aabb {
106                min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
107                max: (center + radius)
108                    .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
109            })
110            .clear();
111        // foundation
112        painter
113            .cylinder(Aabb {
114                min: (center - length).with_z(base - 3),
115                max: (center + length + 1).with_z(base - 2),
116            })
117            .fill(clay.clone());
118        painter
119            .cylinder(Aabb {
120                min: (center - length - 1).with_z(base - 4),
121                max: (center + length + 2).with_z(base - 3),
122            })
123            .fill(clay.clone());
124        painter
125            .cylinder(Aabb {
126                min: (center - length - 2).with_z(base - 5),
127                max: (center + length + 3).with_z(base - 4),
128            })
129            .fill(clay.clone());
130        painter
131            .cylinder(Aabb {
132                min: (center - length - 3).with_z(base - height),
133                max: (center + length + 4).with_z(base - 5),
134            })
135            .fill(clay.clone());
136        // room
137        for s in 0..storeys {
138            let room = painter.cylinder(Aabb {
139                min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
140                max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
141            });
142            room.fill(clay.clone());
143            // decor inlays
144            for dir in DIAGONALS {
145                let decor_pos = center + dir * (length - 2 - s);
146                let decor = painter
147                    .line(
148                        center.with_z(base - 1 + (s * (height + 2))),
149                        decor_pos.with_z(base - 1 + (s * (height + 2))),
150                        5.0,
151                    )
152                    .intersect(room);
153                decor.fill(color.clone());
154                painter
155                    .line(
156                        center.with_z(base - 1 + (s * (height + 2))),
157                        decor_pos.with_z(base - 1 + (s * (height + 2))),
158                        4.0,
159                    )
160                    .intersect(decor)
161                    .fill(clay.clone());
162            }
163        }
164        // clear rooms
165        painter
166            .cylinder(Aabb {
167                min: (center - length + 4).with_z(base - 2),
168                max: (center + 1 + length - 4).with_z(base + (storeys * height)),
169            })
170            .clear();
171        // wood decor
172        painter
173            .cylinder(Aabb {
174                min: (center - length + 4).with_z(base - 1),
175                max: (center + 1 + length - 4).with_z(base),
176            })
177            .fill(wood_dark.clone());
178        painter
179            .cylinder(Aabb {
180                min: (center - length + 4).with_z(base),
181                max: (center + 1 + length - 4).with_z(base + 1),
182            })
183            .fill(sprite_fill);
184
185        painter
186            .cylinder(Aabb {
187                min: (center - length + 4).with_z(base + (storeys * height) - 1),
188                max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
189            })
190            .fill(wood_dark.clone());
191
192        for s in 0..storeys {
193            // entries, windows
194            for dir in CARDINALS {
195                let frame_pos = center + dir * (length - 2 - s);
196                let clear_pos = center + dir * (length + 2 - s);
197
198                painter
199                    .line(
200                        center.with_z(base - 1 + (s * (height + 2))),
201                        frame_pos.with_z(base - 1 + (s * (height + 2))),
202                        3.0,
203                    )
204                    .fill(color.clone());
205                painter
206                    .line(
207                        center.with_z(base - 1 + (s * (height + 2))),
208                        clear_pos.with_z(base - 1 + (s * (height + 2))),
209                        2.0,
210                    )
211                    .clear();
212            }
213        }
214        // re clear room
215        painter
216            .cylinder(Aabb {
217                min: (center - length + 5).with_z(base - 2),
218                max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
219            })
220            .clear();
221        // floor
222        painter
223            .cylinder(Aabb {
224                min: (center - (length / 2) - 1).with_z(base - 3),
225                max: (center + (length / 2) + 1).with_z(base - 2),
226            })
227            .fill(color);
228        painter
229            .cylinder(Aabb {
230                min: (center - (length / 2) + 1).with_z(base - 3),
231                max: (center + (length / 2) - 1).with_z(base - 2),
232            })
233            .fill(clay.clone());
234
235        // reed roof lines
236
237        for n in 1..=reed_parts as i32 {
238            let pos = Vec2::new(
239                center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
240                center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
241            );
242            painter
243                .line(
244                    pos.with_z(base + (storeys * height) - (height / 2)),
245                    center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
246                    1.0,
247                )
248                .fill(reed.clone());
249        }
250        // chimney
251        painter
252            .cylinder(Aabb {
253                min: (center - 3)
254                    .with_z(base - 1 + (storeys * height) + (height / 2) + reed_var as i32),
255                max: (center + 4)
256                    .with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
257            })
258            .fill(wood_dark);
259        // clear chimney
260        painter
261            .cylinder(Aabb {
262                min: (center - 2).with_z(base + (storeys * height) - 4),
263                max: (center + 3)
264                    .with_z(base + 5 * (storeys * height) + (height / 2) + reed_var as i32),
265            })
266            .clear();
267
268        painter
269            .cylinder(Aabb {
270                min: (center - 1).with_z(base - 2),
271                max: (center + 2).with_z(base - 1),
272            })
273            .fill(clay);
274        painter
275            .aabb(Aabb {
276                min: (center).with_z(base - 2),
277                max: (center + 1).with_z(base - 1),
278            })
279            .clear();
280        painter
281            .aabb(Aabb {
282                min: (center).with_z(base - 3),
283                max: (center + 1).with_z(base - 2),
284            })
285            .fill(Fill::Block(Block::air(SpriteKind::Ember)));
286
287        let mut stations = vec![
288            SpriteKind::CraftingBench,
289            SpriteKind::Forge,
290            SpriteKind::SpinningWheel,
291            SpriteKind::TanningRack,
292            SpriteKind::CookingPot,
293            SpriteKind::Cauldron,
294            SpriteKind::Loom,
295            SpriteKind::Anvil,
296            SpriteKind::DismantlingBench,
297            SpriteKind::RepairBench,
298        ];
299        let cr_pos = stations.len() as f32;
300        let phi = TAU / cr_pos;
301        'outer: for d in 0..2 {
302            let dist = 4 + d;
303            for n in 1..=cr_pos as i32 {
304                let pos = Vec2::new(
305                    center.x + ((dist as f32) * ((n as f32 * phi).cos())) as i32,
306                    center.y + ((dist as f32) * ((n as f32 * phi).sin())) as i32,
307                );
308                if stations.is_empty() {
309                    break 'outer;
310                }
311                let cr_station = stations.swap_remove(
312                    RandomField::new(0).get(pos.with_z(base)) as usize % stations.len(),
313                );
314                painter.sprite(pos.with_z(base - 2), cr_station);
315            }
316        }
317
318        painter.spawn(
319            EntityInfo::at((center).with_z(base - 2).map(|e| e as f32 + 0.5))
320                .into_special(SpecialEntity::Waypoint),
321        );
322    }
323}