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