1use super::*;
2use crate::{
3 Land,
4 site2::gen::{place_circular_as_vec, spiral_staircase},
5 util::{CARDINALS, DIAGONALS, RandomField, Sampler},
6};
7use common::{
8 generation::SpecialEntity,
9 terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::{f32::consts::TAU, sync::Arc};
13use vek::*;
14
15pub struct SavannahAirshipDock {
17 pub door_tile: Vec2<i32>,
19 pub(crate) alt: i32,
21 pub center: Vec2<i32>,
22 length: i32,
23 platform_height: i32,
24 pub docking_positions: Vec<Vec3<i32>>,
25}
26
27impl SavannahAirshipDock {
28 pub fn generate(
29 land: &Land,
30 _rng: &mut impl Rng,
31 site: &Site,
32 door_tile: Vec2<i32>,
33 door_dir: Vec2<i32>,
34 tile_aabr: Aabr<i32>,
35 alt: Option<i32>,
36 ) -> Self {
37 let door_tile_pos = site.tile_center_wpos(door_tile);
38 let bounds = Aabr {
39 min: site.tile_wpos(tile_aabr.min),
40 max: site.tile_wpos(tile_aabr.max),
41 };
42 let center = bounds.center();
43 let alt = alt.unwrap_or_else(|| {
44 land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
45 }) + 2;
46 let base = alt + 1;
47 let length = 18;
48 let platform_height = 40;
49 let mut docking_positions = vec![];
50 let top_floor = base + platform_height - 2;
51 for dir in CARDINALS {
52 let docking_pos = center + dir * (length + 5);
53 docking_positions.push(docking_pos.with_z(top_floor));
54 }
55
56 Self {
57 door_tile: door_tile_pos,
58 alt,
59 center,
60 length,
61 platform_height,
62 docking_positions,
63 }
64 }
65
66 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
67 SpawnRules {
68 trees: {
69 const AIRSHIP_MIN_TREE_DIST2: i32 = 59i32.pow(2);
74 wpos.distance_squared(self.center) > AIRSHIP_MIN_TREE_DIST2
75 },
76 waypoints: false,
77 ..SpawnRules::default()
78 }
79 }
80}
81
82impl Structure for SavannahAirshipDock {
83 #[cfg(feature = "use-dyn-lib")]
84 const UPDATE_FN: &'static [u8] = b"render_savannah_airship_dock\0";
85
86 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_savannah_airship_dock")]
87 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
88 let base = self.alt + 1;
89 let center = self.center;
90 let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
91 let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
92 let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
93 let color = Fill::Sampling(Arc::new(|center| {
94 Some(match (RandomField::new(0).get(center)) % 7 {
95 0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
96 1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
97 2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
98 3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
99 4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
100 5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
101 _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
102 })
103 }));
104 let length = self.length;
105 let height = length / 2;
106 let platform_height = self.platform_height;
107 let storeys = 1;
108 let radius = length + (length / 3);
109 let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
110 let reed_parts = 36_f32 + reed_var;
111 let phi = TAU / reed_parts;
112
113 painter
115 .cylinder(Aabb {
116 min: (center - length).with_z(base - 3),
117 max: (center + length + 1).with_z(base - 2),
118 })
119 .fill(clay.clone());
120 painter
121 .cylinder(Aabb {
122 min: (center - length - 1).with_z(base - 4),
123 max: (center + length + 2).with_z(base - 3),
124 })
125 .fill(clay.clone());
126 painter
127 .cylinder(Aabb {
128 min: (center - length - 2).with_z(base - 5),
129 max: (center + length + 3).with_z(base - 4),
130 })
131 .fill(clay.clone());
132 painter
133 .cylinder(Aabb {
134 min: (center - length - 3).with_z(base - height),
135 max: (center + length + 4).with_z(base - 5),
136 })
137 .fill(clay.clone());
138 painter
140 .cylinder(Aabb {
141 min: (center - (2 * (length / 3)) - 1).with_z(base + platform_height - 4),
142 max: (center + (2 * (length / 3)) + 1).with_z(base + platform_height - 3),
143 })
144 .fill(color.clone());
145 painter
146 .cylinder(Aabb {
147 min: (center - (2 * (length / 3))).with_z(base + platform_height - 4),
148 max: (center + (2 * (length / 3))).with_z(base + platform_height - 3),
149 })
150 .fill(clay.clone());
151 painter
152 .cylinder(Aabb {
153 min: (center - length - 2).with_z(base + platform_height - 3),
154 max: (center + length + 2).with_z(base + platform_height - 2),
155 })
156 .fill(color.clone());
157 painter
158 .cylinder(Aabb {
159 min: (center - length - 1).with_z(base + platform_height - 3),
160 max: (center + length + 1).with_z(base + platform_height - 2),
161 })
162 .fill(clay.clone());
163 for dir in CARDINALS {
165 let dock_pos = center + dir * (length + 2);
166 painter
167 .cylinder(Aabb {
168 min: (dock_pos - 5).with_z(base + platform_height - 3),
169 max: (dock_pos + 5).with_z(base + platform_height - 2),
170 })
171 .fill(color.clone());
172 painter
173 .cylinder(Aabb {
174 min: (dock_pos - 4).with_z(base + platform_height - 3),
175 max: (dock_pos + 4).with_z(base + platform_height - 2),
176 })
177 .fill(wood_dark.clone());
178 }
179
180 for dir in CARDINALS {
182 let lantern_pos = center + (dir * length);
183
184 painter.sprite(
185 lantern_pos.with_z(base + platform_height - 2),
186 SpriteKind::Lantern,
187 );
188 }
189 for dir in DIAGONALS {
190 let cargo_pos = center + (dir * ((length / 2) - 1));
191 for dir in CARDINALS {
192 let sprite_pos = cargo_pos + dir;
193 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
194 for r in 0..rows {
195 painter
196 .aabb(Aabb {
197 min: (sprite_pos).with_z(base + platform_height - 2 + r),
198 max: (sprite_pos + 1).with_z(base + platform_height - 1 + r),
199 })
200 .fill(Fill::Block(Block::air(
201 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32
202 {
203 0 => SpriteKind::Barrel,
204 _ => SpriteKind::CrateBlock,
205 },
206 )));
207 if r > 0 {
208 painter.owned_resource_sprite(
209 sprite_pos.with_z(base + platform_height - 1 + r),
210 SpriteKind::Crate,
211 0,
212 );
213 }
214 }
215 }
216 }
217 let campfire_pos = (center - (2 * (length / 3)) - 1).with_z(base + platform_height);
219 painter.spawn(
220 EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
221 .into_special(SpecialEntity::Waypoint),
222 );
223 for b in 0..2 {
224 let base = base + (b * platform_height);
225 let radius = radius - (b * (radius / 3));
226 let length = length - (b * (length / 3));
227 painter
229 .cone(Aabb {
230 min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
231 max: (center + radius)
232 .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
233 })
234 .fill(reed.clone());
235 painter
236 .cone(Aabb {
237 min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
238 max: (center + radius)
239 .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
240 })
241 .clear();
242 for s in 0..storeys {
244 let room = painter.cylinder(Aabb {
245 min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
246 max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
247 });
248 room.fill(clay.clone());
249 for dir in DIAGONALS {
251 let decor_pos = center + dir * (length - 2 - s);
252 let decor = painter
253 .line(
254 center.with_z(base - 1 + (s * (height + 2))),
255 decor_pos.with_z(base - 1 + (s * (height + 2))),
256 5.0,
257 )
258 .intersect(room);
259 decor.fill(color.clone());
260 painter
261 .line(
262 center.with_z(base - 1 + (s * (height + 2))),
263 decor_pos.with_z(base - 1 + (s * (height + 2))),
264 4.0,
265 )
266 .intersect(decor)
267 .fill(clay.clone());
268 }
269 }
270 painter
272 .cylinder(Aabb {
273 min: (center - length + 4).with_z(base - 2),
274 max: (center + 1 + length - 4).with_z(base + (storeys * height)),
275 })
276 .clear();
277 painter
279 .cylinder(Aabb {
280 min: (center - length + 4).with_z(base - 1),
281 max: (center + 1 + length - 4).with_z(base),
282 })
283 .fill(wood_dark.clone());
284 painter
285 .cylinder(Aabb {
286 min: (center - length + 4).with_z(base + (storeys * height) - 1),
287 max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
288 })
289 .fill(wood_dark.clone());
290
291 for s in 0..storeys {
292 for dir in CARDINALS {
294 let frame_pos = center + dir * (length - 2 - s);
295 let clear_pos = center + dir * (length + 2 - s);
296
297 painter
298 .line(
299 center.with_z(base - 1 + (s * (height + 2))),
300 frame_pos.with_z(base - 1 + (s * (height + 2))),
301 3.0,
302 )
303 .fill(color.clone());
304 painter
305 .line(
306 center.with_z(base - 1 + (s * (height + 2))),
307 clear_pos.with_z(base - 1 + (s * (height + 2))),
308 2.0,
309 )
310 .clear();
311 }
312 }
313 painter
315 .cylinder(Aabb {
316 min: (center - length + 5).with_z(base - 2),
317 max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
318 })
319 .clear();
320 painter
322 .cylinder(Aabb {
323 min: (center - (length / 2) - 1).with_z(base - 3),
324 max: (center + (length / 2) + 1).with_z(base - 2),
325 })
326 .fill(color.clone());
327 painter
328 .cylinder(Aabb {
329 min: (center - (length / 2) + 1).with_z(base - 3),
330 max: (center + (length / 2) - 1).with_z(base - 2),
331 })
332 .fill(clay.clone());
333
334 for n in 1..=reed_parts as i32 {
336 let pos = Vec2::new(
337 center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
338 center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
339 );
340 painter
341 .line(
342 pos.with_z(base + (storeys * height) - (height / 2)),
343 center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
344 1.0,
345 )
346 .fill(reed.clone());
347 }
348 }
349
350 let beams_low = place_circular_as_vec(center, (2 * (length / 3)) as f32, 10);
352 let beams_high = place_circular_as_vec(center, (2 * (length / 4)) as f32, 10);
353
354 for b in 0..beams_low.len() {
355 painter
356 .cylinder(Aabb {
357 min: (beams_low[b] - 4).with_z(base + height - 1),
358 max: (beams_low[b] + 4).with_z(base + height),
359 })
360 .fill(wood_dark.clone());
361
362 painter
363 .line(
364 beams_low[b].with_z(base + height),
365 beams_high[b].with_z(base + platform_height - 4),
366 1.5,
367 )
368 .fill(wood_dark.clone());
369 }
370 painter
372 .cylinder(Aabb {
373 min: (center - (length / 3)).with_z(base),
374 max: (center + (length / 3)).with_z(base + platform_height),
375 })
376 .clear();
377
378 let stairs = painter.cylinder(Aabb {
379 min: (center - (length / 3)).with_z(base - 3),
380 max: (center + (length / 3)).with_z(base + platform_height - 2),
381 });
382
383 stairs
384 .sample(spiral_staircase(
385 center.with_z(base - 3),
386 ((length / 3) + 1) as f32,
387 0.5,
388 (platform_height / 4) as f32,
389 ))
390 .fill(clay.clone());
391 }
392}