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