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 pub door_tile: Vec2<i32>,
16 bounds: Aabr<i32>,
18 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 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 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 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 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 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 let adjacent_type = if is_corner {
204 RelativeNeighborPosition::L
205 } else {
206 RelativeNeighborPosition::I
207 };
208
209 let ori = if !is_corner {
210 if hit_min_x_bounds || hit_max_x_bounds {
213 2
214 } else {
215 0
216 }
217 } else {
218 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}