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 let length = 14;
51 let height = 2 * (length / 3);
52 let floors = 4;
53 let base = alt + 1;
54 let top_floor = base + 5 + (height * (floors + 1));
55 let docking_positions = CARDINALS
56 .iter()
57 .map(|dir| (center + dir * 31).with_z(top_floor))
58 .collect::<Vec<_>>();
59 Self {
60 bounds,
61 door_tile: door_tile_pos,
62 alt,
63 docking_positions,
64 center,
65 base,
66 length,
67 height,
68 floors,
69 }
70 }
71
72 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
73 SpawnRules {
74 trees: {
75 const AIRSHIP_MIN_TREE_DIST: i32 = 89;
80 !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST)
81 },
82 waypoints: false,
83 ..SpawnRules::default()
84 }
85 }
86}
87
88impl Structure for DesertCityAirshipDock {
89 #[cfg(feature = "use-dyn-lib")]
90 const UPDATE_FN: &'static [u8] = b"render_desertcityairshipdock\0";
91
92 #[cfg_attr(
93 feature = "be-dyn-lib",
94 unsafe(export_name = "render_desertcityairshipdock")
95 )]
96 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
97 let sandstone = Fill::Sampling(Arc::new(|center| {
98 Some(match (RandomField::new(0).get(center)) % 37 {
99 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
100 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
101 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
102 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
103 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
104 })
105 }));
106 let sandstone_broken = Fill::Sampling(Arc::new(|center| {
107 Some(match (RandomField::new(0).get(center)) % 42 {
108 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
109 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
110 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
111 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
112 36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
113 _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
114 })
115 }));
116 let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
117 let base = self.base;
118 let center = self.center;
119 painter
121 .aabb(Aabb {
122 min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
123 max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
124 })
125 .union(painter.aabb(Aabb {
126 min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
127 max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
128 }))
129 .union(painter.aabb(Aabb {
130 min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
131 max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
132 }))
133 .union(painter.aabb(Aabb {
134 min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
135 max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
136 }))
137 .fill(sandstone_broken);
138 painter
139 .aabb(Aabb {
140 min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
141 max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
142 })
143 .clear();
144 painter
145 .aabb(Aabb {
146 min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
147 max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
148 })
149 .clear();
150 painter
152 .aabb(Aabb {
153 min: (self.bounds.min + 1).with_z(base - 20),
154 max: (self.bounds.max).with_z(base),
155 })
156 .fill(sandstone.clone());
157
158 let length = self.length;
160 let height = self.height;
161 let floors = self.floors;
162 let carve = length / 4;
163
164 for f in 0..=floors {
165 let bldg_base = base + f * (height + 1);
166 let bldg_length = length;
167 if f == floors {
168 painter
170 .aabb(Aabb {
171 min: Vec2::new(center.x - 7, center.y - 5).with_z(bldg_base + height),
172 max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 6),
173 })
174 .fill(sandstone.clone());
175 painter
176 .aabb(Aabb {
177 min: Vec2::new(center.x - 8, center.y - 4).with_z(bldg_base + height),
178 max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 5),
179 })
180 .clear();
181 painter
182 .aabb(Aabb {
183 min: Vec2::new(center.x - 11, center.y - 5).with_z(bldg_base + height),
184 max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 5),
185 })
186 .clear();
187 painter
188 .aabb(Aabb {
189 min: Vec2::new(center.x - 10, center.y - 4).with_z(bldg_base + height),
190 max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 2),
191 })
192 .fill(wood.clone());
193 }
194 painter
196 .aabb(Aabb {
197 min: (center - bldg_length).with_z(bldg_base),
198 max: (center + bldg_length).with_z(bldg_base + height),
199 })
200 .fill(sandstone.clone());
201 painter
203 .aabb(Aabb {
204 min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
205 max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
206 })
207 .fill(wood.clone());
208 painter
209 .aabb(Aabb {
210 min: (center - bldg_length - f).with_z(bldg_base + height),
211 max: (center + bldg_length + f).with_z(bldg_base + height + 1),
212 })
213 .fill(sandstone.clone());
214 painter
216 .aabb(Aabb {
217 min: (center - bldg_length + 1).with_z(bldg_base),
218 max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
219 })
220 .clear();
221
222 let clear_limit_1 = painter.aabb(Aabb {
223 min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
224 .with_z(bldg_base),
225 max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
226 .with_z(bldg_base + height),
227 });
228 let clear_limit_2 = painter.aabb(Aabb {
229 min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
230 .with_z(bldg_base),
231 max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
232 .with_z(bldg_base + height),
233 });
234 for c in 0..=4 {
235 let space = c * ((2 * carve) + 1);
236 painter
237 .vault(
238 Aabb {
239 min: Vec2::new(
240 center.x - bldg_length,
241 center.y + space - bldg_length - carve + 1,
242 )
243 .with_z(bldg_base + (height / 2)),
244 max: Vec2::new(
245 center.x + bldg_length,
246 center.y + space - bldg_length + carve - 1,
247 )
248 .with_z(bldg_base + height - 1),
249 },
250 Dir::X,
251 )
252 .intersect(clear_limit_1)
253 .clear();
254 painter
255 .vault(
256 Aabb {
257 min: Vec2::new(
258 center.x + space - bldg_length - carve + 1,
259 center.y - bldg_length,
260 )
261 .with_z(bldg_base + (height / 2)),
262 max: Vec2::new(
263 center.x + space - bldg_length + carve - 1,
264 center.y + bldg_length,
265 )
266 .with_z(bldg_base + height - 1),
267 },
268 Dir::Y,
269 )
270 .intersect(clear_limit_2)
271 .clear();
272
273 painter
274 .aabb(Aabb {
275 min: Vec2::new(
276 center.x - bldg_length,
277 center.y + space - bldg_length - carve,
278 )
279 .with_z(bldg_base),
280 max: Vec2::new(
281 center.x + bldg_length,
282 center.y + space - bldg_length + carve,
283 )
284 .with_z(bldg_base + (height / 2)),
285 })
286 .intersect(clear_limit_1)
287 .clear();
288 painter
289 .aabb(Aabb {
290 min: Vec2::new(
291 center.x + space - bldg_length - carve,
292 center.y - bldg_length,
293 )
294 .with_z(bldg_base),
295 max: Vec2::new(
296 center.x + space - bldg_length + carve,
297 center.y + bldg_length,
298 )
299 .with_z(bldg_base + (height / 2)),
300 })
301 .intersect(clear_limit_2)
302 .clear();
303 }
304 for dir in DIAGONALS {
305 let clear_pos = center + dir * bldg_length;
306 painter
307 .aabb(Aabb {
308 min: (clear_pos - 1).with_z(bldg_base),
309 max: (clear_pos + 1).with_z(bldg_base + height - 1),
310 })
311 .clear();
312 }
313 for dir in DIAGONALS {
314 let lamp_pos = center + dir * (bldg_length - 1);
315 painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
316 if f == 4 {
317 let lamp_pos = center + dir * bldg_length;
318 painter.sprite(
319 lamp_pos.with_z(bldg_base + height + 1),
320 SpriteKind::StreetLamp,
321 );
322
323 let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
324 for dir in CARDINALS {
325 let sprite_pos = cargo_pos + dir;
326 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
327 for r in 0..rows {
328 painter
329 .aabb(Aabb {
330 min: (sprite_pos).with_z(bldg_base + height + 1 + r),
331 max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
332 })
333 .fill(Fill::Block(Block::air(
334 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
335 as i32
336 {
337 0 => SpriteKind::Barrel,
338 _ => SpriteKind::CrateBlock,
339 },
340 )));
341 if r > 0 {
342 painter.owned_resource_sprite(
343 sprite_pos.with_z(bldg_base + height + 2 + r),
344 SpriteKind::Crate,
345 0,
346 );
347 }
348 }
349 }
350 }
351 }
352 if f == 4 {
354 for dir in CARDINALS {
355 let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
356 let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
357 let dock_pos = center + dir * 27;
358 painter
359 .aabb(Aabb {
360 min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
361 max: (gangway_pos_2 + 3).with_z(bldg_base + height),
362 })
363 .fill(wood.clone());
364 painter
365 .aabb(Aabb {
366 min: (gangway_pos_1 - 3).with_z(bldg_base + height),
367 max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
368 })
369 .fill(wood.clone());
370 painter
371 .cylinder(Aabb {
372 min: (dock_pos - 4).with_z(bldg_base + height),
373 max: (dock_pos + 4).with_z(bldg_base + height + 1),
374 })
375 .fill(wood.clone());
376 painter
377 .cylinder(Aabb {
378 min: (dock_pos - 3).with_z(bldg_base + height - 1),
379 max: (dock_pos + 3).with_z(bldg_base + height),
380 })
381 .fill(wood.clone());
382 }
383 painter.spawn(
385 EntityInfo::at(
386 Vec2::new(center.x + bldg_length - 2, center.y)
387 .with_z(bldg_base + height + 1)
388 .map(|e| e as f32 + 0.5),
389 )
390 .into_special(SpecialEntity::Waypoint),
391 );
392 }
393 painter
395 .aabb(Aabb {
396 min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
397 max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
398 })
399 .fill(wood.clone());
400 painter
401 .aabb(Aabb {
402 min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
403 max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
404 })
405 .clear();
406
407 for w in 0..((bldg_length / 2) + 2) {
408 painter
409 .aabb(Aabb {
410 min: Vec2::new(
411 center.x - (bldg_length / 2) - 2 + (2 * w),
412 center.y - (bldg_length / 2),
413 )
414 .with_z(bldg_base + w),
415 max: Vec2::new(
416 center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
417 center.y + (bldg_length / 2),
418 )
419 .with_z(bldg_base + 1 + w),
420 })
421 .fill(wood.clone());
422 painter
423 .aabb(Aabb {
424 min: Vec2::new(
425 center.x - (bldg_length / 2) - 2 + (2 * w),
426 center.y - (bldg_length / 2),
427 )
428 .with_z(bldg_base - 1 + w),
429 max: Vec2::new(
430 center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
431 center.y + (bldg_length / 2),
432 )
433 .with_z(bldg_base + w),
434 })
435 .fill(sandstone.clone());
436 }
437 }
438 }
439}