veloren_world/site2/plot/
bridge.rs

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 outset = 7;
144
145    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    /*
181    let t = 4;
182    b.union(
183        painter.aabb(aabb(
184            (bridge.start.xy() - side - forward * (top - bridge.start.z))
185                .with_z(bridge.start.z - t),
186            (bridge.end.xy() + side + forward * (top - bridge.end.z))
187                .with_z(bridge.start.z),
188        )),
189    )
190    .translate(Vec3::new(0, 0, t))
191    .without(b)
192    .clear();
193    */
194
195    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    /*
470    let place_lights = |center: Vec3<i32>| {
471        painter.sprite(
472            orth_dir
473                .select_aabr_with(bridge_aabr, center.xy())
474                .with_z(center.z),
475            SpriteKind::FireBowlGround,
476        );
477        painter.sprite(
478            (-orth_dir)
479                .select_aabr_with(bridge_aabr, center.xy())
480                .with_z(center.z),
481            SpriteKind::FireBowlGround,
482        );
483    };
484
485    place_lights(bridge_aabr.center().with_z(bridge_top + 1));
486
487    let light_spacing = 1;
488    let num_lights = (len - 1) / 2 / light_spacing;
489
490    let place_lights = |i: i32| {
491        let offset = i * light_spacing;
492        let z =
493            bridge_start_z + 1 + (offset + if len / 2 % 2 == 0 { 4 } else { 3 }) / (slope_inv - 1);
494
495        place_lights(
496            (bridge
497                .dir
498                .select_aabr_with(bridge_aabr, bridge_aabr.center())
499                - forward * offset)
500                .with_z(z),
501        );
502        place_lights(
503            ((-bridge.dir).select_aabr_with(bridge_aabr, bridge_aabr.center()) + forward * offset)
504                .with_z(z),
505        );
506    };
507    for i in 0..num_lights {
508        place_lights(i);
509    }
510    */
511
512    // Small chance to spawn a troll.
513    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(
632                c + dir.to_vec2(),
633                SpriteKind::WallSconce,
634                dir.sprite_ori_legacy(),
635            );
636        }
637    }
638
639    painter.rotated_sprite(
640        (tower_aabr.center() + bridge.dir.to_vec2() * (tower_size - 1))
641            .with_z(bridge.end.z + tower_height_extend / 2),
642        SpriteKind::WallLamp,
643        (-bridge.dir).sprite_ori_legacy(),
644    );
645
646    match roof_kind {
647        RoofKind::Crenelated => {
648            painter
649                .aabb(aabb(
650                    (tower_aabr.min - 1).with_z(tower_end + 1),
651                    (tower_aabr.max + 1).with_z(tower_end + 2),
652                ))
653                .fill(rock.clone());
654
655            painter
656                .aabbs_around_aabb(
657                    aabb(
658                        tower_aabr.min.with_z(tower_end + 3),
659                        tower_aabr.max.with_z(tower_end + 3),
660                    ),
661                    1,
662                    1,
663                )
664                .fill(rock.clone());
665
666            painter
667                .aabb(aabb(
668                    tower_aabr.min.with_z(tower_end + 2),
669                    tower_aabr.max.with_z(tower_end + 2),
670                ))
671                .clear();
672
673            painter
674                .aabbs_around_aabb(
675                    aabb(
676                        (tower_aabr.min + 1).with_z(tower_end + 2),
677                        (tower_aabr.max - 1).with_z(tower_end + 2),
678                    ),
679                    1,
680                    4,
681                )
682                .fill(Fill::sprite(SpriteKind::FireBowlGround));
683        },
684        RoofKind::Hipped => {
685            painter
686                .pyramid(aabb(
687                    (tower_aabr.min - 1).with_z(tower_end + 1),
688                    (tower_aabr.max + 1).with_z(tower_end + 2 + tower_size),
689                ))
690                .fill(wood);
691        },
692    }
693
694    let offset = 15;
695    let thickness = 3;
696
697    let size = (offset - thickness) / 2;
698
699    let n = len / offset;
700    let p = len / n;
701
702    let offset = forward * p;
703
704    let size = bridge_width * orthogonal + forward * size;
705    let start = bridge.dir.select_aabr_with(tower_aabr, tower_aabr.center()) + forward;
706    painter
707        .aabb(aabb(
708            (start - orthogonal * bridge_width).with_z(bridge.center.z - 10),
709            (bridge.end + orthogonal * bridge_width).with_z(bridge.end.z - 1),
710        ))
711        .without(
712            painter
713                .vault(
714                    aabb(
715                        (start + offset / 2 - size).with_z(bridge.center.z - 10),
716                        (start + offset / 2 + size).with_z(bridge.end.z - 3),
717                    ),
718                    orth_dir,
719                )
720                .repeat(offset.with_z(0), n as u32),
721        )
722        .fill(rock);
723
724    painter
725        .aabb(aabb(
726            (start - orthogonal * bridge_width).with_z(bridge.end.z),
727            (bridge.end + orthogonal * bridge_width).with_z(bridge.end.z + 5),
728        ))
729        .clear();
730
731    let light_spacing = 10;
732    let n = len / light_spacing;
733    let p = len / n;
734
735    let start = bridge.end;
736    let offset = -forward * p;
737    for i in 1..=n {
738        let c = start + i * offset;
739
740        painter.sprite(c + orthogonal * bridge_width, SpriteKind::StreetLamp);
741        painter.sprite(c - orthogonal * bridge_width, SpriteKind::StreetLamp);
742    }
743}
744
745fn render_hang(bridge: &Bridge, painter: &Painter) {
746    let orth_dir = bridge.dir.orthogonal();
747
748    let orthogonal = orth_dir.to_vec2();
749    let forward = bridge.dir.to_vec2();
750
751    let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
752    let wood = Fill::Block(Block::new(BlockKind::Wood, Rgb::new(133, 94, 66)));
753
754    let bridge_width = bridge.width();
755    let side = orthogonal * bridge_width;
756
757    let aabr = Aabr {
758        min: bridge.start.xy() - side,
759        max: bridge.end.xy() + side,
760    }
761    .made_valid();
762
763    let top_offset = 4;
764    let top = bridge.end.z + top_offset;
765
766    let [ramp_f, aabr] = bridge.dir.split_aabr_offset(aabr, top - bridge.start.z + 1);
767
768    painter
769        .aabb(aabb(
770            ramp_f.min.with_z(bridge.start.z - 10),
771            ramp_f.max.with_z(bridge.start.z),
772        ))
773        .fill(rock.clone());
774    painter
775        .ramp_inset(
776            aabb(ramp_f.min.with_z(bridge.start.z), ramp_f.max.with_z(top)),
777            top - bridge.start.z + 1,
778            bridge.dir,
779        )
780        .fill(rock.clone());
781
782    let [ramp_b, aabr] = (-bridge.dir).split_aabr_offset(aabr, top_offset + 1);
783    painter
784        .aabb(aabb(
785            ramp_b.min.with_z(bridge.end.z - 10),
786            ramp_b.max.with_z(bridge.end.z),
787        ))
788        .fill(rock.clone());
789    painter
790        .ramp(
791            aabb(ramp_b.min.with_z(bridge.end.z), ramp_b.max.with_z(top)),
792            -bridge.dir,
793        )
794        .fill(rock.clone());
795
796    let len = bridge.dir.select(aabr.size());
797
798    let h = 3 * len.sqrt() / 4;
799
800    let x = len / 2;
801
802    let xsqr = (x * x) as f32;
803    let hsqr = (h * h) as f32;
804    let w = ((xsqr + (xsqr * (4.0 * hsqr + xsqr)).sqrt()) / 2.0)
805        .sqrt()
806        .ceil()
807        + 1.0;
808
809    let bottom = top - (h - (hsqr - hsqr * x as f32 / w).sqrt().ceil() as i32);
810
811    let w = w as i32;
812    let c = aabr.center();
813
814    let cylinder = painter
815        .horizontal_cylinder(
816            aabb(
817                (c - forward * w - side).with_z(bottom),
818                (c + forward * w + side).with_z(bottom + h * 2),
819            ),
820            orth_dir,
821        )
822        .intersect(painter.aabb(aabb(
823            aabr.min.with_z(bottom),
824            aabr.max.with_z(bottom + h * 2),
825        )));
826
827    cylinder.fill(wood.clone());
828
829    cylinder.translate(Vec3::unit_z()).clear();
830
831    let edges = cylinder
832        .without(cylinder.translate(Vec3::unit_z()))
833        .without(painter.aabb(aabb(
834            (c - forward * w - orthogonal * (bridge_width - 1)).with_z(bottom),
835            (c + forward * w + orthogonal * (bridge_width - 1)).with_z(bottom + h * 2),
836        )));
837
838    edges
839        .translate(Vec3::unit_z())
840        .fill(Fill::sprite(SpriteKind::Rope));
841
842    edges.translate(Vec3::unit_z() * 2).fill(wood);
843
844    let column_height = 3;
845    let column_range = top..=top + column_height;
846    painter
847        .column(
848            bridge.dir.select_aabr_with(ramp_f, ramp_f.min),
849            column_range.clone(),
850        )
851        .fill(rock.clone());
852    painter
853        .column(
854            bridge.dir.select_aabr_with(ramp_f, ramp_f.max),
855            column_range.clone(),
856        )
857        .fill(rock.clone());
858    painter
859        .column(
860            (-bridge.dir).select_aabr_with(ramp_b, ramp_b.min),
861            column_range.clone(),
862        )
863        .fill(rock.clone());
864    painter
865        .column(
866            (-bridge.dir).select_aabr_with(ramp_b, ramp_b.max),
867            column_range,
868        )
869        .fill(rock);
870}
871
872pub struct Bridge {
873    /// The original start position in world coords.
874    pub(crate) original_start: Vec2<i32>,
875    /// The original end position in world coords.
876    pub(crate) original_end: Vec2<i32>,
877
878    pub(crate) start: Vec3<i32>,
879    pub(crate) end: Vec3<i32>,
880    pub(crate) dir: Dir,
881    center: Vec3<i32>,
882    kind: BridgeKind,
883    biome: BiomeKind,
884}
885
886impl Bridge {
887    pub fn generate(
888        land: &Land,
889        index: IndexRef,
890        rng: &mut impl Rng,
891        site: &Site,
892        start: Vec2<i32>,
893        end: Vec2<i32>,
894    ) -> Self {
895        let original_start = site.tile_wpos(start);
896        let original_end = site.tile_wpos(end);
897
898        let min_water_dist = 5;
899        let find_edge = |start: Vec2<i32>, end: Vec2<i32>| {
900            let mut test_start = start;
901            let dir = Dir::from_vec2(end - start).to_vec2();
902            let mut last_alt = if let Some(col) = land.column_sample(start, index) {
903                col.alt as i32
904            } else {
905                return (
906                    test_start.with_z(land.get_alt_approx(start) as i32),
907                    i32::MAX,
908                );
909            };
910            let mut step = 0;
911            loop {
912                if let Some(sample) = land.column_sample(test_start + step * dir, index) {
913                    let alt = sample.alt as i32;
914                    let water_dist = sample.water_dist.unwrap_or(16.0) as i32;
915                    if last_alt - alt > 1 + (step + 2) / 3
916                        || sample.riverless_alt - sample.alt > 2.0
917                    {
918                        break (test_start.with_z(last_alt), water_dist);
919                    } else {
920                        test_start += step * dir;
921
922                        if water_dist <= min_water_dist {
923                            break (test_start.with_z(alt), water_dist);
924                        }
925
926                        step = water_dist - min_water_dist;
927
928                        last_alt = alt;
929                    }
930                } else {
931                    break (test_start.with_z(last_alt), i32::MAX);
932                }
933            }
934        };
935
936        let (test_start, start_dist) = find_edge(original_start, original_end);
937
938        let (test_end, end_dist) = find_edge(original_end, original_start);
939
940        let (start, start_dist, end, end_dist) = if test_start.z < test_end.z {
941            (test_start, start_dist, test_end, end_dist)
942        } else {
943            (test_end, end_dist, test_start, start_dist)
944        };
945
946        let center = (start.xy() + end.xy()) / 2;
947        let col = land.column_sample(center, index).unwrap();
948        let center = center.with_z(col.alt as i32);
949        let water_alt = col.water_level as i32;
950        let bridge = BridgeKind::random(rng, start, start_dist, end, end_dist, water_alt);
951        Self {
952            original_start,
953            original_end,
954            start,
955            end,
956            center,
957            dir: Dir::from_vec2(end.xy() - start.xy()),
958            kind: bridge,
959            biome: land
960                .get_chunk_wpos(center.xy())
961                .map_or(BiomeKind::Void, |chunk| chunk.get_biome()),
962        }
963    }
964
965    pub fn width(&self) -> i32 { self.kind.width() }
966}
967
968impl Structure for Bridge {
969    #[cfg(feature = "use-dyn-lib")]
970    const UPDATE_FN: &'static [u8] = b"render_bridge\0";
971
972    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_bridge"))]
973    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
974        match &self.kind {
975            BridgeKind::Flat => render_flat(self, painter),
976            BridgeKind::Tower(roof) => render_tower(self, painter, roof),
977            BridgeKind::Short => render_short(self, painter),
978            BridgeKind::HeightenedViaduct(data) => render_heightened_viaduct(self, painter, data),
979            BridgeKind::HangBridge => render_hang(self, painter),
980        }
981    }
982}