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
163pub fn 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 benches_around(
286 &self,
287 bench: SpriteKind,
288 spacing: usize,
289 offset: i32,
290 bounds: Aabr<i32>,
291 alt: i32,
292 );
293
294 fn tileable1(
298 &self,
299 pos: Vec3<i32>,
300 dir: Dir2,
301 size: i32,
302 middle_sprite: SpriteKind,
303 side_sprite: SpriteKind,
304 );
305
306 fn mirrored2(&self, pos: Vec3<i32>, dir: Dir2, sprite: SpriteKind) {
308 self.tileable1(pos, dir, 2, SpriteKind::Empty, sprite);
309 }
310
311 fn tileable2(&self, tileable: &Tileable2);
315}
316
317impl PainterSpriteExt for Painter {
318 fn lanternpost_wood(&self, pos: Vec3<i32>, dir: Dir2) {
319 let sprite_ori = dir.sprite_ori();
320 self.rotated_sprite(pos, SpriteKind::LanternpostWoodBase, sprite_ori);
321 self.column(pos.xy(), pos.z + 1..pos.z + 4).clear();
322 self.rotated_sprite(
323 pos + Vec3::unit_z() * 3,
324 SpriteKind::LanternpostWoodUpper,
325 sprite_ori,
326 );
327 self.rotated_sprite(
328 pos + dir.to_vec3() + Vec3::unit_z() * 3,
329 SpriteKind::LanternpostWoodLantern,
330 sprite_ori,
331 );
332 }
333
334 fn chairs_around(&self, chair: SpriteKind, spacing: usize, bounds: Aabr<i32>, alt: i32) {
335 for dir in Dir2::iter() {
336 let s = dir.orthogonal().select(bounds.size());
337 if s <= 2 && dir.select(bounds.size()) > s {
339 continue;
340 }
341
342 let min = dir.orthogonal().select(bounds.min);
343 let max = dir.orthogonal().select(bounds.max);
344 let center = dir.orthogonal().select(bounds.center());
345 for i in (min..=center)
346 .step_by(spacing + 1)
347 .chain((center..=max).rev().step_by(spacing + 1))
348 {
349 let p = dir.select_aabr_with(bounds, i) + dir.to_vec2();
350 single_block(
351 self,
352 p.with_z(alt),
353 Block::air(chair)
354 .with_attr(Ori(dir.opposite().sprite_ori()))
355 .expect("Chairs should have the Ori attribute"),
356 );
357 }
358 }
359 }
360
361 fn benches_around(
362 &self,
363 bench: SpriteKind,
364 spacing: usize,
365 offset: i32,
366 bounds: Aabr<i32>,
367 alt: i32,
368 ) {
369 for dir in Dir2::iter() {
370 let s = dir.orthogonal().select(bounds.size());
371 if s <= 2 && dir.select(bounds.size()) > s {
373 continue;
374 }
375
376 let min = dir.orthogonal().select(bounds.min);
377 let max = dir.orthogonal().select(bounds.max);
378 let center = dir.orthogonal().select(bounds.center());
379 for i in (min + offset..=center)
380 .step_by(spacing + 2)
381 .chain((center..=max - offset).rev().step_by(spacing + 2))
382 {
383 let p = dir.select_aabr_with(bounds, i) + dir.to_vec2();
384 self.mirrored2(p.with_z(alt), dir, bench);
385 }
386 }
387 }
388
389 fn tileable1(
390 &self,
391 pos: Vec3<i32>,
392 dir: Dir2,
393 size: i32,
394 middle_sprite: SpriteKind,
395 side_sprite: SpriteKind,
396 ) {
397 if size < 2 {
398 return;
399 }
400 let orth = dir.rotated_ccw();
401
402 let extent_min = (size - 1) / 2;
403 let extent_max = size / 2;
404
405 let aabr = Aabr {
406 min: pos.xy() - orth.to_vec2() * extent_min,
407 max: pos.xy() + orth.to_vec2() * extent_max,
408 }
409 .made_valid();
410
411 if size > 2 {
412 self.aabb(Aabb {
413 min: (aabr.min + orth.abs().to_vec2()).with_z(pos.z),
414 max: (aabr.max - orth.abs().to_vec2()).with_z(pos.z) + 1,
415 })
416 .fill(Fill::Sprite(ori_mirror(
417 Block::air(middle_sprite),
418 dir,
419 false,
420 false,
421 )));
422 }
423
424 single_block(
425 self,
426 aabr.min.with_z(pos.z),
427 ori_mirror(Block::air(side_sprite), dir, false, orth.is_negative()),
428 );
429
430 single_block(
431 self,
432 aabr.max.with_z(pos.z),
433 ori_mirror(Block::air(side_sprite), dir, false, orth.is_positive()),
434 );
435 }
436
437 fn tileable2(&self, tileable: &Tileable2) {
438 let alt = tileable.alt;
439 let bounds = tileable.bounds;
440 let size = tileable.size();
441 if size.reduce_min() < 2 {
442 return;
444 }
445
446 if size.w > 2 && size.h > 2 {
447 self.aabb(Aabb {
448 min: (bounds.min + 1).with_z(alt),
449 max: (bounds.max - 1).with_z(alt) + 1,
450 })
451 .fill(Fill::Sprite(ori_mirror(
452 tileable.center(),
453 tileable.rotation,
454 false,
455 false,
456 )));
457 }
458
459 if size.h > 2 {
460 let rot = Dir2::NegY;
461 self.aabb(Aabb {
462 min: Vec3::new(bounds.min.x, bounds.min.y + 1, alt),
463 max: Vec3::new(bounds.min.x, bounds.max.y - 1, alt) + 1,
464 })
465 .fill(Fill::Sprite(ori_mirror(
466 tileable.side(Dir2::NegX),
467 rot,
468 false,
469 false,
470 )));
471
472 self.aabb(Aabb {
473 min: Vec3::new(bounds.max.x, bounds.min.y + 1, alt),
474 max: Vec3::new(bounds.max.x, bounds.max.y - 1, alt) + 1,
475 })
476 .fill(Fill::Sprite(ori_mirror(
477 tileable.side(Dir2::X),
478 rot,
479 false,
480 true,
482 )));
483 }
484
485 if size.w > 2 {
486 let rot = Dir2::X;
487 self.aabb(Aabb {
488 min: Vec3::new(bounds.min.x + 1, bounds.min.y, alt),
489 max: Vec3::new(bounds.max.x - 1, bounds.min.y, alt) + 1,
490 })
491 .fill(Fill::Sprite(ori_mirror(
492 tileable.side(Dir2::NegY),
493 rot,
494 false,
495 false,
496 )));
497
498 self.aabb(Aabb {
499 min: Vec3::new(bounds.min.x + 1, bounds.max.y, alt),
500 max: Vec3::new(bounds.max.x - 1, bounds.max.y, alt) + 1,
501 })
502 .fill(Fill::Sprite(ori_mirror(
503 tileable.side(Dir2::Y),
504 rot,
505 false,
506 true,
507 )));
508 }
509
510 let rot = tileable.rotation;
511 let orth = rot.rotated_ccw();
512 single_block(
513 self,
514 bounds.min.with_z(alt),
515 ori_mirror(
516 tileable.corner(Dir2::NegX),
517 rot,
518 rot.is_negative(),
519 orth.is_negative(),
520 ),
521 );
522 single_block(
523 self,
524 Vec3::new(bounds.max.x, bounds.min.y, alt),
525 ori_mirror(
526 tileable.corner(Dir2::NegY),
527 rot,
528 orth.is_positive(),
529 rot.is_negative(),
530 ),
531 );
532 single_block(
533 self,
534 Vec3::new(bounds.min.x, bounds.max.y, alt),
535 ori_mirror(
536 tileable.corner(Dir2::Y),
537 rot,
538 orth.is_negative(),
539 rot.is_positive(),
540 ),
541 );
542 single_block(
543 self,
544 bounds.max.with_z(alt),
545 ori_mirror(
546 tileable.corner(Dir2::X),
547 rot,
548 rot.is_positive(),
549 orth.is_positive(),
550 ),
551 );
552 }
553}