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(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 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 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 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 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 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 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 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 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 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 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 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 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}