veloren_world/site2/plot/
plaza.rs

1use 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
19/// Represents house data generated by the `generate()` method
20pub 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                    // TODO: Not sure if this is always correct
133                    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}