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