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