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