veloren_world/site2/plot/
road.rs1use 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 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
34pub 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 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}