veloren_world/site2/plot/
road.rs

1use super::*;
2use crate::{
3    ColumnSample, Land,
4    util::{LOCALITY, RandomField, Sampler},
5};
6use common::terrain::{Block, BlockKind};
7use enumset::EnumSet;
8use rand::prelude::*;
9use strum::IntoEnumIterator;
10use util::sprites::PainterSpriteExt;
11use vek::*;
12
13#[derive(Clone, Copy, PartialEq, Eq)]
14pub enum RoadKind {
15    Default,
16    Terracotta,
17}
18
19impl RoadKind {
20    /// Intended to be placed at `riverless_alt`.
21    pub fn place_light(&self, pos: Vec3<i32>, dir: Dir, painter: &Painter) {
22        let wood_corner = Fill::Brick(BlockKind::Wood, Rgb::new(86, 50, 50), 10);
23        painter
24            .column(pos.xy(), pos.z - 4..pos.z)
25            .sample_with_column(|p, col| p.z > col.riverless_alt as i32)
26            .fill(wood_corner);
27        match self {
28            RoadKind::Default => painter.lanternpost_wood(pos, dir),
29            RoadKind::Terracotta => painter.sprite(pos, SpriteKind::LampTerracotta),
30        }
31    }
32}
33
34/// Represents house data generated by the `generate()` method
35pub struct Road {
36    pub path: Path<Vec2<i32>>,
37    pub kind: RoadKind,
38}
39
40impl Structure for Road {
41    #[cfg(feature = "use-dyn-lib")]
42    const UPDATE_FN: &'static [u8] = b"render_road\0";
43
44    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_road")]
45    fn render_inner(&self, site: &Site, land: &Land, painter: &Painter) {
46        let field = RandomField::new(76237);
47
48        for p in self.path.iter() {
49            if (p.y + p.x) % 3 != 0 {
50                continue;
51            }
52
53            let current_tile = site.tiles.get(*p);
54            let TileKind::Road {
55                w,
56                a: this_a,
57                b: this_b,
58                ..
59            } = current_tile.kind
60            else {
61                continue;
62            };
63
64            let center = site.tile_center_wpos(*p);
65
66            let light_wpos = |dir: Dir| {
67                let width = w as i32 * 2 - 1 - (dir.signum() + 1) / 2;
68                center + dir.to_vec2() * width
69            };
70            let available_dirs: EnumSet<Dir> = Dir::iter()
71                .filter(|dir| {
72                    let light_wpos = light_wpos(*dir);
73                    let tpos = site.wpos_tile_pos(light_wpos);
74                    [tpos, tpos + dir.to_vec2()].into_iter().all(|tpos| {
75                        let tile = site.tiles.get(tpos);
76                        tile.is_natural()
77                            || if let TileKind::Road { a, b, .. } = tile.kind {
78                                current_tile.plot == tile.plot && this_a == a && this_b == b
79                            } else {
80                                false
81                            }
82                    })
83                })
84                .collect();
85
86            if available_dirs.is_empty() {
87                continue;
88            }
89
90            let i = field.get(p.with_z(11)) as usize % available_dirs.len();
91            let Some(dir) = available_dirs.iter().nth(i) else {
92                continue;
93            };
94
95            let wpos = light_wpos(dir);
96
97            // TODO: Not sure if this is always correct
98            let alt =
99                land.get_alt_approx(wpos)
100                    .max(land.get_interpolated(wpos, |c| c.water_alt) + 1.0) as i32
101                    + 1;
102            let wpos = wpos.with_z(alt);
103            self.kind.place_light(wpos, -dir, painter);
104        }
105    }
106
107    fn rel_terrain_offset(&self, col: &ColumnSample) -> i32 {
108        (col.riverless_alt as i32).max(col.water_level as i32 + 1)
109    }
110
111    fn terrain_surface_at<R: Rng>(
112        &self,
113        wpos: Vec2<i32>,
114        old: Block,
115        _rng: &mut R,
116        col: &ColumnSample,
117        z_off: i32,
118        site: &Site,
119    ) -> Option<Block> {
120        let z = self.rel_terrain_offset(col) + z_off;
121        if col.alt < col.water_level && z < 0 {
122            return None;
123        }
124
125        if z_off <= 0 {
126            let tpos = site.wpos_tile_pos(wpos);
127            let mut near_roads = LOCALITY.iter().filter_map(|rpos| {
128                let tile = site.tiles.get(tpos + rpos);
129                if let TileKind::Road { a, b, w } = &tile.kind {
130                    if let Some(PlotKind::Road(Road { path, .. })) =
131                        tile.plot.map(|p| &site.plot(p).kind)
132                    {
133                        let is_start = *a == 0;
134                        let is_end = *b == path.len() as u16 - 1;
135                        let a = path.nodes()[*a as usize];
136                        let b = path.nodes()[*b as usize];
137                        let path_dir = Dir::from_vec2(b - a);
138                        Some((
139                            LineSegment2 {
140                                start: site.tile_center_wpos(a)
141                                    - if is_start {
142                                        path_dir.to_vec2() * TILE_SIZE as i32 / 2
143                                    } else {
144                                        Vec2::zero()
145                                    },
146                                end: site.tile_center_wpos(b)
147                                    + if is_end {
148                                        path_dir.to_vec2() * TILE_SIZE as i32 / 2
149                                    } else {
150                                        Vec2::zero()
151                                    },
152                            }
153                            .as_(),
154                            *w,
155                        ))
156                    } else {
157                        None
158                    }
159                } else {
160                    None
161                }
162            });
163
164            let wposf = wpos.map(|e| e as f32);
165            if near_roads.any(|(line, w)| line.distance_to_point(wposf) < w as f32 * 2.0) {
166                let sub_surface_color = col.sub_surface_color * 0.5;
167                Some(Block::new(
168                    BlockKind::Earth,
169                    (sub_surface_color * 255.0).as_(),
170                ))
171            } else {
172                None
173            }
174        } else if old.is_fluid() || old.kind() == BlockKind::Snow || old.is_terrain() {
175            Some(old.into_vacant())
176        } else {
177            None
178        }
179    }
180}