1use crate::site::{Fill, Painter};
2
3use common::{
4 terrain::{
5 Block, SpriteKind,
6 sprite::{MirrorX, Ori},
7 },
8 util::Dir2,
9};
10use enum_map::EnumMap;
11use strum::IntoEnumIterator as _;
12use vek::*;
13
14pub struct Tileable2 {
19 alt: i32,
20 bounds: Aabr<i32>,
21 center: Block,
22 side: EnumMap<Dir2, Block>,
23 corner: EnumMap<Dir2, Block>,
25 rotation: Dir2,
26}
27
28impl Tileable2 {
29 pub fn empty() -> Self {
30 Self {
31 alt: 0,
32 bounds: Aabr::default(),
33 center: Block::empty(),
34 side: EnumMap::from_fn(|_| Block::empty()),
35 corner: EnumMap::from_fn(|_| Block::empty()),
36 rotation: Dir2::X,
37 }
38 }
39
40 pub fn new_sprite(
41 bounds: Aabr<i32>,
42 alt: i32,
43 center: SpriteKind,
44 side: SpriteKind,
45 corner: SpriteKind,
46 ) -> Self {
47 Self::empty()
48 .with_bounds(bounds)
49 .with_alt(alt)
50 .with_center_sprite(center)
51 .with_side_sprite(side)
52 .with_corner_sprite(corner)
53 }
54
55 pub fn two_by(len: i32, pos: Vec3<i32>, dir: Dir2) -> Self {
56 Self::empty()
57 .with_rotation(dir)
58 .with_center_size(pos, Vec2::new(len, 2))
59 }
60
61 pub fn with_center_size(self, center: Vec3<i32>, size: Vec2<i32>) -> Self {
62 let extent_min = (size - 1) / 2;
63 let extent_max = size / 2;
64 let rot = self.rotation;
65 let bounds = Aabr {
66 min: center.xy() - rot.vec2(extent_min.x, extent_min.y),
67 max: center.xy() + rot.vec2(extent_max.x, extent_max.y),
68 }
69 .made_valid();
70 self.with_alt(center.z).with_bounds(bounds)
71 }
72
73 pub fn with_bounds(mut self, bounds: Aabr<i32>) -> Self {
75 self.bounds = bounds;
76 self
77 }
78
79 pub fn with_alt(mut self, alt: i32) -> Self {
80 self.alt = alt;
81 self
82 }
83
84 pub fn with_center(mut self, block: Block) -> Self {
85 self.center = block;
86 self
87 }
88
89 pub fn with_center_sprite(mut self, sprite: SpriteKind) -> Self {
90 self.center = self.center.with_sprite(sprite);
91 self
92 }
93
94 pub fn with_side_sprite(mut self, sprite: SpriteKind) -> Self {
95 for (_, block) in self.side.iter_mut() {
96 *block = block.with_sprite(sprite);
97 }
98 self
99 }
100
101 pub fn with_side(mut self, new_block: Block) -> Self {
102 for (_, block) in self.side.iter_mut() {
103 *block = new_block;
104 }
105 self
106 }
107
108 pub fn with_side_dir(mut self, dir: Dir2, sprite: SpriteKind) -> Self {
109 self.side[dir] = self.side[dir].with_sprite(sprite);
110 self
111 }
112
113 pub fn with_side_axis(self, axis: Dir2, sprite: SpriteKind) -> Self {
114 self.with_side_dir(axis, sprite)
115 .with_side_dir(-axis, sprite)
116 }
117
118 pub fn with_corner_sprite(mut self, sprite: SpriteKind) -> Self {
119 for (_, block) in self.corner.iter_mut() {
120 *block = block.with_sprite(sprite);
121 }
122 self
123 }
124
125 pub fn with_corner_dir(mut self, dir: Dir2, block: Block) -> Self {
127 self.corner[dir] = block;
128 self
129 }
130
131 pub fn with_corner_sprite_dir(mut self, dir: Dir2, sprite: SpriteKind) -> Self {
133 self.corner[dir] = self.corner[dir].with_sprite(sprite);
134 self
135 }
136
137 pub fn with_corner_side(self, axis: Dir2, sprite: Block) -> Self {
138 self.with_corner_dir(axis, sprite)
139 .with_corner_dir(axis.rotated_ccw(), sprite)
140 }
141
142 pub fn with_corner_sprite_side(self, axis: Dir2, sprite: SpriteKind) -> Self {
143 self.with_corner_sprite_dir(axis, sprite)
144 .with_corner_sprite_dir(axis.rotated_ccw(), sprite)
145 }
146
147 pub fn with_rotation(mut self, dir: Dir2) -> Self {
148 self.rotation = dir;
149 self
150 }
151
152 pub fn bounds(&self) -> Aabr<i32> { self.bounds }
153
154 pub fn size(&self) -> Extent2<i32> { self.bounds.size() + 1 }
155
156 pub fn center(&self) -> Block { self.center }
157
158 pub fn side(&self, dir: Dir2) -> Block { self.side[dir.relative_to(self.rotation)] }
159
160 pub fn corner(&self, dir: Dir2) -> Block { self.corner[dir.relative_to(self.rotation)] }
161}
162
163fn single_block(painter: &Painter, pos: Vec3<i32>, block: Block) {
164 painter
165 .aabb(Aabb {
166 min: pos,
167 max: pos + 1,
168 })
169 .fill(Fill::Sprite(block))
170}
171
172fn ori_mirror(mut block: Block, dir: Dir2, x: bool, y: bool) -> Block {
175 let dir_res = block.get_attr::<Ori>().map(|old_ori| {
176 let (old_dir, offset) =
177 Dir2::from_sprite_ori(old_ori.0).expect("We got this from the Ori attr");
178 let new_dir = dir.relative_to(old_dir);
179 Ori(new_dir.sprite_ori() + offset)
180 });
181 let mirror_x_res = block
182 .get_attr::<MirrorX>()
183 .map(|old_x| MirrorX(old_x.0 ^ x));
184
185 if let (Ok(o), Ok(x)) = (dir_res, mirror_x_res) {
186 if y {
187 block
189 .set_attr(Ori((o.0 + 4) % 8))
190 .expect("We read the attr");
191 block.set_attr(MirrorX(!x.0)).expect("We read the attr");
192 } else {
193 block.set_attr(o).expect("We read the attr");
194 block.set_attr(x).expect("We read the attr");
195 }
196 }
197
198 block
199}
200
201pub trait PainterSpriteExt {
202 fn lanternpost_wood(&self, pos: Vec3<i32>, dir: Dir2);
203
204 fn bed(
205 &self,
206 pos: Vec3<i32>,
207 dir: Dir2,
208 head: SpriteKind,
209 middle: SpriteKind,
210 tail: SpriteKind,
211 ) -> Aabr<i32> {
212 let bed = Tileable2::two_by(3, pos, dir)
213 .with_corner_sprite_side(Dir2::Y, head)
214 .with_corner_sprite_side(Dir2::NegY, tail)
215 .with_side_sprite(middle);
216 self.tileable2(&bed);
217
218 bed.bounds()
219 }
220
221 fn bed_wood_woodland(&self, pos: Vec3<i32>, dir: Dir2) -> Aabr<i32> {
222 self.bed(
223 pos,
224 dir,
225 SpriteKind::BedWoodWoodlandHead,
226 SpriteKind::BedWoodWoodlandMiddle,
227 SpriteKind::BedWoodWoodlandTail,
228 )
229 }
230
231 fn bed_desert(&self, pos: Vec3<i32>, dir: Dir2) -> Aabr<i32> {
232 self.bed(
233 pos,
234 dir,
235 SpriteKind::BedDesertHead,
236 SpriteKind::BedDesertMiddle,
237 SpriteKind::BedDesertTail,
238 )
239 }
240
241 fn bed_cliff(&self, pos: Vec3<i32>, dir: Dir2) -> Aabr<i32> {
242 self.bed(
243 pos,
244 dir,
245 SpriteKind::BedCliffHead,
246 SpriteKind::BedCliffMiddle,
247 SpriteKind::BedCliffTail,
248 )
249 }
250
251 fn bed_savannah(&self, pos: Vec3<i32>, dir: Dir2) -> Aabr<i32> {
252 self.bed(
253 pos,
254 dir,
255 SpriteKind::BedSavannahHead,
256 SpriteKind::BedSavannahMiddle,
257 SpriteKind::BedSavannahTail,
258 )
259 }
260
261 fn bed_coastal(&self, pos: Vec3<i32>, dir: Dir2) -> Aabr<i32> {
262 self.bed(
263 pos,
264 dir,
265 SpriteKind::BedCoastalHead,
266 SpriteKind::BedCoastalMiddle,
267 SpriteKind::BedCoastalTail,
268 )
269 }
270
271 fn table_wood_fancy_woodland(&self, pos: Vec3<i32>, axis: Dir2) -> Aabr<i32> {
272 let table = Tileable2::two_by(3, pos, axis)
273 .with_side_sprite(SpriteKind::TableWoodFancyWoodlandBody)
274 .with_corner_sprite(SpriteKind::TableWoodFancyWoodlandCorner);
275
276 self.tileable2(&table);
277
278 table.bounds()
279 }
280
281 fn chairs_around(&self, chair: SpriteKind, spacing: usize, bounds: Aabr<i32>, alt: i32);
283
284 fn tileable1(
288 &self,
289 pos: Vec3<i32>,
290 dir: Dir2,
291 size: i32,
292 middle_sprite: SpriteKind,
293 side_sprite: SpriteKind,
294 );
295
296 fn mirrored2(&self, pos: Vec3<i32>, dir: Dir2, sprite: SpriteKind) {
298 self.tileable1(pos, dir, 2, SpriteKind::Empty, sprite);
299 }
300
301 fn tileable2(&self, tileable: &Tileable2);
305}
306
307impl PainterSpriteExt for Painter {
308 fn lanternpost_wood(&self, pos: Vec3<i32>, dir: Dir2) {
309 let sprite_ori = dir.sprite_ori();
310 self.rotated_sprite(pos, SpriteKind::LanternpostWoodBase, sprite_ori);
311 self.column(pos.xy(), pos.z + 1..pos.z + 4).clear();
312 self.rotated_sprite(
313 pos + Vec3::unit_z() * 3,
314 SpriteKind::LanternpostWoodUpper,
315 sprite_ori,
316 );
317 self.rotated_sprite(
318 pos + dir.to_vec3() + Vec3::unit_z() * 3,
319 SpriteKind::LanternpostWoodLantern,
320 sprite_ori,
321 );
322 }
323
324 fn chairs_around(&self, chair: SpriteKind, spacing: usize, bounds: Aabr<i32>, alt: i32) {
325 for dir in Dir2::iter() {
326 let s = dir.orthogonal().select(bounds.size());
327 if s <= 2 && dir.select(bounds.size()) > s {
329 continue;
330 }
331
332 let min = dir.orthogonal().select(bounds.min);
333 let max = dir.orthogonal().select(bounds.max);
334 let center = dir.orthogonal().select(bounds.center());
335 for i in (min..=center)
336 .step_by(spacing + 1)
337 .chain((center..=max).rev().step_by(spacing + 1))
338 {
339 let p = dir.select_aabr_with(bounds, i) + dir.to_vec2();
340 single_block(
341 self,
342 p.with_z(alt),
343 Block::air(chair)
344 .with_attr(Ori(dir.opposite().sprite_ori()))
345 .expect("Chairs should have the Ori attribute"),
346 );
347 }
348 }
349 }
350
351 fn tileable1(
352 &self,
353 pos: Vec3<i32>,
354 dir: Dir2,
355 size: i32,
356 middle_sprite: SpriteKind,
357 side_sprite: SpriteKind,
358 ) {
359 if size < 2 {
360 return;
361 }
362 let orth = dir.rotated_ccw();
363
364 let extent_min = (size - 1) / 2;
365 let extent_max = size / 2;
366
367 let aabr = Aabr {
368 min: pos.xy() - orth.to_vec2() * extent_min,
369 max: pos.xy() + orth.to_vec2() * extent_max,
370 }
371 .made_valid();
372
373 if size > 2 {
374 self.aabb(Aabb {
375 min: (aabr.min + orth.abs().to_vec2()).with_z(pos.z),
376 max: (aabr.max - orth.abs().to_vec2()).with_z(pos.z) + 1,
377 })
378 .fill(Fill::Sprite(ori_mirror(
379 Block::air(middle_sprite),
380 dir,
381 false,
382 false,
383 )));
384 }
385
386 single_block(
387 self,
388 aabr.min.with_z(pos.z),
389 ori_mirror(Block::air(side_sprite), dir, false, orth.is_negative()),
390 );
391
392 single_block(
393 self,
394 aabr.max.with_z(pos.z),
395 ori_mirror(Block::air(side_sprite), dir, false, orth.is_positive()),
396 );
397 }
398
399 fn tileable2(&self, tileable: &Tileable2) {
400 let alt = tileable.alt;
401 let bounds = tileable.bounds;
402 let size = tileable.size();
403 if size.reduce_min() < 2 {
404 return;
406 }
407
408 if size.w > 2 && size.h > 2 {
409 self.aabb(Aabb {
410 min: (bounds.min + 1).with_z(alt),
411 max: (bounds.max - 1).with_z(alt) + 1,
412 })
413 .fill(Fill::Sprite(ori_mirror(
414 tileable.center(),
415 tileable.rotation,
416 false,
417 false,
418 )));
419 }
420
421 if size.h > 2 {
422 let rot = Dir2::NegY;
423 self.aabb(Aabb {
424 min: Vec3::new(bounds.min.x, bounds.min.y + 1, alt),
425 max: Vec3::new(bounds.min.x, bounds.max.y - 1, alt) + 1,
426 })
427 .fill(Fill::Sprite(ori_mirror(
428 tileable.side(Dir2::NegX),
429 rot,
430 false,
431 false,
432 )));
433
434 self.aabb(Aabb {
435 min: Vec3::new(bounds.max.x, bounds.min.y + 1, alt),
436 max: Vec3::new(bounds.max.x, bounds.max.y - 1, alt) + 1,
437 })
438 .fill(Fill::Sprite(ori_mirror(
439 tileable.side(Dir2::X),
440 rot,
441 false,
442 true,
444 )));
445 }
446
447 if size.w > 2 {
448 let rot = Dir2::X;
449 self.aabb(Aabb {
450 min: Vec3::new(bounds.min.x + 1, bounds.min.y, alt),
451 max: Vec3::new(bounds.max.x - 1, bounds.min.y, alt) + 1,
452 })
453 .fill(Fill::Sprite(ori_mirror(
454 tileable.side(Dir2::NegY),
455 rot,
456 false,
457 false,
458 )));
459
460 self.aabb(Aabb {
461 min: Vec3::new(bounds.min.x + 1, bounds.max.y, alt),
462 max: Vec3::new(bounds.max.x - 1, bounds.max.y, alt) + 1,
463 })
464 .fill(Fill::Sprite(ori_mirror(
465 tileable.side(Dir2::Y),
466 rot,
467 false,
468 true,
469 )));
470 }
471
472 let rot = tileable.rotation;
473 let orth = rot.rotated_ccw();
474 single_block(
475 self,
476 bounds.min.with_z(alt),
477 ori_mirror(
478 tileable.corner(Dir2::NegX),
479 rot,
480 rot.is_negative(),
481 orth.is_negative(),
482 ),
483 );
484 single_block(
485 self,
486 Vec3::new(bounds.max.x, bounds.min.y, alt),
487 ori_mirror(
488 tileable.corner(Dir2::NegY),
489 rot,
490 orth.is_positive(),
491 rot.is_negative(),
492 ),
493 );
494 single_block(
495 self,
496 Vec3::new(bounds.min.x, bounds.max.y, alt),
497 ori_mirror(
498 tileable.corner(Dir2::Y),
499 rot,
500 orth.is_negative(),
501 rot.is_positive(),
502 ),
503 );
504 single_block(
505 self,
506 bounds.max.with_z(alt),
507 ori_mirror(
508 tileable.corner(Dir2::X),
509 rot,
510 rot.is_positive(),
511 orth.is_positive(),
512 ),
513 );
514 }
515}