1use super::*;
2use crate::{Land, site2::gen::PrimitiveTransform};
3use common::{
4 generation::EntityInfo,
5 terrain::{BiomeKind, Block, BlockKind},
6};
7use num::integer::Roots;
8use rand::prelude::*;
9use vek::*;
10
11enum RoofKind {
12 Crenelated,
13 Hipped,
14}
15
16struct HeightenedViaduct {
17 slope_inv: i32,
18 bridge_start_offset: i32,
19 vault_spacing: i32,
20 vault_size: (i32, i32),
21 side_vault_size: (i32, i32),
22 holes: bool,
23}
24
25impl HeightenedViaduct {
26 fn random(rng: &mut impl Rng, height: i32) -> Self {
27 let vault_spacing = *[3, 4, 5, 6].choose(rng).unwrap();
28 Self {
29 slope_inv: rng.gen_range(6..=8),
30 bridge_start_offset: rng.gen_range({
31 let min = (5 - height / 3).max(0);
32 min..=(12 - height).max(min)
33 }),
34 vault_spacing,
35 vault_size: *[(3, 16), (1, 4), (1, 4), (1, 4), (5, 32), (5, 32)]
36 .choose(rng)
37 .unwrap(),
38 side_vault_size: *[(4, 5), (7, 10), (7, 10), (13, 20)].choose(rng).unwrap(),
39 holes: vault_spacing >= 4 && vault_spacing % 2 == 0 && rng.gen_bool(0.8),
40 }
41 }
42}
43
44enum BridgeKind {
45 Flat,
46 Tower(RoofKind),
47 Short,
48 HeightenedViaduct(HeightenedViaduct),
49 HangBridge,
50}
51
52impl BridgeKind {
53 fn random(
54 rng: &mut impl Rng,
55 start: Vec3<i32>,
56 start_dist: i32,
57 end: Vec3<i32>,
58 end_dist: i32,
59 water_alt: i32,
60 ) -> BridgeKind {
61 let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max();
62 let height = end.z - start.z;
63 let down = start.z - water_alt;
64 (0..=4)
65 .filter_map(|bridge| match bridge {
66 0 if height >= 16 => Some(BridgeKind::Tower(match rng.gen_range(0..=2) {
67 0 => RoofKind::Crenelated,
68 _ => RoofKind::Hipped,
69 })),
70 1 if len < 60 => Some(BridgeKind::Short),
71 2 if len >= 50
72 && height < 13
73 && down < 20
74 && ((start_dist > 13 && end_dist > 13)
75 || (start_dist - end_dist).abs() < 6) =>
76 {
77 Some(BridgeKind::HeightenedViaduct(HeightenedViaduct::random(
78 rng, height,
79 )))
80 },
81 3 if height < 10 && down > 10 => Some(BridgeKind::HangBridge),
82 4 if down > 8 => Some(BridgeKind::Flat),
83 _ => None,
84 })
85 .collect::<Vec<_>>()
86 .into_iter()
87 .choose(rng)
88 .unwrap_or(BridgeKind::Flat)
89 }
90
91 fn width(&self) -> i32 {
92 match self {
93 BridgeKind::HangBridge => 2,
94 _ => 8,
95 }
96 }
97}
98
99fn aabb(min: Vec3<i32>, max: Vec3<i32>) -> Aabb<i32> {
100 let aabb = Aabb { min, max }.made_valid();
101 Aabb {
102 min: aabb.min,
103 max: aabb.max + 1,
104 }
105}
106
107fn render_short(bridge: &Bridge, painter: &Painter) {
108 let (bridge_fill, edge_fill) = match bridge.biome {
109 BiomeKind::Desert => (
110 Fill::Block(Block::new(BlockKind::Rock, Rgb::new(212, 191, 142))),
111 Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(190))),
112 ),
113 _ => (
114 Fill::Brick(BlockKind::Rock, Rgb::gray(70), 25),
115 Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(130))),
116 ),
117 };
118
119 let bridge_width = 3;
120
121 let orth_dir = bridge.dir.orthogonal();
122
123 let orthogonal = orth_dir.to_vec2();
124 let forward = bridge.dir.to_vec2();
125
126 let len = (bridge.start.xy() - bridge.end.xy())
127 .map(|e| e.abs())
128 .reduce_max();
129 let inset = 4;
130
131 let top = bridge.end.z + (len / 5).max(8) - inset;
132
133 let side = orthogonal * bridge_width;
134
135 let remove = painter.vault(
136 aabb(
137 (bridge.start.xy() - side + forward * inset).with_z(bridge.start.z),
138 (bridge.end.xy() + side - forward * inset).with_z(top - 2),
139 ),
140 orth_dir,
141 );
142
143 let up_ramp = |point: Vec3<i32>, dir: Dir, side_len: i32| {
146 let forward = dir.to_vec2();
147 let side = dir.orthogonal().to_vec2() * side_len;
148 let ramp_in = top - point.z;
149 painter
150 .ramp(
151 aabb(
152 point - side,
153 (point.xy() + side + forward * ramp_in).with_z(top),
154 ),
155 dir,
156 )
157 .union(painter.aabb(aabb(
158 (point - side).with_z(point.z - 4),
159 point + side + forward * ramp_in,
160 )))
161 };
162
163 let bridge_prim = |side_len: i32| {
164 let side = orthogonal * side_len;
165 painter
166 .aabb(aabb(
167 (bridge.start.xy() - side + forward * (top - bridge.start.z))
168 .with_z(bridge.start.z),
169 (bridge.end.xy() + side - forward * (top - bridge.end.z)).with_z(top),
170 ))
171 .union(up_ramp(bridge.start, bridge.dir, side_len).union(up_ramp(
172 bridge.end,
173 -bridge.dir,
174 side_len,
175 )))
176 };
177
178 let b = bridge_prim(bridge_width);
179
180 b.without(remove).fill(bridge_fill);
196
197 let prim = bridge_prim(bridge_width + 1);
198
199 prim.translate(Vec3::unit_z())
200 .without(prim)
201 .without(painter.aabb(aabb(
202 bridge.start - side - forward,
203 (bridge.end.xy() + side + forward).with_z(top + 1),
204 )))
205 .fill(edge_fill);
206}
207
208fn render_flat(bridge: &Bridge, painter: &Painter) {
209 let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
210 let light_rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(130)));
211
212 let orth_dir = bridge.dir.orthogonal();
213
214 let orthogonal = orth_dir.to_vec2();
215 let forward = bridge.dir.to_vec2();
216
217 let height = bridge.end.z - bridge.start.z;
218
219 let bridge_width = bridge.width();
220 let side = orthogonal * bridge_width;
221
222 let aabr = Aabr {
223 min: bridge.start.xy() - side,
224 max: bridge.end.xy() + side,
225 }
226 .made_valid();
227
228 let [ramp_aabr, aabr] = bridge.dir.split_aabr_offset(aabr, height);
229
230 let ramp_prim = |ramp_aabr: Aabr<i32>, offset: i32| {
231 painter
232 .aabb(aabb(
233 ramp_aabr.min.with_z(bridge.start.z - 10 + offset),
234 ramp_aabr.max.with_z(bridge.start.z - 1 + offset),
235 ))
236 .union(painter.ramp(
237 aabb(
238 ramp_aabr.min.with_z(bridge.start.z + offset),
239 ramp_aabr.max.with_z(bridge.end.z + offset),
240 ),
241 bridge.dir,
242 ))
243 };
244
245 ramp_prim(ramp_aabr, 1).fill(light_rock.clone());
246
247 let ramp_aabr = orth_dir
248 .opposite()
249 .trim_aabr(orth_dir.trim_aabr(ramp_aabr, 1), 1);
250 ramp_prim(ramp_aabr, 5).clear();
251 ramp_prim(ramp_aabr, 0).fill(rock.clone());
252
253 let vault_width = 12;
254 let vault_offset = 5;
255 let bridge_thickness = 4;
256
257 let [vault, _] = bridge.dir.split_aabr_offset(aabr, vault_width);
258
259 let len = bridge.dir.select(aabr.size());
260 let true_offset = vault_width + vault_offset;
261 let n = (len / true_offset).max(1);
262 let p = len / n;
263
264 let holes = painter
265 .vault(
266 aabb(
267 vault.min.with_z(bridge.center.z - 20),
268 vault.max.with_z(bridge.end.z - bridge_thickness - 1),
269 ),
270 orth_dir,
271 )
272 .repeat((forward * p).with_z(0), n as u32);
273
274 painter
275 .aabb(aabb(
276 aabr.min.with_z(bridge.center.z - 10),
277 aabr.max.with_z(bridge.end.z + 1),
278 ))
279 .without(holes)
280 .fill(light_rock);
281
282 let aabr = orth_dir
283 .opposite()
284 .trim_aabr(orth_dir.trim_aabr(aabr, 1), 1);
285 painter
286 .aabb(aabb(
287 aabr.min.with_z(bridge.end.z + 1),
288 aabr.max.with_z(bridge.end.z + 8),
289 ))
290 .clear();
291
292 painter
293 .aabb(aabb(
294 aabr.min.with_z(bridge.end.z),
295 aabr.max.with_z(bridge.end.z),
296 ))
297 .fill(rock);
298}
299
300fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &HeightenedViaduct) {
301 let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
302 let light_rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(130)));
303 let orth_dir = bridge.dir.orthogonal();
304
305 let orthogonal = orth_dir.to_vec2();
306 let forward = bridge.dir.to_vec2();
307
308 let slope_inv = data.slope_inv;
309
310 let len = (bridge.start.xy() - bridge.end.xy())
311 .map(|e| e.abs())
312 .reduce_max();
313
314 let bridge_start_z = bridge.end.z + data.bridge_start_offset;
315 let bridge_top = bridge_start_z + len / slope_inv / 2;
316
317 let bridge_width = bridge.width();
318 let side = orthogonal * bridge_width;
319
320 let aabr = Aabr {
321 min: bridge.start.xy() - side,
322 max: bridge.end.xy() + side,
323 }
324 .made_valid();
325
326 let [_start_aabr, rest] = bridge
327 .dir
328 .split_aabr_offset(aabr, bridge_start_z - bridge.start.z);
329 let [_end_aabr, bridge_aabr] =
330 (-bridge.dir).split_aabr_offset(rest, bridge_start_z - bridge.end.z);
331 let under = bridge.center.z - 15;
332
333 let bridge_prim = |bridge_width: i32| {
334 let side = orthogonal * bridge_width;
335
336 let aabr = Aabr {
337 min: bridge.start.xy() - side,
338 max: bridge.end.xy() + side,
339 }
340 .made_valid();
341
342 let [start_aabr, rest] = bridge
343 .dir
344 .split_aabr_offset(aabr, bridge_start_z - bridge.start.z);
345 let [end_aabr, bridge_aabr] =
346 (-bridge.dir).split_aabr_offset(rest, bridge_start_z - bridge.end.z);
347 let [bridge_start, bridge_end] = bridge
348 .dir
349 .split_aabr_offset(bridge_aabr, bridge.dir.select(bridge_aabr.size()) / 2);
350
351 let ramp_in_aabr = |aabr: Aabr<i32>, dir: Dir, zmin, zmax| {
352 let inset = dir.select(aabr.size());
353 painter.ramp_inset(
354 aabb(aabr.min.with_z(zmin), aabr.max.with_z(zmax)),
355 inset,
356 dir,
357 )
358 };
359
360 ramp_in_aabr(start_aabr, bridge.dir, bridge.start.z, bridge_start_z)
361 .union(
362 ramp_in_aabr(end_aabr, -bridge.dir, bridge.end.z, bridge_start_z)
363 .union(ramp_in_aabr(
364 bridge_start,
365 bridge.dir,
366 bridge_start_z + 1,
367 bridge_top,
368 ))
369 .union(ramp_in_aabr(
370 bridge_end,
371 -bridge.dir,
372 bridge_start_z + 1,
373 bridge_top,
374 )),
375 )
376 .union(
377 painter
378 .aabb(aabb(
379 start_aabr.min.with_z(under),
380 start_aabr.max.with_z(bridge.start.z - 1),
381 ))
382 .union(painter.aabb(aabb(
383 end_aabr.min.with_z(under),
384 end_aabr.max.with_z(bridge.end.z - 1),
385 ))),
386 )
387 .union(painter.aabb(aabb(
388 bridge_aabr.min.with_z(under),
389 bridge_aabr.max.with_z(bridge_start_z),
390 )))
391 };
392
393 let br = bridge_prim(bridge_width - 1);
394 let b = br.without(br.translate(-Vec3::unit_z()));
395
396 let c = bridge_aabr.center();
397 let len = bridge.dir.select(bridge_aabr.size());
398 let vault_size = data.vault_size.0 * len / data.vault_size.1;
399 let side_vault = data.side_vault_size.0 * vault_size / data.side_vault_size.1;
400 let vertical = 5;
401 let spacing = data.vault_spacing;
402 let vault_top = bridge_top - vertical;
403 let side_vault_top = vault_top - (vault_size + spacing + 1 + side_vault) / slope_inv;
404 let side_vault_offset = vault_size + spacing + 1;
405
406 let mut remove = painter.vault(
407 aabb(
408 (c - side - forward * vault_size).with_z(under),
409 (c + side + forward * vault_size).with_z(vault_top),
410 ),
411 orth_dir,
412 );
413
414 if side_vault * 2 + side_vault_offset < len / 2 + 5 {
415 remove = remove.union(
416 painter
417 .vault(
418 aabb(
419 (c - side + forward * side_vault_offset).with_z(under),
420 (c + side + forward * (side_vault * 2 + side_vault_offset))
421 .with_z(side_vault_top),
422 ),
423 orth_dir,
424 )
425 .union(
426 painter.vault(
427 aabb(
428 (c - side - forward * side_vault_offset).with_z(under),
429 (c + side - forward * (side_vault * 2 + side_vault_offset))
430 .with_z(side_vault_top),
431 ),
432 orth_dir,
433 ),
434 ),
435 );
436
437 if data.holes {
438 remove = remove.union(
439 painter
440 .vault(
441 aabb(
442 (c - side + forward * (vault_size + 1)).with_z(side_vault_top - 4),
443 (c + side + forward * (vault_size + spacing))
444 .with_z(side_vault_top + 2),
445 ),
446 orth_dir,
447 )
448 .union(
449 painter.vault(
450 aabb(
451 (c - side - forward * (vault_size + 1)).with_z(side_vault_top - 4),
452 (c + side - forward * (vault_size + spacing))
453 .with_z(side_vault_top + 2),
454 ),
455 orth_dir,
456 ),
457 ),
458 );
459 }
460 }
461
462 bridge_prim(bridge_width).without(remove).fill(rock);
463 b.translate(-Vec3::unit_z()).fill(light_rock);
464
465 br.translate(Vec3::unit_z() * 5)
466 .without(br.translate(-Vec3::unit_z()))
467 .clear();
468
469 let mut rng = thread_rng();
514 if rng.gen_bool(0.1) {
515 painter.spawn(
516 EntityInfo::at(c.with_z(bridge.center.z).as_()).with_asset_expect(
517 "common.entity.wild.aggressive.swamp_troll",
518 &mut rng,
519 None,
520 ),
521 );
522 }
523}
524
525fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
526 let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
527 let wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(40, 28, 20)));
528
529 let tower_size = 5;
530
531 let bridge_width = tower_size - 2;
532
533 let orth_dir = bridge.dir.orthogonal();
534
535 let orthogonal = orth_dir.to_vec2();
536 let forward = bridge.dir.to_vec2();
537
538 let tower_height_extend = 10;
539
540 let tower_end = bridge.end.z + tower_height_extend;
541
542 let tower_center = bridge.start.xy() + forward * tower_size;
543 let tower_aabr = Aabr {
544 min: tower_center - tower_size,
545 max: tower_center + tower_size,
546 };
547
548 let len = (bridge.dir.select(bridge.end.xy()) - bridge.dir.select_aabr(tower_aabr)).abs() - 1;
549
550 painter
551 .aabb(aabb(
552 tower_aabr.min.with_z(bridge.start.z - 5),
553 tower_aabr.max.with_z(tower_end),
554 ))
555 .fill(rock.clone());
556
557 painter
558 .aabb(aabb(
559 (tower_aabr.min + 1).with_z(bridge.start.z),
560 (tower_aabr.max - 1).with_z(tower_end - 1),
561 ))
562 .clear();
563
564 let c = (-bridge.dir).select_aabr_with(tower_aabr, tower_aabr.center());
565 painter
566 .aabb(aabb(
567 (c - orthogonal).with_z(bridge.start.z),
568 (c + orthogonal).with_z(bridge.start.z + 2),
569 ))
570 .clear();
571
572 let ramp_height = 8;
573
574 let ramp_aabb = aabb(
575 (c - forward - orthogonal).with_z(bridge.start.z - 1),
576 (c - forward * ramp_height + orthogonal).with_z(bridge.start.z + ramp_height - 2),
577 );
578
579 painter
580 .aabb(ramp_aabb)
581 .without(painter.ramp(ramp_aabb, -bridge.dir))
582 .clear();
583
584 let c = bridge.dir.select_aabr_with(tower_aabr, tower_aabr.center());
585 painter
586 .aabb(aabb(
587 (c - orthogonal).with_z(bridge.end.z),
588 (c + orthogonal).with_z(bridge.end.z + 2),
589 ))
590 .clear();
591
592 let stair_thickness = 2;
593 painter
594 .staircase_in_aabb(
595 aabb(
596 (tower_aabr.min + 1).with_z(bridge.start.z),
597 (tower_aabr.max - 1).with_z(bridge.end.z - 1),
598 ),
599 stair_thickness,
600 bridge.dir.rotated_ccw(),
601 )
602 .fill(rock.clone());
603 let aabr = bridge
604 .dir
605 .rotated_cw()
606 .split_aabr_offset(tower_aabr, stair_thickness + 1)[1];
607
608 painter
609 .aabb(aabb(
610 aabr.min.with_z(bridge.end.z - 1),
611 aabr.max.with_z(bridge.end.z - 1),
612 ))
613 .fill(rock.clone());
614
615 painter
616 .aabb(aabb(
617 tower_aabr.center().with_z(bridge.start.z),
618 tower_aabr.center().with_z(bridge.end.z - 1),
619 ))
620 .fill(rock.clone());
621
622 let offset = tower_size * 2 - 2;
623 let d = 2;
624 let n = (bridge.end.z - bridge.start.z - d) / offset;
625 let p = (bridge.end.z - bridge.start.z - d) / n;
626
627 for i in 1..=n {
628 let c = tower_aabr.center().with_z(bridge.start.z + i * p);
629
630 for dir in Dir::ALL {
631 painter.rotated_sprite(c + dir.to_vec2(), SpriteKind::WallSconce, dir.sprite_ori());
632 }
633 }
634
635 painter.rotated_sprite(
636 (tower_aabr.center() + bridge.dir.to_vec2() * (tower_size - 1))
637 .with_z(bridge.end.z + tower_height_extend / 2),
638 SpriteKind::WallLamp,
639 (-bridge.dir).sprite_ori(),
640 );
641
642 match roof_kind {
643 RoofKind::Crenelated => {
644 painter
645 .aabb(aabb(
646 (tower_aabr.min - 1).with_z(tower_end + 1),
647 (tower_aabr.max + 1).with_z(tower_end + 2),
648 ))
649 .fill(rock.clone());
650
651 painter
652 .aabbs_around_aabb(
653 aabb(
654 tower_aabr.min.with_z(tower_end + 3),
655 tower_aabr.max.with_z(tower_end + 3),
656 ),
657 1,
658 1,
659 )
660 .fill(rock.clone());
661
662 painter
663 .aabb(aabb(
664 tower_aabr.min.with_z(tower_end + 2),
665 tower_aabr.max.with_z(tower_end + 2),
666 ))
667 .clear();
668
669 painter
670 .aabbs_around_aabb(
671 aabb(
672 (tower_aabr.min + 1).with_z(tower_end + 2),
673 (tower_aabr.max - 1).with_z(tower_end + 2),
674 ),
675 1,
676 4,
677 )
678 .fill(Fill::sprite(SpriteKind::FireBowlGround));
679 },
680 RoofKind::Hipped => {
681 painter
682 .pyramid(aabb(
683 (tower_aabr.min - 1).with_z(tower_end + 1),
684 (tower_aabr.max + 1).with_z(tower_end + 2 + tower_size),
685 ))
686 .fill(wood);
687 },
688 }
689
690 let offset = 15;
691 let thickness = 3;
692
693 let size = (offset - thickness) / 2;
694
695 let n = len / offset;
696 let p = len / n;
697
698 let offset = forward * p;
699
700 let size = bridge_width * orthogonal + forward * size;
701 let start = bridge.dir.select_aabr_with(tower_aabr, tower_aabr.center()) + forward;
702 painter
703 .aabb(aabb(
704 (start - orthogonal * bridge_width).with_z(bridge.center.z - 10),
705 (bridge.end + orthogonal * bridge_width).with_z(bridge.end.z - 1),
706 ))
707 .without(
708 painter
709 .vault(
710 aabb(
711 (start + offset / 2 - size).with_z(bridge.center.z - 10),
712 (start + offset / 2 + size).with_z(bridge.end.z - 3),
713 ),
714 orth_dir,
715 )
716 .repeat(offset.with_z(0), n as u32),
717 )
718 .fill(rock);
719
720 painter
721 .aabb(aabb(
722 (start - orthogonal * bridge_width).with_z(bridge.end.z),
723 (bridge.end + orthogonal * bridge_width).with_z(bridge.end.z + 5),
724 ))
725 .clear();
726
727 let light_spacing = 10;
728 let n = len / light_spacing;
729 let p = len / n;
730
731 let start = bridge.end;
732 let offset = -forward * p;
733 for i in 1..=n {
734 let c = start + i * offset;
735
736 painter.sprite(c + orthogonal * bridge_width, SpriteKind::StreetLamp);
737 painter.sprite(c - orthogonal * bridge_width, SpriteKind::StreetLamp);
738 }
739}
740
741fn render_hang(bridge: &Bridge, painter: &Painter) {
742 let orth_dir = bridge.dir.orthogonal();
743
744 let orthogonal = orth_dir.to_vec2();
745 let forward = bridge.dir.to_vec2();
746
747 let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
748 let wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(133, 94, 66)));
749
750 let bridge_width = bridge.width();
751 let side = orthogonal * bridge_width;
752
753 let aabr = Aabr {
754 min: bridge.start.xy() - side,
755 max: bridge.end.xy() + side,
756 }
757 .made_valid();
758
759 let top_offset = 4;
760 let top = bridge.end.z + top_offset;
761
762 let [ramp_f, aabr] = bridge.dir.split_aabr_offset(aabr, top - bridge.start.z + 1);
763
764 painter
765 .aabb(aabb(
766 ramp_f.min.with_z(bridge.start.z - 10),
767 ramp_f.max.with_z(bridge.start.z),
768 ))
769 .fill(rock.clone());
770 painter
771 .ramp_inset(
772 aabb(ramp_f.min.with_z(bridge.start.z), ramp_f.max.with_z(top)),
773 top - bridge.start.z + 1,
774 bridge.dir,
775 )
776 .fill(rock.clone());
777
778 let [ramp_b, aabr] = (-bridge.dir).split_aabr_offset(aabr, top_offset + 1);
779 painter
780 .aabb(aabb(
781 ramp_b.min.with_z(bridge.end.z - 10),
782 ramp_b.max.with_z(bridge.end.z),
783 ))
784 .fill(rock.clone());
785 painter
786 .ramp(
787 aabb(ramp_b.min.with_z(bridge.end.z), ramp_b.max.with_z(top)),
788 -bridge.dir,
789 )
790 .fill(rock.clone());
791
792 let len = bridge.dir.select(aabr.size());
793
794 let h = 3 * len.sqrt() / 4;
795
796 let x = len / 2;
797
798 let xsqr = (x * x) as f32;
799 let hsqr = (h * h) as f32;
800 let w = ((xsqr + (xsqr * (4.0 * hsqr + xsqr)).sqrt()) / 2.0)
801 .sqrt()
802 .ceil()
803 + 1.0;
804
805 let bottom = top - (h - (hsqr - hsqr * x as f32 / w).sqrt().ceil() as i32);
806
807 let w = w as i32;
808 let c = aabr.center();
809
810 let cylinder = painter
811 .horizontal_cylinder(
812 aabb(
813 (c - forward * w - side).with_z(bottom),
814 (c + forward * w + side).with_z(bottom + h * 2),
815 ),
816 orth_dir,
817 )
818 .intersect(painter.aabb(aabb(
819 aabr.min.with_z(bottom),
820 aabr.max.with_z(bottom + h * 2),
821 )));
822
823 cylinder.fill(wood.clone());
824
825 cylinder.translate(Vec3::unit_z()).clear();
826
827 let edges = cylinder
828 .without(cylinder.translate(Vec3::unit_z()))
829 .without(painter.aabb(aabb(
830 (c - forward * w - orthogonal * (bridge_width - 1)).with_z(bottom),
831 (c + forward * w + orthogonal * (bridge_width - 1)).with_z(bottom + h * 2),
832 )));
833
834 edges
835 .translate(Vec3::unit_z())
836 .fill(Fill::sprite(SpriteKind::Rope));
837
838 edges.translate(Vec3::unit_z() * 2).fill(wood);
839
840 let column_height = 3;
841 let column_range = top..=top + column_height;
842 painter
843 .column(
844 bridge.dir.select_aabr_with(ramp_f, ramp_f.min),
845 column_range.clone(),
846 )
847 .fill(rock.clone());
848 painter
849 .column(
850 bridge.dir.select_aabr_with(ramp_f, ramp_f.max),
851 column_range.clone(),
852 )
853 .fill(rock.clone());
854 painter
855 .column(
856 (-bridge.dir).select_aabr_with(ramp_b, ramp_b.min),
857 column_range.clone(),
858 )
859 .fill(rock.clone());
860 painter
861 .column(
862 (-bridge.dir).select_aabr_with(ramp_b, ramp_b.max),
863 column_range,
864 )
865 .fill(rock);
866}
867
868pub struct Bridge {
869 pub(crate) original_start: Vec2<i32>,
871 pub(crate) original_end: Vec2<i32>,
873
874 pub(crate) start: Vec3<i32>,
875 pub(crate) end: Vec3<i32>,
876 pub(crate) dir: Dir,
877 center: Vec3<i32>,
878 kind: BridgeKind,
879 biome: BiomeKind,
880}
881
882impl Bridge {
883 pub fn generate(
884 land: &Land,
885 index: IndexRef,
886 rng: &mut impl Rng,
887 site: &Site,
888 start: Vec2<i32>,
889 end: Vec2<i32>,
890 ) -> Self {
891 let original_start = site.tile_wpos(start);
892 let original_end = site.tile_wpos(end);
893
894 let min_water_dist = 5;
895 let find_edge = |start: Vec2<i32>, end: Vec2<i32>| {
896 let mut test_start = start;
897 let dir = Dir::from_vec2(end - start).to_vec2();
898 let mut last_alt = if let Some(col) = land.column_sample(start, index) {
899 col.alt as i32
900 } else {
901 return (
902 test_start.with_z(land.get_alt_approx(start) as i32),
903 i32::MAX,
904 );
905 };
906 let mut step = 0;
907 loop {
908 if let Some(sample) = land.column_sample(test_start + step * dir, index) {
909 let alt = sample.alt as i32;
910 let water_dist = sample.water_dist.unwrap_or(16.0) as i32;
911 if last_alt - alt > 1 + (step + 2) / 3
912 || sample.riverless_alt - sample.alt > 2.0
913 {
914 break (test_start.with_z(last_alt), water_dist);
915 } else {
916 test_start += step * dir;
917
918 if water_dist <= min_water_dist {
919 break (test_start.with_z(alt), water_dist);
920 }
921
922 step = water_dist - min_water_dist;
923
924 last_alt = alt;
925 }
926 } else {
927 break (test_start.with_z(last_alt), i32::MAX);
928 }
929 }
930 };
931
932 let (test_start, start_dist) = find_edge(original_start, original_end);
933
934 let (test_end, end_dist) = find_edge(original_end, original_start);
935
936 let (start, start_dist, end, end_dist) = if test_start.z < test_end.z {
937 (test_start, start_dist, test_end, end_dist)
938 } else {
939 (test_end, end_dist, test_start, start_dist)
940 };
941
942 let center = (start.xy() + end.xy()) / 2;
943 let col = land.column_sample(center, index).unwrap();
944 let center = center.with_z(col.alt as i32);
945 let water_alt = col.water_level as i32;
946 let bridge = BridgeKind::random(rng, start, start_dist, end, end_dist, water_alt);
947 Self {
948 original_start,
949 original_end,
950 start,
951 end,
952 center,
953 dir: Dir::from_vec2(end.xy() - start.xy()),
954 kind: bridge,
955 biome: land
956 .get_chunk_wpos(center.xy())
957 .map_or(BiomeKind::Void, |chunk| chunk.get_biome()),
958 }
959 }
960
961 pub fn width(&self) -> i32 { self.kind.width() }
962}
963
964impl Structure for Bridge {
965 #[cfg(feature = "use-dyn-lib")]
966 const UPDATE_FN: &'static [u8] = b"render_bridge\0";
967
968 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_bridge")]
969 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
970 match &self.kind {
971 BridgeKind::Flat => render_flat(self, painter),
972 BridgeKind::Tower(roof) => render_tower(self, painter, roof),
973 BridgeKind::Short => render_short(self, painter),
974 BridgeKind::HeightenedViaduct(data) => render_heightened_viaduct(self, painter, data),
975 BridgeKind::HangBridge => render_hang(self, painter),
976 }
977 }
978}