veloren_world/site2/plot/
savannah_workshop.rs1use 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
14pub struct SavannahWorkshop {
16 pub door_tile: Vec2<i32>,
18 bounds: Aabr<i32>,
20 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 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 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 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 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 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 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 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 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 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 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 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 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}