1use super::*;
2use crate::{
3 Land,
4 util::{DIAGONALS, RandomField, Sampler, within_distance},
5};
6use common::{
7 generation::SpecialEntity,
8 terrain::{BlockKind, SpriteKind},
9};
10
11use rand::prelude::*;
12use std::sync::Arc;
13use vek::*;
14
15pub struct DesertCityAirshipDock {
17 pub door_tile: Vec2<i32>,
19 bounds: Aabr<i32>,
21 pub(crate) alt: i32,
23 pub docking_positions: Vec<Vec3<i32>>,
24 pub center: Vec2<i32>,
25 base: i32,
26 length: i32,
27 height: i32,
28 floors: i32,
29}
30
31impl DesertCityAirshipDock {
32 pub fn generate(
33 land: &Land,
34 _rng: &mut impl Rng,
35 site: &Site,
36 door_tile: Vec2<i32>,
37 door_dir: Vec2<i32>,
38 tile_aabr: Aabr<i32>,
39 alt: Option<i32>,
40 ) -> Self {
41 let door_tile_pos = site.tile_center_wpos(door_tile);
42 let bounds = Aabr {
43 min: site.tile_wpos(tile_aabr.min),
44 max: site.tile_wpos(tile_aabr.max),
45 };
46 let alt = alt.unwrap_or_else(|| {
47 land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
48 });
49 let center = bounds.center();
50
51 let mut max_surface_alt = i32::MIN;
60 for dx in -3..=3 {
62 for dy in -3..=3 {
63 let pos = center + Vec2::new(dx * 24, dy * 24);
64 let alt = land.get_surface_alt_approx(pos) as i32;
65 if alt > max_surface_alt {
66 max_surface_alt = alt;
67 }
68 }
69 }
70
71 let foundation_qtr = bounds.size().w / 4;
75 let mut max_foundation_alt = i32::MIN;
76 for dx in -2..=2 {
77 for dy in -2..=2 {
78 let pos = center + Vec2::new(dx * foundation_qtr, dy * foundation_qtr);
79 let alt = land.get_surface_alt_approx(pos) as i32;
80 if alt > max_foundation_alt {
81 max_foundation_alt = alt;
82 }
83 }
84 }
85
86 let length = 14;
87 let height = 2 * (length / 3);
88 let floors = 4;
89
90 let mut base = max_foundation_alt + 1;
92 let mut top_floor = base + 5 + (height * (floors + 1));
93 let clearance = top_floor - (max_surface_alt + 6);
95 if clearance < 0 {
96 base += -clearance;
97 top_floor += -clearance;
98 }
99
100 let docking_positions = CARDINALS
101 .iter()
102 .map(|dir| (center + dir * 31).with_z(top_floor))
103 .collect::<Vec<_>>();
104 Self {
105 bounds,
106 door_tile: door_tile_pos,
107 alt,
108 docking_positions,
109 center,
110 base,
111 length,
112 height,
113 floors,
114 }
115 }
116
117 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
118 SpawnRules {
119 trees: {
120 const AIRSHIP_MIN_TREE_DIST: i32 = 89;
125 !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST)
126 },
127 waypoints: false,
128 ..SpawnRules::default()
129 }
130 }
131}
132
133impl Structure for DesertCityAirshipDock {
134 #[cfg(feature = "use-dyn-lib")]
135 const UPDATE_FN: &'static [u8] = b"render_desertcityairshipdock\0";
136
137 #[cfg_attr(
138 feature = "be-dyn-lib",
139 unsafe(export_name = "render_desertcityairshipdock")
140 )]
141 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
142 let sandstone = Fill::Sampling(Arc::new(|center| {
143 Some(match (RandomField::new(0).get(center)) % 37 {
144 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
145 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
146 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
147 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
148 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
149 })
150 }));
151 let sandstone_broken = Fill::Sampling(Arc::new(|center| {
152 Some(match (RandomField::new(0).get(center)) % 42 {
153 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
154 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
155 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
156 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
157 36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
158 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
159 })
160 }));
161 let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
162 let base = self.base;
163 let center = self.center;
164 painter
166 .aabb(Aabb {
167 min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
168 max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
169 })
170 .union(painter.aabb(Aabb {
171 min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
172 max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
173 }))
174 .union(painter.aabb(Aabb {
175 min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
176 max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
177 }))
178 .union(painter.aabb(Aabb {
179 min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
180 max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
181 }))
182 .fill(sandstone_broken);
183 painter
184 .aabb(Aabb {
185 min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
186 max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
187 })
188 .clear();
189 painter
190 .aabb(Aabb {
191 min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
192 max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
193 })
194 .clear();
195 painter
197 .aabb(Aabb {
198 min: (self.bounds.min + 1).with_z(base - 20),
199 max: (self.bounds.max).with_z(base),
200 })
201 .fill(sandstone.clone());
202
203 let length = self.length;
205 let height = self.height;
206 let floors = self.floors;
207 let carve = length / 4;
208
209 for f in 0..=floors {
210 let bldg_base = base + f * (height + 1);
211 let bldg_length = length;
212 if f == floors {
213 painter
215 .aabb(Aabb {
216 min: Vec2::new(center.x - 7, center.y - 5).with_z(bldg_base + height),
217 max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 6),
218 })
219 .fill(sandstone.clone());
220 painter
221 .aabb(Aabb {
222 min: Vec2::new(center.x - 8, center.y - 4).with_z(bldg_base + height),
223 max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 5),
224 })
225 .clear();
226 painter
227 .aabb(Aabb {
228 min: Vec2::new(center.x - 11, center.y - 5).with_z(bldg_base + height),
229 max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 5),
230 })
231 .clear();
232 painter
233 .aabb(Aabb {
234 min: Vec2::new(center.x - 10, center.y - 4).with_z(bldg_base + height),
235 max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 2),
236 })
237 .fill(wood.clone());
238 }
239 painter
241 .aabb(Aabb {
242 min: (center - bldg_length).with_z(bldg_base),
243 max: (center + bldg_length).with_z(bldg_base + height),
244 })
245 .fill(sandstone.clone());
246 painter
248 .aabb(Aabb {
249 min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
250 max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
251 })
252 .fill(wood.clone());
253 painter
254 .aabb(Aabb {
255 min: (center - bldg_length - f).with_z(bldg_base + height),
256 max: (center + bldg_length + f).with_z(bldg_base + height + 1),
257 })
258 .fill(sandstone.clone());
259 painter
261 .aabb(Aabb {
262 min: (center - bldg_length + 1).with_z(bldg_base),
263 max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
264 })
265 .clear();
266
267 let clear_limit_1 = painter.aabb(Aabb {
268 min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
269 .with_z(bldg_base),
270 max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
271 .with_z(bldg_base + height),
272 });
273 let clear_limit_2 = painter.aabb(Aabb {
274 min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
275 .with_z(bldg_base),
276 max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
277 .with_z(bldg_base + height),
278 });
279 for c in 0..=4 {
280 let space = c * ((2 * carve) + 1);
281 painter
282 .vault(
283 Aabb {
284 min: Vec2::new(
285 center.x - bldg_length,
286 center.y + space - bldg_length - carve + 1,
287 )
288 .with_z(bldg_base + (height / 2)),
289 max: Vec2::new(
290 center.x + bldg_length,
291 center.y + space - bldg_length + carve - 1,
292 )
293 .with_z(bldg_base + height - 1),
294 },
295 Dir::X,
296 )
297 .intersect(clear_limit_1)
298 .clear();
299 painter
300 .vault(
301 Aabb {
302 min: Vec2::new(
303 center.x + space - bldg_length - carve + 1,
304 center.y - bldg_length,
305 )
306 .with_z(bldg_base + (height / 2)),
307 max: Vec2::new(
308 center.x + space - bldg_length + carve - 1,
309 center.y + bldg_length,
310 )
311 .with_z(bldg_base + height - 1),
312 },
313 Dir::Y,
314 )
315 .intersect(clear_limit_2)
316 .clear();
317
318 painter
319 .aabb(Aabb {
320 min: Vec2::new(
321 center.x - bldg_length,
322 center.y + space - bldg_length - carve,
323 )
324 .with_z(bldg_base),
325 max: Vec2::new(
326 center.x + bldg_length,
327 center.y + space - bldg_length + carve,
328 )
329 .with_z(bldg_base + (height / 2)),
330 })
331 .intersect(clear_limit_1)
332 .clear();
333 painter
334 .aabb(Aabb {
335 min: Vec2::new(
336 center.x + space - bldg_length - carve,
337 center.y - bldg_length,
338 )
339 .with_z(bldg_base),
340 max: Vec2::new(
341 center.x + space - bldg_length + carve,
342 center.y + bldg_length,
343 )
344 .with_z(bldg_base + (height / 2)),
345 })
346 .intersect(clear_limit_2)
347 .clear();
348 }
349 for dir in DIAGONALS {
350 let clear_pos = center + dir * bldg_length;
351 painter
352 .aabb(Aabb {
353 min: (clear_pos - 1).with_z(bldg_base),
354 max: (clear_pos + 1).with_z(bldg_base + height - 1),
355 })
356 .clear();
357 }
358 for dir in DIAGONALS {
359 let lamp_pos = center + dir * (bldg_length - 1);
360 painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
361 if f == 4 {
362 let lamp_pos = center + dir * bldg_length;
363 painter.sprite(
364 lamp_pos.with_z(bldg_base + height + 1),
365 SpriteKind::StreetLamp,
366 );
367
368 let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
369 for dir in CARDINALS {
370 let sprite_pos = cargo_pos + dir;
371 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
372 for r in 0..rows {
373 painter
374 .aabb(Aabb {
375 min: (sprite_pos).with_z(bldg_base + height + 1 + r),
376 max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
377 })
378 .fill(Fill::Block(Block::air(
379 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
380 as i32
381 {
382 0 => SpriteKind::Barrel,
383 _ => SpriteKind::CrateBlock,
384 },
385 )));
386 if r > 0 {
387 painter.owned_resource_sprite(
388 sprite_pos.with_z(bldg_base + height + 2 + r),
389 SpriteKind::Crate,
390 0,
391 );
392 }
393 }
394 }
395 }
396 }
397 if f == 4 {
399 for dir in CARDINALS {
400 let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
401 let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
402 let dock_pos = center + dir * 27;
403 painter
404 .aabb(Aabb {
405 min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
406 max: (gangway_pos_2 + 3).with_z(bldg_base + height),
407 })
408 .fill(wood.clone());
409 painter
410 .aabb(Aabb {
411 min: (gangway_pos_1 - 3).with_z(bldg_base + height),
412 max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
413 })
414 .fill(wood.clone());
415 painter
416 .cylinder(Aabb {
417 min: (dock_pos - 4).with_z(bldg_base + height),
418 max: (dock_pos + 4).with_z(bldg_base + height + 1),
419 })
420 .fill(wood.clone());
421 painter
422 .cylinder(Aabb {
423 min: (dock_pos - 3).with_z(bldg_base + height - 1),
424 max: (dock_pos + 3).with_z(bldg_base + height),
425 })
426 .fill(wood.clone());
427 }
428 painter.spawn(
430 EntityInfo::at(
431 Vec2::new(center.x + bldg_length - 2, center.y)
432 .with_z(bldg_base + height + 1)
433 .map(|e| e as f32 + 0.5),
434 )
435 .into_special(SpecialEntity::Waypoint),
436 );
437 }
438 painter
440 .aabb(Aabb {
441 min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
442 max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
443 })
444 .fill(wood.clone());
445 painter
446 .aabb(Aabb {
447 min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
448 max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
449 })
450 .clear();
451
452 for w in 0..((bldg_length / 2) + 2) {
453 painter
454 .aabb(Aabb {
455 min: Vec2::new(
456 center.x - (bldg_length / 2) - 2 + (2 * w),
457 center.y - (bldg_length / 2),
458 )
459 .with_z(bldg_base + w),
460 max: Vec2::new(
461 center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
462 center.y + (bldg_length / 2),
463 )
464 .with_z(bldg_base + 1 + w),
465 })
466 .fill(wood.clone());
467 painter
468 .aabb(Aabb {
469 min: Vec2::new(
470 center.x - (bldg_length / 2) - 2 + (2 * w),
471 center.y - (bldg_length / 2),
472 )
473 .with_z(bldg_base - 1 + w),
474 max: Vec2::new(
475 center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
476 center.y + (bldg_length / 2),
477 )
478 .with_z(bldg_base + w),
479 })
480 .fill(sandstone.clone());
481 }
482 }
483 }
484}