veloren_world/site2/plot/
plaza.rs1use super::*;
2use crate::{ColumnSample, Land};
3use common::terrain::{Block, BlockKind};
4use enum_map::EnumMap;
5use rand::prelude::*;
6use strum::IntoEnumIterator;
7use vek::*;
8
9#[derive(Default)]
10struct CornerMeta {
11 water_alt: i32,
12 alt: i32,
13}
14
15impl CornerMeta {
16 fn water(&self) -> bool { self.alt < self.water_alt }
17}
18
19pub struct Plaza {
21 pub aabr: Aabr<i32>,
22 pub kind: RoadKind,
23 corner_meta: EnumMap<Dir, CornerMeta>,
24 pub hard_alt: Option<i32>,
25}
26
27impl Plaza {
28 pub fn generate(
29 tile_aabr: Aabr<i32>,
30 kind: RoadKind,
31 site: &Site,
32 land: &Land,
33 index: IndexRef,
34 ) -> Self {
35 let aabr = Aabr {
36 min: site.tile_wpos(tile_aabr.min),
37 max: site.tile_wpos(tile_aabr.max),
38 };
39 let mut iaabr = aabr;
40 iaabr.max -= 1;
41
42 let get_corner_meta = |wpos| {
43 land.column_sample(wpos, index)
44 .map(|col| CornerMeta {
45 water_alt: col.water_level as i32,
46 alt: col.alt as i32,
47 })
48 .unwrap_or_default()
49 };
50
51 let center = get_corner_meta(iaabr.center());
52
53 let corner_meta: EnumMap<Dir, CornerMeta> = Dir::iter()
54 .map(|d| {
55 let o = d.rotated_cw();
56 let pos = d.select_aabr_with(iaabr, o.select_aabr(iaabr));
57 (d, get_corner_meta(pos))
58 })
59 .collect();
60
61 let any_water = center.water() || corner_meta.values().any(|c| c.water());
62
63 Self {
64 aabr,
65 kind,
66 corner_meta,
67 hard_alt: if any_water {
68 Some((land.get_alt_approx(aabr.center()) as i32).max(center.water_alt + 1))
69 } else {
70 None
71 },
72 }
73 }
74}
75
76impl Structure for Plaza {
77 #[cfg(feature = "use-dyn-lib")]
78 const UPDATE_FN: &'static [u8] = b"render_plaza\0";
79
80 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_plaza")]
81 fn render_inner(&self, site: &Site, land: &Land, painter: &Painter) {
82 if let Some(alt) = self.hard_alt {
83 let wood_corner = Fill::Brick(BlockKind::Wood, Rgb::new(86, 50, 50), 10);
84 let bounds = Aabb {
85 min: self.aabr.min.with_z(alt),
86 max: self.aabr.max.with_z(alt + 1),
87 };
88 painter.aabb(bounds).fill(wood_corner.clone());
89 let mut iaabr = self.aabr;
90 let hsize = iaabr.half_size();
91 iaabr.min += 1;
92 iaabr.max -= 2;
93 for (d, meta) in self.corner_meta.iter() {
94 let o = d.rotated_cw();
95
96 let corner = d.select_aabr_with(iaabr, o.select_aabr(iaabr));
97
98 painter
99 .column(corner, meta.alt - 5..alt)
100 .fill(wood_corner.clone());
101
102 for (dir, index) in [(d, d.rotated_ccw()), (o, o)] {
103 let dp = -dir;
104 let min_alt = meta.alt.min(self.corner_meta[d.relative_to(index)].alt);
105 for i in (0..dp.select(hsize)).step_by(8) {
106 let p = corner + dp.to_vec2() * i;
107 painter
108 .column(p, min_alt - 5..alt)
109 .fill(wood_corner.clone());
110 }
111 }
112 }
113 }
114
115 let tile_aabr = Aabr {
116 min: site.wpos_tile_pos(self.aabr.min),
117 max: site.wpos_tile_pos(self.aabr.max) - 1,
118 };
119
120 for dir in Dir::iter() {
121 let orth = dir.orthogonal();
122
123 for i in (orth.select(tile_aabr.min) + 1)..orth.select(tile_aabr.max) {
124 let tpos = dir.select_aabr_with(tile_aabr, i);
125 if (tpos.x + tpos.y) % 3 != 0 {
126 continue;
127 }
128
129 if site.tiles.get(tpos + dir.to_vec2()).is_empty() {
130 let wpos = site.tile_center_wpos(tpos);
131
132 let alt = self
134 .hard_alt
135 .unwrap_or_else(|| land.get_alt_approx(wpos) as i32)
136 + 1;
137 let wpos = wpos.with_z(alt);
138 self.kind.place_light(wpos, -dir, painter);
139 }
140 }
141 }
142
143 let rng = &mut thread_rng();
144 if rng.gen_bool(0.05) {
145 let spec = [
146 "common.entity.wild.peaceful.cat",
147 "common.entity.wild.peaceful.dog",
148 ]
149 .choose(rng)
150 .unwrap();
151 let center = self.aabr.center();
152 painter.spawn(
153 EntityInfo::at(
154 Vec3::new(center.x, center.y, land.get_alt_approx(center) as i32).as_(),
155 )
156 .with_asset_expect(spec, rng, None)
157 .with_alignment(Alignment::Tame),
158 );
159 }
160 }
161
162 fn rel_terrain_offset(&self, col: &ColumnSample) -> i32 { col.riverless_alt as i32 }
163
164 fn terrain_surface_at<R: Rng>(
165 &self,
166 _wpos: Vec2<i32>,
167 old: Block,
168 _rng: &mut R,
169 col: &ColumnSample,
170 z_off: i32,
171 _site: &Site,
172 ) -> Option<Block> {
173 let z = self.rel_terrain_offset(col) + z_off;
174 if col.water_level > col.alt || self.hard_alt.is_some_and(|alt| z < alt) {
175 return None;
176 };
177 if z_off <= 0 {
178 let sub_surface_color = col.sub_surface_color * 0.5;
179
180 let block = Block::new(BlockKind::Earth, (sub_surface_color * 255.0).as_());
181 if old.is_filled() {
182 if old.is_terrain() { Some(block) } else { None }
183 } else if self.hard_alt.is_none() {
184 Some(block)
185 } else {
186 None
187 }
188 } else if old.is_fluid() || old.kind() == BlockKind::Snow || old.is_terrain() {
189 Some(old.into_vacant())
190 } else {
191 None
192 }
193 }
194}