Skip to main content

veloren_world/site/plot/
barn.rs

1use super::*;
2use crate::{
3    ColumnSample, Land,
4    site::{generation::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 = "dyn-lib")]
61    #[unsafe(export_name = "as_dyn_structure_barn")]
62    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
63        Some((Self::as_dyn_impl(self), "as_dyn_structure_barn"))
64    }
65
66    fn spawn_rules_inner(
67        &self,
68        spawn_rules: &mut SpawnRules,
69        _land: &Land,
70        _wpos: Vec2<i32>,
71        weight: f32,
72    ) {
73        spawn_rules.prefer_alt(self.alt as f32, weight + weight.powi(4) * 25.0);
74    }
75
76    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
77        let base = self.alt;
78        let plot_center = self.bounds.center();
79
80        // blend the tiles below the barn with neighboring tiles for a more
81        // natural look, this is roughly similar to the way cliff_tower does it
82        let surface_color = self.surface_color.map(|e| (e * 255.0) as u8);
83        let sub_surface_color = self.sub_surface_color.map(|e| (e * 255.0) as u8);
84        let gradient_center = Vec3::new(
85            plot_center.x as f32,
86            plot_center.y as f32,
87            (base - 1) as f32,
88        );
89        let gradient_var_1 = RandomField::new(0).get(plot_center.with_z(base - 1)) as i32 % 8;
90        let gradient_var_2 = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 10;
91
92        let brick = Fill::Gradient(
93            util::gradient::Gradient::new(
94                gradient_center,
95                12.0 + gradient_var_1 as f32,
96                util::gradient::Shape::Point,
97                (surface_color, sub_surface_color),
98            )
99            .with_repeat(if gradient_var_2 > 5 {
100                WrapMode::Repeat
101            } else {
102                WrapMode::PingPong
103            }),
104            BlockKind::Rock,
105        );
106
107        let barn_path = "site_structures.plot_structures.barn";
108        let asset_handle = PrefabStructure::load_group(barn_path);
109        let barn_prefab_structure = asset_handle.read()[0].clone();
110        let barn_prefab_structure_bounds = barn_prefab_structure.get_bounds();
111        let barn_length = barn_prefab_structure_bounds.max.x - barn_prefab_structure_bounds.min.x;
112        let barn_half_length = barn_length / 2;
113        let barn_width = barn_prefab_structure_bounds.max.y - barn_prefab_structure_bounds.min.y;
114        let barn_half_width = barn_width / 2;
115        let barn_height = barn_prefab_structure_bounds.max.z - barn_prefab_structure_bounds.min.z;
116
117        painter
118            .aabb(Aabb {
119                min: Vec2::new(
120                    plot_center.x - barn_half_length,
121                    plot_center.y - barn_half_width,
122                )
123                .with_z(base - 10),
124                max: Vec2::new(
125                    plot_center.x + barn_half_length,
126                    plot_center.y + barn_half_width,
127                )
128                .with_z(base - 1),
129            })
130            .fill(brick.clone());
131
132        // air to clear any hills that appear inside of the the barn
133        let air = Fill::Block(Block::new(BlockKind::Air, Rgb::new(255, 255, 255)));
134
135        painter
136            .aabb(Aabb {
137                min: Vec2::new(
138                    plot_center.x - barn_half_length,
139                    plot_center.y - barn_half_width,
140                )
141                .with_z(base),
142                max: Vec2::new(
143                    plot_center.x + barn_half_length,
144                    plot_center.y + barn_half_width,
145                )
146                .with_z(base + barn_height),
147            })
148            .fill(air);
149
150        // barn prefab
151        let entrance_pos: Vec3<i32> = (plot_center.x, plot_center.y, self.alt).into();
152        let barn_site_pos: Vec3<i32> = entrance_pos + Vec3::new(0, 0, -1);
153
154        // Render the prefab
155        painter
156            .prim(Primitive::Prefab(Box::new(barn_prefab_structure.clone())))
157            .translate(barn_site_pos)
158            .fill(Fill::Prefab(
159                Box::new(barn_prefab_structure),
160                barn_site_pos,
161                0,
162            ));
163    }
164
165    fn terrain_surface_at_inner(
166        &self,
167        wpos: Vec2<i32>,
168        old: Block,
169        _rng: &mut ChaCha8Rng,
170        col: &ColumnSample,
171        z_off: i32,
172        _site: &Site,
173    ) -> Option<Block> {
174        let hit_min_x_bounds = wpos.x == self.bounds.min.x;
175        let hit_min_y_bounds = wpos.y == self.bounds.min.y;
176        let hit_max_x_bounds = wpos.x == self.bounds.max.x - 1;
177        let hit_max_y_bounds = wpos.y == self.bounds.max.y - 1;
178
179        let is_bounds =
180            hit_min_x_bounds || hit_min_y_bounds || hit_max_x_bounds || hit_max_y_bounds;
181
182        let is_corner = (hit_max_y_bounds || hit_min_y_bounds)
183            && (hit_max_x_bounds || hit_min_x_bounds)
184            && is_bounds;
185
186        if z_off == 0 {
187            // soil
188            Some(Block::new(
189                if self.is_desert {
190                    BlockKind::Sand
191                } else {
192                    BlockKind::Grass
193                },
194                (Lerp::lerp(
195                    col.surface_color,
196                    col.sub_surface_color * 0.5,
197                    false as i32 as f32,
198                ) * 255.0)
199                    .as_(),
200            ))
201        } else if z_off == 1 && is_bounds {
202            // fence
203            let adjacent_type = if is_corner {
204                RelativeNeighborPosition::L
205            } else {
206                RelativeNeighborPosition::I
207            };
208
209            let ori = if !is_corner {
210                // for straight - "I"
211                // can only go in the vertical or horizontal direction
212                if hit_min_x_bounds || hit_max_x_bounds {
213                    2
214                } else {
215                    0
216                }
217            } else {
218                // for corners - "L"
219                // can be rotated in 4 different directions
220                if hit_min_x_bounds && hit_min_y_bounds {
221                    4
222                } else if hit_max_x_bounds && hit_min_y_bounds {
223                    6
224                } else if hit_min_x_bounds && hit_max_y_bounds {
225                    2
226                } else {
227                    0
228                }
229            };
230
231            Some(
232                old.into_vacant()
233                    .with_sprite(SpriteKind::FenceWoodWoodland)
234                    .with_ori(ori)
235                    .unwrap()
236                    .with_adjacent_type(adjacent_type)
237                    .unwrap(),
238            )
239        } else {
240            None
241        }
242    }
243}