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
118impl Structure for DesertCityAirshipDock {
119 #[cfg(feature = "dyn-lib")]
120 #[unsafe(export_name = "as_dyn_structure_desertcityairshipdock")]
121 fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
122 Some((
123 Self::as_dyn_impl(self),
124 "as_dyn_structure_desertcityairshipdock",
125 ))
126 }
127
128 fn spawn_rules_inner(
129 &self,
130 spawn_rules: &mut SpawnRules,
131 _land: &Land,
132 wpos: Vec2<i32>,
133 _weight: f32,
134 ) {
135 const AIRSHIP_MIN_TREE_DIST: i32 = 89;
140 spawn_rules.trees &= !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST);
141
142 const MIN_FLAT_DIST: f32 = 12.0;
143 const SLOPE_LENGTH: f32 = 16.0;
144 let dist = wpos.as_::<f32>().distance(self.center.as_());
145 let weight = (1.0 - (dist - MIN_FLAT_DIST).max(0.0) / SLOPE_LENGTH).max(0.0);
146 spawn_rules.prefer_alt(self.alt as f32, weight);
147 }
148
149 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
150 let sandstone = Fill::Sampling(Arc::new(|center| {
151 Some(match (RandomField::new(0).get(center)) % 37 {
152 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
153 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
154 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
155 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
156 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
157 })
158 }));
159 let sandstone_broken = Fill::Sampling(Arc::new(|center| {
160 Some(match (RandomField::new(0).get(center)) % 42 {
161 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
162 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
163 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
164 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
165 36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
166 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
167 })
168 }));
169 let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
170 let base = self.base;
171 let center = self.center;
172 painter
174 .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.min.x + 2, self.bounds.max.y).with_z(base + 2),
177 })
178 .union(painter.aabb(Aabb {
179 min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
180 max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
181 }))
182 .union(painter.aabb(Aabb {
183 min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
184 max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
185 }))
186 .union(painter.aabb(Aabb {
187 min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
188 max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
189 }))
190 .fill(sandstone_broken);
191 painter
192 .aabb(Aabb {
193 min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
194 max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
195 })
196 .clear();
197 painter
198 .aabb(Aabb {
199 min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
200 max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
201 })
202 .clear();
203 painter
205 .aabb(Aabb {
206 min: (self.bounds.min + 1).with_z(base - 20),
207 max: (self.bounds.max).with_z(base),
208 })
209 .fill(sandstone.clone());
210
211 let length = self.length;
213 let height = self.height;
214 let floors = self.floors;
215 let carve = length / 4;
216
217 for f in 0..=floors {
218 let bldg_base = base + f * (height + 1);
219 let bldg_length = length;
220 if f == floors {
221 painter
223 .aabb(Aabb {
224 min: Vec2::new(center.x - 7, center.y - 5).with_z(bldg_base + height),
225 max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 6),
226 })
227 .fill(sandstone.clone());
228 painter
229 .aabb(Aabb {
230 min: Vec2::new(center.x - 8, center.y - 4).with_z(bldg_base + height),
231 max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 5),
232 })
233 .clear();
234 painter
235 .aabb(Aabb {
236 min: Vec2::new(center.x - 11, center.y - 5).with_z(bldg_base + height),
237 max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 5),
238 })
239 .clear();
240 painter
241 .aabb(Aabb {
242 min: Vec2::new(center.x - 10, center.y - 4).with_z(bldg_base + height),
243 max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 2),
244 })
245 .fill(wood.clone());
246 }
247 painter
249 .aabb(Aabb {
250 min: (center - bldg_length).with_z(bldg_base),
251 max: (center + bldg_length).with_z(bldg_base + height),
252 })
253 .fill(sandstone.clone());
254 painter
256 .aabb(Aabb {
257 min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
258 max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
259 })
260 .fill(wood.clone());
261 painter
262 .aabb(Aabb {
263 min: (center - bldg_length - f).with_z(bldg_base + height),
264 max: (center + bldg_length + f).with_z(bldg_base + height + 1),
265 })
266 .fill(sandstone.clone());
267 painter
269 .aabb(Aabb {
270 min: (center - bldg_length + 1).with_z(bldg_base),
271 max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
272 })
273 .clear();
274
275 let clear_limit_1 = painter.aabb(Aabb {
276 min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
277 .with_z(bldg_base),
278 max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
279 .with_z(bldg_base + height),
280 });
281 let clear_limit_2 = painter.aabb(Aabb {
282 min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
283 .with_z(bldg_base),
284 max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
285 .with_z(bldg_base + height),
286 });
287 for c in 0..=4 {
288 let space = c * ((2 * carve) + 1);
289 painter
290 .vault(
291 Aabb {
292 min: Vec2::new(
293 center.x - bldg_length,
294 center.y + space - bldg_length - carve + 1,
295 )
296 .with_z(bldg_base + (height / 2)),
297 max: Vec2::new(
298 center.x + bldg_length,
299 center.y + space - bldg_length + carve - 1,
300 )
301 .with_z(bldg_base + height - 1),
302 },
303 Dir2::X,
304 )
305 .intersect(clear_limit_1)
306 .clear();
307 painter
308 .vault(
309 Aabb {
310 min: Vec2::new(
311 center.x + space - bldg_length - carve + 1,
312 center.y - bldg_length,
313 )
314 .with_z(bldg_base + (height / 2)),
315 max: Vec2::new(
316 center.x + space - bldg_length + carve - 1,
317 center.y + bldg_length,
318 )
319 .with_z(bldg_base + height - 1),
320 },
321 Dir2::Y,
322 )
323 .intersect(clear_limit_2)
324 .clear();
325
326 painter
327 .aabb(Aabb {
328 min: Vec2::new(
329 center.x - bldg_length,
330 center.y + space - bldg_length - carve,
331 )
332 .with_z(bldg_base),
333 max: Vec2::new(
334 center.x + bldg_length,
335 center.y + space - bldg_length + carve,
336 )
337 .with_z(bldg_base + (height / 2)),
338 })
339 .intersect(clear_limit_1)
340 .clear();
341 painter
342 .aabb(Aabb {
343 min: Vec2::new(
344 center.x + space - bldg_length - carve,
345 center.y - bldg_length,
346 )
347 .with_z(bldg_base),
348 max: Vec2::new(
349 center.x + space - bldg_length + carve,
350 center.y + bldg_length,
351 )
352 .with_z(bldg_base + (height / 2)),
353 })
354 .intersect(clear_limit_2)
355 .clear();
356 }
357 for dir in DIAGONALS {
358 let clear_pos = center + dir * bldg_length;
359 painter
360 .aabb(Aabb {
361 min: (clear_pos - 1).with_z(bldg_base),
362 max: (clear_pos + 1).with_z(bldg_base + height - 1),
363 })
364 .clear();
365 }
366 for dir in DIAGONALS {
367 let lamp_pos = center + dir * (bldg_length - 1);
368 painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
369 if f == 4 {
370 let lamp_pos = center + dir * bldg_length;
371 painter.sprite(
372 lamp_pos.with_z(bldg_base + height + 1),
373 SpriteKind::StreetLamp,
374 );
375
376 let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
377 for dir in CARDINALS {
378 let sprite_pos = cargo_pos + dir;
379 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
380 for r in 0..rows {
381 painter
382 .aabb(Aabb {
383 min: (sprite_pos).with_z(bldg_base + height + 1 + r),
384 max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
385 })
386 .fill(Fill::Block(Block::air(
387 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
388 as i32
389 {
390 0 => SpriteKind::Barrel,
391 _ => SpriteKind::CrateBlock,
392 },
393 )));
394 if r > 0 {
395 painter.owned_resource_sprite(
396 sprite_pos.with_z(bldg_base + height + 2 + r),
397 SpriteKind::Crate,
398 0,
399 );
400 }
401 }
402 }
403 }
404 }
405 if f == 4 {
407 for dir in CARDINALS {
408 let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
409 let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
410 let dock_pos = center + dir * 27;
411 painter
412 .aabb(Aabb {
413 min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
414 max: (gangway_pos_2 + 3).with_z(bldg_base + height),
415 })
416 .fill(wood.clone());
417 painter
418 .aabb(Aabb {
419 min: (gangway_pos_1 - 3).with_z(bldg_base + height),
420 max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
421 })
422 .fill(wood.clone());
423 painter
424 .cylinder(Aabb {
425 min: (dock_pos - 4).with_z(bldg_base + height),
426 max: (dock_pos + 4).with_z(bldg_base + height + 1),
427 })
428 .fill(wood.clone());
429 painter
430 .cylinder(Aabb {
431 min: (dock_pos - 3).with_z(bldg_base + height - 1),
432 max: (dock_pos + 3).with_z(bldg_base + height),
433 })
434 .fill(wood.clone());
435 }
436 painter.spawn(
438 EntityInfo::at(
439 Vec2::new(center.x + bldg_length - 2, center.y)
440 .with_z(bldg_base + height + 1)
441 .map(|e| e as f32 + 0.5),
442 )
443 .into_special(SpecialEntity::Waypoint),
444 );
445 }
446 painter
448 .aabb(Aabb {
449 min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
450 max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
451 })
452 .fill(wood.clone());
453 painter
454 .aabb(Aabb {
455 min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
456 max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
457 })
458 .clear();
459
460 for w in 0..((bldg_length / 2) + 2) {
461 painter
462 .aabb(Aabb {
463 min: Vec2::new(
464 center.x - (bldg_length / 2) - 2 + (2 * w),
465 center.y - (bldg_length / 2),
466 )
467 .with_z(bldg_base + w),
468 max: Vec2::new(
469 center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
470 center.y + (bldg_length / 2),
471 )
472 .with_z(bldg_base + 1 + w),
473 })
474 .fill(wood.clone());
475 painter
476 .aabb(Aabb {
477 min: Vec2::new(
478 center.x - (bldg_length / 2) - 2 + (2 * w),
479 center.y - (bldg_length / 2),
480 )
481 .with_z(bldg_base - 1 + w),
482 max: Vec2::new(
483 center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
484 center.y + (bldg_length / 2),
485 )
486 .with_z(bldg_base + w),
487 })
488 .fill(sandstone.clone());
489 }
490 }
491 }
492}