1use super::*;
2use crate::Land;
3use common::{
4 calendar::Calendar,
5 terrain::{Block, BlockKind},
6};
7use rand::prelude::*;
8use vek::*;
9
10struct Wing {
23 origin: Vec2<i32>,
24 dir: Dir2,
25 dim: Vec2<i32>,
27 children: Vec<Wing>,
28}
29
30impl Wing {
31 fn aabr(&self) -> Aabr<i32> {
32 let p = self.origin
33 + self.dir.rotated_cw().to_vec2() * self.dim.x
34 + self.dir.to_vec2() * self.dim.y;
35 Aabr {
36 min: Vec2::min(self.origin, p),
37 max: Vec2::max(self.origin, p),
38 }
39 }
40}
41
42struct Room {
43 #[allow(dead_code)]
44 aabb: Aabb<i32>,
45}
46
47pub struct Building {
49 pub door_tile: Vec2<i32>,
51 tile_aabr: Aabr<i32>,
53 #[allow(dead_code)]
55 bounds: Aabr<i32>,
56 pub(crate) alt: i32,
58 #[allow(dead_code)]
59 rooms: Vec<Room>,
60 base: Wing,
61}
62
63impl Building {
64 pub fn generate(
65 land: &Land,
66 rng: &mut impl Rng,
67 site: &Site,
68 door_tile: Vec2<i32>,
69 door_dir: Vec2<i32>,
70 tile_aabr: Aabr<i32>,
71 _calendar: Option<&Calendar>,
72 alt: Option<i32>,
73 ) -> Self {
74 let mut rooms = Vec::new();
75
76 for _ in 0..rng.random_range(2..5) {
77 let min = tile_aabr
78 .min
79 .map2(tile_aabr.max, |min, max| rng.random_range(min..max));
80 let max = min.map2(tile_aabr.max, |min, max| rng.random_range(min..max) + 1);
81 rooms.push(Room {
82 aabb: Aabb {
83 min: min.with_z(0),
84 max: max.with_z(rng.random_range(1..4)),
85 },
86 });
87 }
88
89 fn generate_wing(parent: &mut Wing, _limits: Aabr<i32>, rng: &mut impl Rng) {
90 if rng.random_bool(0.5) && parent.dim.y >= 3 {
91 let w = rng.random_range(1..=parent.dim.y);
92 let is_left = rng.random_bool(0.5);
93 let offset = rng.random_range(0..=parent.dim.y - w);
94 let mut wing = Wing {
95 dir: if is_left {
96 parent.dir.rotated_cw()
97 } else {
98 parent.dir.rotated_ccw()
99 },
100 origin: parent.origin
101 + if is_left {
102 Vec2::new(-1, 0)
103 } else {
104 parent.dir.rotated_cw().to_vec2() * parent.dim.x
105 }
106 + parent.dir.to_vec2()
107 * if is_left {
108 offset
109 } else {
110 parent.dim.y - 1 - offset
111 },
112 dim: Vec2::new(w, parent.dim.x + rng.random_range(1..3)),
113 children: Vec::new(),
114 };
115 generate_wing(&mut wing, _limits, rng);
116 parent.children.push(wing);
117 }
118 }
119
120 let mut base = Wing {
121 dir: Dir2::Y,
122 dim: Vec2::new(2, 3),
123 origin: Vec2::new((tile_aabr.min.x + tile_aabr.max.x) / 2, tile_aabr.min.y),
124 children: Vec::new(),
125 };
126 generate_wing(&mut base, tile_aabr, rng);
127
128 Self {
129 door_tile,
130 tile_aabr,
131 bounds: Aabr {
132 min: site.tile_wpos(tile_aabr.min),
133 max: site.tile_wpos(tile_aabr.max),
134 },
135 alt: alt.unwrap_or_else(|| {
136 land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
137 }),
138 rooms,
139 base,
140 }
141 }
142
143 fn walk_wing(
144 &self,
145 wing: &Wing,
146 site: &Site,
147 painter: &Painter,
148 _interior: &mut PrimitiveRef,
149 body: &mut PrimitiveRef,
150 roof: &mut PrimitiveRef,
151 depth: i32,
152 ) {
153 let aabr = wing.aabr();
154 let aabb = Aabb {
155 min: site.tile_wpos(aabr.min).with_z(self.alt),
156 max: site.tile_wpos(aabr.max).with_z(self.alt + 10 - depth * 3) + 1,
157 };
158 *body = body.union(painter.aabb(aabb));
159 *roof = roof.union(
160 painter.vault(
161 Aabb {
162 min: aabb.min.xy().with_z(aabb.max.z),
163 max: aabb
164 .max
165 .xy()
166 .with_z(aabb.max.z + wing.dim.x * TILE_SIZE as i32 / 2),
167 },
168 wing.dir,
169 ),
170 );
171 for child in &wing.children {
172 self.walk_wing(child, site, painter, _interior, body, roof, depth + 1);
173 }
174 }
175}
176
177impl Structure for Building {
178 #[cfg(feature = "dyn-lib")]
179 #[unsafe(export_name = "as_dyn_structure_building")]
180 fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
181 Some((Self::as_dyn_impl(self), "as_dyn_structure_building"))
182 }
183
184 fn spawn_rules_inner(
185 &self,
186 spawn_rules: &mut SpawnRules,
187 _land: &Land,
188 _wpos: Vec2<i32>,
189 weight: f32,
190 ) {
191 spawn_rules.prefer_alt(self.alt as f32, weight * 1.0);
192 }
193
194 fn render_inner(&self, site: &Site, _land: &Land, painter: &Painter) {
195 const ROOM_HEIGHT: i32 = 5;
196 let mut interior = painter.empty();
197 let mut body = painter.empty();
198 let mut frame = painter.empty();
199 let mut roof = painter.empty();
200
201 self.walk_wing(
202 &self.base,
203 site,
204 painter,
205 &mut interior,
206 &mut body,
207 &mut roof,
208 0,
209 );
210
211 for room in &self.rooms {
212 let min = site
213 .tile_wpos(room.aabb.min.xy())
214 .with_z(self.alt + room.aabb.min.z * ROOM_HEIGHT);
215 let max = site
216 .tile_wpos(room.aabb.max.xy())
217 .with_z(self.alt + room.aabb.max.z * ROOM_HEIGHT)
218 + 1;
219
220 const EAVES: i32 = 1;
221
222 interior = interior.union(painter.aabb(Aabb {
224 min: min + 1,
225 max: max - 1,
226 }));
227
228 body = body.union(painter.aabb(Aabb { min, max }));
230
231 roof = roof.union(
233 painter.pyramid(Aabb {
234 min: (min.xy() - EAVES).with_z(max.z),
235 max: (max.xy() + EAVES)
236 .with_z(max.z + (max.xy() - min.xy()).reduce_min() + EAVES * 2),
237 }),
238 );
239
240 for tile in util::aabb_iter(room.aabb) {
242 let min = site
243 .tile_wpos(tile.xy())
244 .with_z(self.alt + tile.z * ROOM_HEIGHT);
245 let max = min + Vec2::broadcast(TILE_SIZE as i32).with_z(ROOM_HEIGHT) + 1;
246 frame = frame.union(
247 painter
248 .aabb(Aabb { min, max })
249 .without(painter.aabb(Aabb {
250 min: min + 1 - Vec3::unit_x(),
251 max: max - 1 + Vec3::unit_x(),
252 }))
253 .without(painter.aabb(Aabb {
254 min: min + 1 - Vec3::unit_y(),
255 max: max - 1 + Vec3::unit_y(),
256 }))
257 .without(painter.aabb(Aabb {
258 min: min + 1 - Vec3::unit_z(),
259 max: max - 1 + Vec3::unit_z(),
260 })),
261 );
262 }
263 }
264
265 painter
267 .aabb(Aabb {
268 min: (site.tile_wpos(self.tile_aabr.min) - 1).with_z(self.alt - 10),
269 max: (site.tile_wpos(self.tile_aabr.max) + 2).with_z(self.alt),
270 })
271 .fill(Fill::Block(Block::new(
272 BlockKind::Rock,
273 Rgb::new(50, 50, 60),
274 )));
275 body.without(interior).fill(Fill::Block(Block::new(
277 BlockKind::Wood,
278 Rgb::new(180, 120, 70),
279 )));
280 roof.fill(Fill::Block(Block::new(
282 BlockKind::Wood,
283 Rgb::new(100, 30, 10),
284 )));
285 frame.fill(Fill::Block(Block::new(
287 BlockKind::Wood,
288 Rgb::new(100, 70, 50),
289 )));
290 painter
292 .aabb(Aabb {
293 min: (site.tile_wpos(self.door_tile)).with_z(self.alt),
294 max: (site.tile_wpos(self.door_tile + 1)).with_z(self.alt + 10),
295 })
296 .fill(Fill::Block(Block::new(
297 BlockKind::Wood,
298 Rgb::new(50, 70, 50),
299 )));
300 }
301}