veloren_world/site/plot/
barn.rs

1use super::*;
2use crate::{
3    ColumnSample, Land,
4    site::{r#gen::PrimitiveTransform, util::gradient::WrapMode},
5    util::{RandomField, Sampler},
6};
7use common::terrain::{
8    Block, BlockKind, SpriteKind, Structure as PrefabStructure, sprite::RelativeNeighborPosition,
9};
10use rand::prelude::*;
11use vek::*;
12
13pub struct Barn {
14    /// Tile position of the door tile
15    pub door_tile: Vec2<i32>,
16    /// Axis aligned bounding region for the house
17    bounds: Aabr<i32>,
18    /// Approximate altitude of the door tile
19    pub(crate) alt: i32,
20    is_desert: bool,
21    surface_color: Rgb<f32>,
22    sub_surface_color: Rgb<f32>,
23}
24
25impl Barn {
26    pub fn generate(
27        land: &Land,
28        index: IndexRef,
29        _rng: &mut impl Rng,
30        site: &Site,
31        door_tile: Vec2<i32>,
32        door_dir: Vec2<i32>,
33        tile_aabr: Aabr<i32>,
34        is_desert: bool,
35    ) -> Self {
36        let door_tile_pos = site.tile_center_wpos(door_tile);
37        let bounds = Aabr {
38            min: site.tile_wpos(tile_aabr.min),
39            max: site.tile_wpos(tile_aabr.max),
40        };
41        let (surface_color, sub_surface_color) =
42            if let Some(sample) = land.column_sample(bounds.center(), index) {
43                (sample.surface_color, sample.sub_surface_color)
44            } else {
45                (Rgb::new(161.0, 116.0, 86.0), Rgb::new(88.0, 64.0, 64.0))
46            };
47
48        Self {
49            door_tile: door_tile_pos,
50            bounds,
51            alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32 + 2,
52            is_desert,
53            surface_color,
54            sub_surface_color,
55        }
56    }
57}
58
59impl Structure for Barn {
60    #[cfg(feature = "use-dyn-lib")]
61    const UPDATE_FN: &'static [u8] = b"render_barn\0";
62
63    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_barn"))]
64    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
65        let base = self.alt;
66        let plot_center = self.bounds.center();
67
68        // blend the tiles below the barn with neighboring tiles for a more
69        // natural look, this is roughly similar to the way cliff_tower does it
70        let surface_color = self.surface_color.map(|e| (e * 255.0) as u8);
71        let sub_surface_color = self.sub_surface_color.map(|e| (e * 255.0) as u8);
72        let gradient_center = Vec3::new(
73            plot_center.x as f32,
74            plot_center.y as f32,
75            (base - 1) as f32,
76        );
77        let gradient_var_1 = RandomField::new(0).get(plot_center.with_z(base - 1)) as i32 % 8;
78        let gradient_var_2 = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 10;
79
80        let brick = Fill::Gradient(
81            util::gradient::Gradient::new(
82                gradient_center,
83                12.0 + gradient_var_1 as f32,
84                util::gradient::Shape::Point,
85                (surface_color, sub_surface_color),
86            )
87            .with_repeat(if gradient_var_2 > 5 {
88                WrapMode::Repeat
89            } else {
90                WrapMode::PingPong
91            }),
92            BlockKind::Rock,
93        );
94
95        let barn_path = "site_structures.plot_structures.barn";
96        let asset_handle = PrefabStructure::load_group(barn_path);
97        let barn_prefab_structure = asset_handle.read()[0].clone();
98        let barn_prefab_structure_bounds = barn_prefab_structure.get_bounds();
99        let barn_length = barn_prefab_structure_bounds.max.x - barn_prefab_structure_bounds.min.x;
100        let barn_half_length = barn_length / 2;
101        let barn_width = barn_prefab_structure_bounds.max.y - barn_prefab_structure_bounds.min.y;
102        let barn_half_width = barn_width / 2;
103        let barn_height = barn_prefab_structure_bounds.max.z - barn_prefab_structure_bounds.min.z;
104
105        painter
106            .aabb(Aabb {
107                min: Vec2::new(
108                    plot_center.x - barn_half_length,
109                    plot_center.y - barn_half_width,
110                )
111                .with_z(base - 10),
112                max: Vec2::new(
113                    plot_center.x + barn_half_length,
114                    plot_center.y + barn_half_width,
115                )
116                .with_z(base - 1),
117            })
118            .fill(brick.clone());
119
120        // air to clear any hills that appear inside of the the barn
121        let air = Fill::Block(Block::new(BlockKind::Air, Rgb::new(255, 255, 255)));
122
123        painter
124            .aabb(Aabb {
125                min: Vec2::new(
126                    plot_center.x - barn_half_length,
127                    plot_center.y - barn_half_width,
128                )
129                .with_z(base),
130                max: Vec2::new(
131                    plot_center.x + barn_half_length,
132                    plot_center.y + barn_half_width,
133                )
134                .with_z(base + barn_height),
135            })
136            .fill(air);
137
138        // barn prefab
139        let entrance_pos: Vec3<i32> = (plot_center.x, plot_center.y, self.alt).into();
140        let barn_site_pos: Vec3<i32> = entrance_pos + Vec3::new(0, 0, -1);
141
142        // Render the prefab
143        painter
144            .prim(Primitive::Prefab(Box::new(barn_prefab_structure.clone())))
145            .translate(barn_site_pos)
146            .fill(Fill::Prefab(
147                Box::new(barn_prefab_structure),
148                barn_site_pos,
149                0,
150            ));
151    }
152
153    fn terrain_surface_at<R: Rng>(
154        &self,
155        wpos: Vec2<i32>,
156        old: Block,
157        _rng: &mut R,
158        col: &ColumnSample,
159        z_off: i32,
160        _site: &Site,
161    ) -> Option<Block> {
162        let hit_min_x_bounds = wpos.x == self.bounds.min.x;
163        let hit_min_y_bounds = wpos.y == self.bounds.min.y;
164        let hit_max_x_bounds = wpos.x == self.bounds.max.x - 1;
165        let hit_max_y_bounds = wpos.y == self.bounds.max.y - 1;
166
167        let is_bounds =
168            hit_min_x_bounds || hit_min_y_bounds || hit_max_x_bounds || hit_max_y_bounds;
169
170        let is_corner = (hit_max_y_bounds || hit_min_y_bounds)
171            && (hit_max_x_bounds || hit_min_x_bounds)
172            && is_bounds;
173
174        if z_off == 0 {
175            // soil
176            Some(Block::new(
177                if self.is_desert {
178                    BlockKind::Sand
179                } else {
180                    BlockKind::Grass
181                },
182                (Lerp::lerp(
183                    col.surface_color,
184                    col.sub_surface_color * 0.5,
185                    false as i32 as f32,
186                ) * 255.0)
187                    .as_(),
188            ))
189        } else if z_off == 1 && is_bounds {
190            // fence
191            let adjacent_type = if is_corner {
192                RelativeNeighborPosition::L
193            } else {
194                RelativeNeighborPosition::I
195            };
196
197            let ori = if !is_corner {
198                // for straight - "I"
199                // can only go in the vertical or horizontal direction
200                if hit_min_x_bounds || hit_max_x_bounds {
201                    2
202                } else {
203                    0
204                }
205            } else {
206                // for corners - "L"
207                // can be rotated in 4 different directions
208                if hit_min_x_bounds && hit_min_y_bounds {
209                    4
210                } else if hit_max_x_bounds && hit_min_y_bounds {
211                    6
212                } else if hit_min_x_bounds && hit_max_y_bounds {
213                    2
214                } else {
215                    0
216                }
217            };
218
219            Some(
220                old.into_vacant()
221                    .with_sprite(SpriteKind::FenceWoodWoodland)
222                    .with_ori(ori)
223                    .unwrap()
224                    .with_adjacent_type(adjacent_type)
225                    .unwrap(),
226            )
227        } else {
228            None
229        }
230    }
231}