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