1use std::{mem::swap, ops::RangeInclusive};
2
3use common::{
4 comp::Content,
5 lottery::Lottery,
6 store::{Id, Store},
7 terrain::{BlockKind, SpriteCfg, SpriteKind},
8};
9use enum_map::EnumMap;
10use enumset::EnumSet;
11use hashbrown::HashSet;
12use rand::{
13 Rng,
14 seq::{IteratorRandom, SliceRandom},
15};
16use strum::{EnumIter, IntoEnumIterator};
17use vek::*;
18
19use crate::{
20 IndexRef, Land,
21 site::namegen,
22 site2::{Dir, Fill, Site, Structure, gen::PrimitiveTransform, util::Dir3},
23 util::RandomField,
24};
25
26type Neighbor = Option<Id<Room>>;
27
28pub struct Wall {
29 start: Vec2<i32>,
30 end: Vec2<i32>,
31 base_alt: i32,
32 top_alt: i32,
33 from: Neighbor,
34 to: Neighbor,
35 to_dir: Dir,
36 door: Option<(i32, i32)>,
37}
38
39impl Wall {
40 pub fn door_pos(&self) -> Option<Vec3<f32>> {
41 let wall_dir = Dir::from_vec2(self.end - self.start);
42
43 self.door.map(|(door_min, door_max)| {
44 (self.start.as_() + wall_dir.to_vec2().as_() * (door_min + door_max) as f32 / 2.0 + 0.5)
45 .with_z(self.base_alt as f32)
46 })
47 }
48
49 pub fn door_bounds(&self) -> Option<Aabr<i32>> {
50 let wall_dir = Dir::from_vec2(self.end - self.start);
51
52 self.door.map(|(door_min, door_max)| {
53 Aabr {
54 min: self.start + wall_dir.to_vec2() * door_min,
55 max: self.start + wall_dir.to_vec2() * door_max,
56 }
57 .made_valid()
58 })
59 }
60}
61
62#[derive(Copy, Clone)]
63enum RoofStyle {
64 Flat,
65 FlatBars { dir: Dir },
66 LeanTo { dir: Dir, max_z: i32 },
67 Gable { dir: Dir, max_z: i32 },
68 Hip { max_z: i32 },
69 Floor,
70}
71
72struct Roof {
73 bounds: Aabr<i32>,
74 min_z: i32,
75 style: RoofStyle,
76 stairs: Option<(Aabb<i32>, Dir)>,
77}
78
79#[derive(Clone, Copy, EnumIter, enum_map::Enum)]
80enum RoomKind {
81 Garden,
82 Stage,
83 Bar,
84 Seating,
85 Entrance,
86 Cellar,
87}
88
89impl RoomKind {
90 fn size_range(&self) -> (RangeInclusive<i32>, RangeInclusive<i32>) {
92 match self {
93 RoomKind::Garden => (5..=20, 35..=250),
94 RoomKind::Seating => (4..=20, 35..=250),
95 RoomKind::Cellar => (6..=12, 35..=110),
96 RoomKind::Stage => (11..=22, 150..=400),
97 RoomKind::Bar => (9..=16, 80..=196),
98 RoomKind::Entrance => (3..=7, 12..=40),
99 }
100 }
101
102 fn chance(&self, room_counts: &EnumMap<RoomKind, u32>) -> f32 {
103 match self {
104 RoomKind::Garden => 0.05 / (1.0 + room_counts[RoomKind::Garden] as f32).powi(2),
105 RoomKind::Seating => 0.4 / (1.0 + room_counts[RoomKind::Seating] as f32),
106 RoomKind::Stage => match room_counts[RoomKind::Stage] {
107 0 => 1.0,
108 _ => 0.0,
109 },
110 RoomKind::Bar => match room_counts[RoomKind::Bar] {
111 0 => 1.0,
112 1 => 0.01,
113 _ => 0.0,
114 },
115 RoomKind::Entrance => 0.0,
116 RoomKind::Cellar => 1.0,
117 }
118 }
119
120 fn fits(&self, max_bounds: Aabr<i32>) -> bool {
121 let max_min_size = max_bounds.size().reduce_min();
123 let max_area = max_bounds.size().product();
125
126 let (size_range, area_range) = self.size_range();
127 *size_range.start() <= max_min_size && *area_range.start() <= max_area
128 }
129
130 fn entrance_room_lottery(temperature: f32) -> Lottery<RoomKind> {
131 let rooms = [
132 (0.5 * temperature, RoomKind::Garden),
133 (2.0, RoomKind::Entrance),
134 ]
135 .into_iter()
136 .filter(|(c, _)| *c > 0.0)
137 .collect::<Vec<_>>();
138
139 Lottery::from(rooms)
140 }
141
142 fn side_room_lottery(
143 &self,
144 max_bounds: Aabr<i32>,
145 room_counts: &EnumMap<RoomKind, u32>,
146 temperature: f32,
147 ) -> Option<Lottery<RoomKind>> {
148 let rooms: &[RoomKind] = match self {
149 RoomKind::Cellar => &[RoomKind::Cellar],
150 _ => &[
151 RoomKind::Stage,
152 RoomKind::Garden,
153 RoomKind::Bar,
154 RoomKind::Seating,
155 ],
156 };
157 let lottery = rooms.iter()
158 .filter(|kind| kind.fits(max_bounds))
160 .map(|room_kind| {
162 let temp_scale = match room_kind {
163 RoomKind::Garden => temperature,
164 _ => 1.0,
165 };
166
167 (
168 room_kind.chance(room_counts) * temp_scale,
169 *room_kind,
170 )
171 })
172 .filter(|(c, _)| *c > 0.0)
173 .collect::<Vec<_>>();
174
175 if lottery.is_empty() {
176 return None;
177 }
178
179 Some(Lottery::from(lottery))
180 }
181
182 fn basement_rooms(&self) -> &'static [RoomKind] {
183 match self {
184 RoomKind::Bar => &[RoomKind::Cellar],
185 _ => &[],
186 }
187 }
188
189 fn basement_lottery(
190 &self,
191 max_bounds: Aabr<i32>,
192 room_counts: &EnumMap<RoomKind, u32>,
193 ) -> Option<Lottery<RoomKind>> {
194 let lottery = self.basement_rooms().iter()
195 .filter(|kind| kind.fits(max_bounds))
197 .map(|room_kind| {
199 (
200 room_kind.chance(room_counts),
201 *room_kind,
202 )
203 })
204 .collect::<Vec<_>>();
205
206 if lottery.is_empty() {
207 return None;
208 }
209
210 Some(Lottery::from(lottery))
211 }
212}
213
214#[derive(Clone, Copy)]
215pub enum Detail {
216 Bar {
217 aabr: Aabr<i32>,
218 },
219 Table {
220 pos: Vec2<i32>,
221 chairs: EnumSet<Dir>,
222 },
223 Stage {
224 aabr: Aabr<i32>,
225 },
226}
227
228pub struct Room {
229 pub bounds: Aabb<i32>,
231 kind: RoomKind,
232 walls: EnumMap<Dir, Vec<Id<Wall>>>,
234 floors: Vec<Id<Roof>>,
235 roofs: Vec<Id<Roof>>,
236 detail_areas: Vec<Aabr<i32>>,
237 pub details: Vec<Detail>,
238}
239
240impl Room {
241 fn new(bounds: Aabb<i32>, kind: RoomKind) -> Self {
242 Self {
243 bounds,
244 kind,
245 floors: Default::default(),
246 roofs: Default::default(),
247 walls: Default::default(),
248 detail_areas: Default::default(),
249 details: Default::default(),
250 }
251 }
252
253 fn is_covered_by_roof(&self, roofs: &Store<Roof>) -> bool {
255 let aabr = Aabr {
256 min: self.bounds.min.xy(),
257 max: self.bounds.max.xy(),
258 };
259 for roof in self.roofs.iter() {
260 if roofs[*roof].bounds.contains_aabr(aabr) {
261 return true;
262 }
263 }
264 false
265 }
266}
267
268pub struct Tavern {
269 pub name: String,
270 pub rooms: Store<Room>,
271 walls: Store<Wall>,
272 roofs: Store<Roof>,
273 pub door_tile: Vec2<i32>,
275 pub door_wpos: Vec3<i32>,
276 pub bounds: Aabr<i32>,
278}
279
280impl Tavern {
281 pub fn generate(
282 land: &Land,
283 _index: IndexRef,
284 rng: &mut impl Rng,
285 site: &Site,
286 door_tile: Vec2<i32>,
287 door_dir: Dir,
288 tile_aabr: Aabr<i32>,
289 alt: Option<i32>,
290 ) -> Self {
291 let name = namegen::NameGen::location(rng).generate_tavern();
292
293 let mut rooms = Store::default();
294 let mut walls = Store::default();
295 let mut roofs = Store::default();
296 let mut room_counts = EnumMap::<RoomKind, u32>::default();
297
298 let bounds = Aabr {
299 min: site.tile_wpos(tile_aabr.min),
300 max: site.tile_wpos(tile_aabr.max),
301 };
302
303 let ibounds = Aabr {
304 min: bounds.min + 1,
305 max: bounds.max - 2,
306 };
307
308 let door_tile_center = site.tile_center_wpos(door_tile);
309 let door_wpos = door_dir.select_aabr_with(ibounds, door_tile_center);
310 let temperature = land.get_interpolated(door_wpos, |c| c.temp);
311
312 let door_alt = alt.unwrap_or_else(|| land.get_alt_approx(door_wpos).ceil() as i32);
313 let door_wpos = door_wpos.with_z(door_alt);
314
315 fn gen_range_snap(rng: &mut impl Rng, range: RangeInclusive<i32>, snap_max: i32) -> i32 {
316 let res = rng.gen_range(range.clone());
317 if snap_max <= *range.end() && snap_max - res <= 2 {
318 snap_max
319 } else {
320 res
321 }
322 }
323
324 fn place_side_room(
326 room: RoomKind,
327 max_bounds: Aabr<i32>,
328 in_dir: Dir,
329 in_pos: Vec2<i32>,
330 rng: &mut impl Rng,
331 ) -> Option<Aabr<i32>> {
332 let (size_range, area_range) = room.size_range();
333 let min = *size_range.start();
334 let snap_max = in_dir.select(max_bounds.size());
335 let max = snap_max.min(*size_range.end());
336 if max < min {
337 return None;
338 }
339 let size_x = gen_range_snap(rng, min..=max, snap_max);
340
341 let min = ((*area_range.start() + size_x - 1) / size_x).max(*size_range.start());
342 let snap_max = in_dir.orthogonal().select(max_bounds.size());
343 let max = snap_max
344 .min(*size_range.end())
345 .min(*area_range.end() / size_x);
346
347 if max < min {
348 return None;
349 }
350 let size_y = gen_range_snap(rng, min..=max, snap_max);
351
352 let half_size_y = size_y / 2 + (size_y % 2) * rng.gen_range(0..=1);
354 let min = in_pos + in_dir.to_vec2() + in_dir.rotated_cw().to_vec2() * half_size_y;
355 let min = max_bounds.projected_point(min);
356 let max = min + in_dir.to_vec2() * size_x + in_dir.rotated_ccw().to_vec2() * size_y;
357 let max = max_bounds.projected_point(max);
358 let min = max - in_dir.to_vec2() * size_x + in_dir.rotated_cw().to_vec2() * size_y;
359
360 let bounds = Aabr { min, max }.made_valid();
361 Some(bounds)
362 }
363
364 fn place_down_room(
365 room: RoomKind,
366 max_bounds: Aabr<i32>,
367 from_bounds: Aabr<i32>,
368 rng: &mut impl Rng,
369 ) -> Option<Aabr<i32>> {
370 let (size_range, area_range) = room.size_range();
371 let min = Vec2::broadcast(*size_range.start());
372 let max = Vec2::from(max_bounds.size()).map(|e: i32| e.min(*size_range.end()));
373
374 let size_x = gen_range_snap(rng, min.x..=max.x, max_bounds.size().w);
375 let size_y = gen_range_snap(
376 rng,
377 min.y.max(area_range.start() / size_x)..=max.y.min(area_range.end() / size_x),
378 max_bounds.size().h,
379 );
380 let target_size = Vec2::new(size_x, size_y);
381 let dir = Dir::choose(rng);
382 let orth = *[dir.orthogonal(), dir.orthogonal().opposite()]
383 .choose(rng)
384 .unwrap();
385
386 let plane = dir.to_vec2() + orth.to_vec2();
387 let corner = dir.select_aabr_with(from_bounds, orth.select_aabr(from_bounds));
388 let aabr = Aabr {
389 min: corner,
390 max: corner - plane * target_size,
391 }
392 .made_valid();
393
394 let inside = aabr.intersection(max_bounds);
395 let mv = target_size - inside.size();
396 let aabr = Aabr {
397 min: aabr.min + mv * plane,
398 max: aabr.max + mv * plane,
399 };
400
401 let aabr = aabr.intersection(max_bounds);
402
403 let area = aabr.size().product();
404 if aabr.is_valid()
405 && area_range.contains(&area)
406 && size_range.contains(&aabr.size().reduce_min())
407 {
408 Some(aabr)
409 } else {
410 None
411 }
412 }
413
414 struct RoomMeta {
415 id: Id<Room>,
416 free_walls: EnumSet<Dir>,
417 can_add_basement: bool,
418 }
419
420 let mut room_metas = Vec::new();
421
422 {
423 let entrance_rooms = RoomKind::entrance_room_lottery(temperature);
424
425 let entrance_room = *entrance_rooms.choose_seeded(rng.gen());
426 let entrance_room_hgt = rng.gen_range(3..=4);
427 let entrance_room_aabr =
428 place_side_room(entrance_room, ibounds, -door_dir, door_wpos.xy(), rng)
429 .expect("Not enough room in plot for a tavern");
430 let entrance_room_aabb = Aabb {
431 min: entrance_room_aabr.min.with_z(door_wpos.z),
432 max: entrance_room_aabr
433 .max
434 .with_z(door_wpos.z + entrance_room_hgt),
435 }
436 .made_valid();
437
438 let entrance_id = rooms.insert(Room::new(entrance_room_aabb, entrance_room));
439
440 let start = door_dir.select_aabr_with(
441 entrance_room_aabr,
442 Vec2::broadcast(door_dir.rotated_cw().select_aabr(entrance_room_aabr)),
443 ) + door_dir.rotated_cw().to_vec2()
444 + door_dir.to_vec2();
445 let door_center = door_dir.rotated_cw().select(door_wpos.xy() - start).abs();
446 let wall_id = walls.insert(Wall {
447 start,
448 end: door_dir.select_aabr_with(
449 entrance_room_aabr,
450 Vec2::broadcast(door_dir.rotated_ccw().select_aabr(entrance_room_aabr)),
451 ) + door_dir.rotated_ccw().to_vec2()
452 + door_dir.to_vec2(),
453 base_alt: entrance_room_aabb.min.z,
454 top_alt: entrance_room_aabb.max.z,
455 from: None,
456 to: Some(entrance_id),
457 to_dir: -door_dir,
458 door: Some((door_center - 1, door_center + 1)),
459 });
460 rooms[entrance_id].walls[door_dir].push(wall_id);
461
462 room_metas.push(RoomMeta {
463 id: entrance_id,
464 free_walls: Dir::iter().filter(|d| *d != door_dir).collect(),
465 can_add_basement: false,
466 });
467
468 room_counts[entrance_room] += 1;
469 }
470
471 fn to_aabr(aabb: Aabb<i32>) -> Aabr<i32> {
472 Aabr {
473 min: aabb.min.xy(),
474 max: aabb.max.xy(),
475 }
476 }
477
478 fn extend_aabr(aabr: Aabr<i32>, amount: i32) -> Aabr<i32> {
479 Aabr {
480 min: aabr.min - amount,
481 max: aabr.max + amount,
482 }
483 }
484 while !room_metas.is_empty() {
485 let mut room_meta = room_metas.swap_remove(rng.gen_range(0..room_metas.len()));
487 let from_id = room_meta.id;
488 let from_room = &rooms[from_id];
489
490 let from_bounds = to_aabr(from_room.bounds);
491
492 fn fit_room<'r>(
493 rooms: impl Iterator<Item = &'r Room>,
494 min_z: i32,
495 max_z: i32,
496 mut max_bounds: Aabr<i32>,
497 mut max_shrink_dir: impl FnMut(Dir) -> Option<i32>,
498 ) -> Option<Aabr<i32>> {
499 for room in rooms
503 .filter(|room| room.bounds.min.z - 1 <= max_z && room.bounds.max.z + 1 >= min_z)
504 {
505 let test_bounds = to_aabr(room.bounds);
506 let bounds = extend_aabr(test_bounds, 2);
507 let intersection = bounds.intersection(max_bounds);
508 if intersection.is_valid() {
509 let bounds = Dir::iter()
511 .filter(|dir| {
512 dir.select_aabr(intersection) * dir.signum()
513 < dir.select_aabr(max_bounds) * dir.signum()
514 })
515 .map(|min_dir| {
516 let max_shrink = max_shrink_dir(min_dir);
517 let limit = min_dir.vec2_abs(max_shrink, None);
518 Aabr {
519 min: min_dir.select_aabr_with(
520 max_bounds,
521 Vec2::broadcast(
522 min_dir.rotated_ccw().select_aabr(max_bounds),
523 ),
524 ),
525 max: min_dir
526 .select_aabr_with(
527 intersection,
528 Vec2::broadcast(
529 min_dir.rotated_cw().select_aabr(max_bounds),
530 ),
531 )
532 .map2(limit, |a, b| {
533 b.map_or(a, |b| {
534 (a * min_dir.signum()).min(b * min_dir.signum())
535 * min_dir.signum()
536 })
537 }),
538 }
539 .made_valid()
540 })
541 .filter(|bounds| !bounds.intersection(test_bounds).is_valid())
542 .max_by_key(|bounds| bounds.size().product())?;
543
544 max_bounds = bounds;
545 }
546 }
547 Some(max_bounds)
548 }
549 'room_gen: {
550 if let Some(in_dir) = room_meta.free_walls.into_iter().choose(rng) {
551 room_meta.free_walls.remove(in_dir);
552 let right = in_dir.orthogonal();
553 let left = -right;
554
555 let max_bounds = Aabr {
557 min: in_dir.select_aabr_with(from_bounds, ibounds.min)
558 + in_dir.to_vec2() * 2,
559 max: in_dir.select_aabr_with(ibounds, ibounds.max),
560 }
561 .made_valid();
562 let room_hgt = rng.gen_range(3..=5);
564
565 let wanted_alt = land.get_alt_approx(max_bounds.center()) as i32 + 1;
566 let max_stair_length =
567 (in_dir.select(if wanted_alt < from_room.bounds.min.z {
568 from_bounds.size()
569 } else {
570 max_bounds.size()
571 }) / 2)
572 .min(5);
573 let alt = wanted_alt.clamp(
574 from_room.bounds.min.z - max_stair_length,
575 from_room.bounds.min.z + max_stair_length,
576 );
577 let min_z = from_room.bounds.min.z.min(alt);
578 let max_z = from_room.bounds.max.z.max(alt + room_hgt);
579
580 let Some(max_bounds) = fit_room(
581 rooms
582 .iter()
583 .filter(|(id, _)| *id != from_id)
584 .map(|(_, r)| r),
585 min_z,
586 max_z,
587 max_bounds,
588 |dir| {
589 if dir == in_dir {
590 Some(dir.opposite().select_aabr(max_bounds))
593 } else if dir == in_dir.opposite() {
594 None
595 } else {
596 Some(dir.select_aabr(extend_aabr(from_bounds, -1)))
599 }
600 },
601 ) else {
602 break 'room_gen;
603 };
604
605 let Some(room_lottery) = rooms[room_meta.id].kind.side_room_lottery(
606 max_bounds,
607 &room_counts,
608 temperature,
609 ) else {
610 break 'room_gen;
612 };
613
614 let room_kind = *room_lottery.choose_seeded(rng.gen());
615
616 let mut min = left
618 .select_aabr(from_bounds)
619 .max(left.select_aabr(max_bounds));
620 let mut max = right
621 .select_aabr(from_bounds)
622 .min(right.select_aabr(max_bounds));
623 if max < min {
624 swap(&mut min, &mut max);
625 }
626 if min + 2 > max {
627 break 'room_gen;
628 }
629 let in_pos = rng.gen_range(min + 1..=max - 1);
630 let in_pos = in_dir.select_aabr_with(from_bounds, Vec2::broadcast(in_pos))
631 + in_dir.to_vec2();
632
633 let Some(bounds) = place_side_room(room_kind, max_bounds, in_dir, in_pos, rng)
635 else {
636 break 'room_gen;
637 };
638
639 let bounds3 = Aabb {
640 min: bounds.min.with_z(min_z),
641 max: bounds.max.with_z(max_z),
642 };
643 let id = rooms.insert(Room::new(bounds3, room_kind));
644
645 let start = in_dir.select_aabr_with(
646 from_bounds,
647 Vec2::broadcast(
648 left.select_aabr(from_bounds).max(left.select_aabr(bounds)),
649 ),
650 ) + in_dir.to_vec2()
651 + left.to_vec2();
652
653 let end = in_dir.select_aabr_with(
654 from_bounds,
655 Vec2::broadcast(
656 right
657 .select_aabr(from_bounds)
658 .min(right.select_aabr(bounds)),
659 ),
660 ) + in_dir.to_vec2()
661 + right.to_vec2();
662
663 let door_center = right.select(in_pos - start);
664 let b = rng.gen_bool(0.5);
665 let door_min = door_center - b as i32;
666 let door_max = door_center - (!b) as i32;
667 let wall_id = walls.insert(Wall {
668 start,
669 end,
670 base_alt: min_z,
671 top_alt: max_z,
672 from: Some(from_id),
673 to: Some(id),
674 to_dir: in_dir,
675 door: Some((door_min, door_max)),
676 });
677
678 rooms[id].walls[-in_dir].push(wall_id);
679 rooms[from_id].walls[in_dir].push(wall_id);
680
681 room_metas.push(RoomMeta {
682 id,
683 free_walls: Dir::iter().filter(|d| *d != -in_dir).collect(),
684 can_add_basement: !room_kind.basement_rooms().is_empty(),
685 });
686 room_counts[room_kind] += 1;
687 } else if room_meta.can_add_basement {
688 room_meta.can_add_basement = false;
689 let max_bounds = ibounds;
690
691 let room_hgt = rng.gen_range(3..=5);
693 let max_z = from_room.bounds.min.z - 2;
694 let min_z = max_z - room_hgt;
695
696 let Some(max_bounds) = fit_room(
697 rooms
698 .iter()
699 .filter(|(id, _)| *id != from_id)
700 .map(|(_, r)| r),
701 min_z,
702 max_z,
703 max_bounds,
704 |dir| Some(dir.opposite().select_aabr(extend_aabr(from_bounds, -2))),
705 ) else {
706 break 'room_gen;
707 };
708
709 let Some(room_lottery) = rooms[room_meta.id]
710 .kind
711 .basement_lottery(max_bounds, &room_counts)
712 else {
713 break 'room_gen;
715 };
716
717 let room_kind = *room_lottery.choose_seeded(rng.gen());
718
719 let Some(bounds) = place_down_room(room_kind, max_bounds, from_bounds, rng)
721 else {
722 break 'room_gen;
723 };
724
725 let bounds3 = Aabb {
726 min: bounds.min.with_z(min_z),
727 max: bounds.max.with_z(max_z),
728 };
729 let id = rooms.insert(Room::new(bounds3, room_kind));
730
731 room_metas.push(RoomMeta {
732 id,
733 free_walls: EnumSet::all(),
734 can_add_basement: !room_kind.basement_rooms().is_empty(),
735 });
736 room_counts[room_kind] += 1;
737 } else {
738 break 'room_gen;
739 };
740 }
741
742 if !room_meta.free_walls.is_empty() || room_meta.can_add_basement {
744 room_metas.push(room_meta);
745 }
746 }
747
748 for from_id in rooms.ids() {
750 let room_bounds = to_aabr(rooms[from_id].bounds);
751 let mut skip = HashSet::new();
752 skip.insert(from_id);
753 let mut wall_ranges = EnumMap::<Dir, Vec<_>>::default();
754 for dir in Dir::iter() {
755 let orth = dir.orthogonal();
756 let range = (orth.select(room_bounds.min), orth.select(room_bounds.max));
757 wall_ranges[dir].push(range);
758 }
759 let mut split_range = |dir: Dir, min: i32, max: i32| {
761 debug_assert!(min <= max);
762 let mut new_ranges = Vec::new();
763 wall_ranges[dir].retain_mut(|(r_min, r_max)| {
764 if *r_min <= max && *r_max >= min {
765 match (*r_min >= min, *r_max <= max) {
766 (true, true) => false,
767 (true, false) => {
768 *r_min = max + 1;
769 true
770 },
771 (false, true) => {
772 *r_max = min - 1;
773 true
774 },
775 (false, false) => {
776 new_ranges.push((max + 1, *r_max));
777 *r_max = min - 1;
778 true
779 },
780 }
781 } else {
782 true
783 }
784 });
785 wall_ranges[dir].extend(new_ranges);
786 };
787 for dir in Dir::iter() {
788 let connected_walls = &mut rooms[from_id].walls[dir];
789 skip.extend(
790 connected_walls
791 .iter()
792 .flat_map(|wall| walls[*wall].from.into_iter().chain(walls[*wall].to)),
793 );
794 let orth = dir.orthogonal();
795 for wall in connected_walls.iter() {
797 let wall = &walls[*wall];
798 let mut min = orth.select(wall.start);
799 let mut max = orth.select(wall.end);
800 if min > max {
801 swap(&mut min, &mut max);
802 }
803 min += 1;
804 max -= 1;
805 split_range(dir, min, max);
806 }
807 }
808
809 for to_id in rooms.ids().filter(|id| !skip.contains(id)) {
811 let a_min_z = rooms[from_id].bounds.min.z;
812 let a_max_z = rooms[from_id].bounds.max.z;
813 let b_min_z = rooms[to_id].bounds.min.z;
814 let b_max_z = rooms[to_id].bounds.max.z;
815 if a_min_z >= b_max_z || a_max_z <= b_min_z {
816 continue;
818 }
819 let min_z = a_min_z.min(b_min_z);
820 let max_z = a_max_z.max(b_max_z);
821 let n_room_bounds = to_aabr(rooms[to_id].bounds);
822
823 let p1 = n_room_bounds.projected_point(room_bounds.center());
824 let p0 = room_bounds.projected_point(p1);
825
826 let to_dir = Dir::from_vec2(p1 - p0);
827
828 let intersection = to_dir
829 .extend_aabr(room_bounds, 1)
830 .intersection(to_dir.opposite().extend_aabr(n_room_bounds, 1));
831
832 if intersection.is_valid() {
833 let start = intersection.min;
834 let end = intersection.max;
835
836 let orth = to_dir.orthogonal();
837
838 let min = orth.select(start);
839 let max = orth.select(end);
840 split_range(to_dir, min, max);
841 let door = if max - min > 2
842 && max_z - min_z > 3
843 && (rooms[from_id].bounds.min.z - rooms[to_id].bounds.min.z).abs() < 4
844 && rng.gen_bool(0.8)
845 {
846 let door_center = rng.gen_range(1..=max - min - 2);
847 Some((door_center, door_center + 1))
848 } else {
849 None
850 };
851
852 let id = walls.insert(Wall {
853 start: start - orth.to_vec2(),
854 end: end + orth.to_vec2(),
855 base_alt: min_z,
856 top_alt: max_z,
857 from: Some(from_id),
858 to: Some(to_id),
859 to_dir,
860 door,
861 });
862
863 rooms[from_id].walls[to_dir].push(id);
864 rooms[to_id].walls[-to_dir].push(id);
865 }
866 }
867 for (dir, ranges) in wall_ranges {
869 for (min, max) in ranges {
870 let start =
871 dir.select_aabr_with(room_bounds, Vec2::broadcast(min - 1)) + dir.to_vec2();
872 let end =
873 dir.select_aabr_with(room_bounds, Vec2::broadcast(max + 1)) + dir.to_vec2();
874
875 let wall_id = walls.insert(Wall {
876 start,
877 end,
878 base_alt: rooms[from_id].bounds.min.z,
879 top_alt: rooms[from_id].bounds.max.z,
880 from: Some(from_id),
881 to: None,
882 to_dir: dir,
883 door: None,
884 });
885
886 rooms[from_id].walls[dir].push(wall_id);
887 }
888 }
889 }
890
891 for room_id in rooms.ids() {
892 let room = &rooms[room_id];
893 if room.is_covered_by_roof(&roofs) {
895 continue;
896 }
897 let roof_min_z = room.bounds.max.z + 1;
898 let mut roof_bounds = to_aabr(room.bounds);
899 roof_bounds.min -= 2;
900 roof_bounds.max += 2;
901 let mut dirs = Vec::from(Dir::ALL);
902
903 let mut over_rooms = vec![room_id];
904 let mut under_rooms = vec![];
905 while !dirs.is_empty() {
907 let dir = dirs.swap_remove(rng.gen_range(0..dirs.len()));
908 let orth = dir.orthogonal();
909 for (room_id, room) in rooms.iter() {
911 let room_aabr = to_aabr(room.bounds);
912 if room.bounds.max.z + 1 == roof_min_z
913 && dir.select_aabr(roof_bounds) + dir.signum()
914 == (-dir).select_aabr(room_aabr)
915 && orth.select_aabr(roof_bounds) <= orth.select_aabr(room_aabr) + 2
916 && (-orth).select_aabr(roof_bounds) >= (-orth).select_aabr(room_aabr) - 2
917 {
918 if room.is_covered_by_roof(&roofs) {
921 break;
922 }
923 roof_bounds = dir.extend_aabr(roof_bounds, dir.select(room_aabr.size()));
924 dirs.push(dir);
925 over_rooms.push(room_id);
926 break;
927 }
928 }
929 }
930 for (room_id, room) in rooms.iter() {
931 let room_aabr = to_aabr(room.bounds);
932 if room.bounds.min.z - 1 == roof_min_z && room_aabr.collides_with_aabr(roof_bounds)
933 {
934 under_rooms.push(room_id);
935 }
936 }
937
938 let valid_styles = if !under_rooms.is_empty() {
939 vec![(1.0, RoofStyle::Floor)]
940 } else {
941 let mut valid_styles = vec![(0.5, RoofStyle::Flat)];
943
944 let gardens = over_rooms
945 .iter()
946 .filter(|id| matches!(rooms[**id].kind, RoomKind::Garden))
947 .count();
948
949 if gardens == over_rooms.len() {
951 let ratio = Dir::X.select(roof_bounds.size()) as f32
952 / Dir::Y.select(roof_bounds.size()) as f32;
953 valid_styles.extend([
954 (5.0 * ratio, RoofStyle::FlatBars { dir: Dir::X }),
955 (5.0 / ratio, RoofStyle::FlatBars { dir: Dir::Y }),
956 ]);
957 }
958
959 let mut dir_zs = EnumMap::default();
961 for dir in Dir::iter() {
962 let orth = dir.orthogonal();
963 for room in rooms.values() {
964 let room_aabr = to_aabr(room.bounds);
965 if room.bounds.max.z > roof_min_z
966 && dir.select_aabr(roof_bounds) == (-dir).select_aabr(room_aabr)
967 && orth.select_aabr(roof_bounds) <= orth.select_aabr(room_aabr) + 2
968 && (-orth).select_aabr(roof_bounds)
969 >= (-orth).select_aabr(room_aabr) - 2
970 {
971 dir_zs[dir] = Some(room.bounds.max.z);
972 break;
973 }
974 }
975 }
976
977 for dir in [Dir::X, Dir::Y] {
978 if dir_zs[dir.orthogonal()].is_none() && dir_zs[-dir.orthogonal()].is_none() {
979 let max_z = roof_min_z
980 + (dir.orthogonal().select(roof_bounds.size()) / 2 - 1).min(7);
981 let max_z = match (dir_zs[dir], dir_zs[-dir]) {
982 (Some(a), Some(b)) => {
983 if a.min(b) >= roof_min_z + 3 {
984 max_z.min(a.min(b))
985 } else {
986 max_z
987 }
988 },
989 (None, None) => max_z,
990 _ => continue,
991 };
992
993 for max_z in roof_min_z + 3..=max_z {
994 valid_styles.push((1.0, RoofStyle::Gable { dir, max_z }))
995 }
996 }
997 }
998
999 for dir in Dir::iter() {
1000 if let (Some(h), None) = (dir_zs[dir], dir_zs[-dir]) {
1001 for max_z in roof_min_z + 2..=h {
1002 valid_styles.push((1.0, RoofStyle::LeanTo { dir, max_z }))
1003 }
1004 }
1005 }
1006
1007 if Dir::iter().all(|d| dir_zs[d].is_none()) {
1008 for max_z in roof_min_z + 3..=roof_min_z + 7 {
1009 valid_styles.push((0.8, RoofStyle::Hip { max_z }))
1010 }
1011 }
1012
1013 valid_styles
1014 };
1015
1016 let style_lottery = Lottery::from(valid_styles);
1017
1018 debug_assert!(
1019 roof_bounds.is_valid(),
1020 "Roof bounds aren't valid: {:?}",
1021 roof_bounds
1022 );
1023
1024 let stairs = under_rooms
1025 .iter()
1026 .copied()
1027 .flat_map(|to_room| {
1028 let rooms = &rooms;
1029 let walls = &walls;
1030 over_rooms
1031 .iter()
1032 .copied()
1033 .filter_map(move |in_room| {
1034 let to_room_bounds = rooms[to_room].bounds;
1035 let in_room_bounds = rooms[in_room].bounds;
1036 let max_bounds =
1037 to_aabr(to_room_bounds).intersection(to_aabr(in_room_bounds));
1038 let stair_length = to_room_bounds.min.z - 1 - in_room_bounds.min.z;
1039 if !max_bounds.is_valid()
1040 || max_bounds.size().reduce_min() <= stair_length
1041 {
1042 return None;
1043 }
1044
1045 let in_aabr = to_aabr(in_room_bounds);
1046 let to_aabr = to_aabr(to_room_bounds);
1047
1048 let valid_dirs = Dir::iter().filter(move |dir| {
1049 dir.select_aabr(in_aabr) == dir.select_aabr(max_bounds)
1050 || dir.select_aabr(to_aabr) == dir.select_aabr(max_bounds)
1051 });
1052
1053 Some(valid_dirs.clone().flat_map(move |dir| {
1054 valid_dirs
1055 .clone()
1056 .filter(move |d| d.abs() != dir.abs())
1057 .filter_map(move |orth| {
1058 let stair_width = 2;
1059 let stair_aabr = orth.trim_aabr(
1060 dir.trim_aabr(
1061 max_bounds,
1062 dir.select(max_bounds.size()) - stair_length,
1063 ),
1064 orth.select(max_bounds.size()) - stair_width + 1,
1065 );
1066
1067 let test_aabr = Aabr {
1068 min: stair_aabr.min - 1,
1069 max: stair_aabr.max - 1,
1070 };
1071 if !stair_aabr.is_valid()
1072 || rooms[in_room]
1073 .walls
1074 .values()
1075 .chain(rooms[to_room].walls.values())
1076 .flatten()
1077 .any(|wall| {
1078 walls[*wall].door_bounds().is_some_and(
1079 |door_bounds| {
1080 test_aabr
1081 .collides_with_aabr(door_bounds)
1082 },
1083 )
1084 })
1085 {
1086 return None;
1087 }
1088
1089 Some((
1090 Aabb {
1091 min: stair_aabr.min.with_z(in_room_bounds.min.z),
1092 max: stair_aabr
1093 .max
1094 .with_z(to_room_bounds.min.z - 1),
1095 },
1096 dir,
1097 ))
1098 })
1099 }))
1100 })
1101 .flatten()
1102 })
1103 .choose(rng);
1104
1105 let roof_id = roofs.insert(Roof {
1106 bounds: roof_bounds,
1107 min_z: roof_min_z,
1108 stairs,
1109 style: *style_lottery.choose_seeded(rng.gen()),
1110 });
1111
1112 for room_id in over_rooms {
1113 rooms[room_id].roofs.push(roof_id);
1114 }
1115 for room_id in under_rooms {
1116 rooms[room_id].floors.push(roof_id);
1117 }
1118 }
1119
1120 for room in rooms.values_mut() {
1122 let bounds = to_aabr(room.bounds);
1123 let walls = &walls;
1124 let mut avoid = room
1125 .walls
1126 .iter()
1127 .flat_map(|(dir, dir_walls)| {
1128 dir_walls.iter().filter_map(move |wall_id| {
1129 let wall = &walls[*wall_id];
1130
1131 let door_bounds = wall.door_bounds()?;
1132
1133 Some(
1134 Aabr {
1135 min: dir.select_aabr_with(bounds, door_bounds.min),
1136 max: dir.select_with(bounds.center(), door_bounds.max),
1137 }
1138 .made_valid(),
1139 )
1140 })
1141 })
1142 .chain(
1143 room.floors
1144 .iter()
1145 .chain(room.roofs.iter())
1146 .filter_map(|roof| {
1147 let aabr = to_aabr(roofs[*roof].stairs?.0);
1148 let intersection = aabr.intersection(bounds);
1149 intersection.is_valid().then_some(intersection)
1150 }),
1151 )
1152 .collect::<Vec<_>>();
1153
1154 let mut x = bounds.min.x;
1155 while x <= bounds.max.x {
1157 let mut y = bounds.min.y;
1158 'y_loop: while y <= bounds.max.y {
1159 let min = Vec2::new(x, y);
1160 let mut max_y = bounds.max.y;
1161 for area in avoid.iter() {
1162 let contains_x = area.min.x <= min.x && min.x <= area.max.x;
1163 let contains_y = area.min.y <= min.y && min.y <= area.max.y;
1164 if contains_x && contains_y {
1165 y = area.max.y + 1;
1166 continue 'y_loop;
1167 }
1168
1169 if contains_x && min.y < area.min.y && area.min.y - 1 < max_y {
1170 max_y = area.min.y - 1;
1171 }
1172 }
1173
1174 let max_x = avoid
1175 .iter()
1176 .filter_map(|area| {
1177 if area.min.x > x && area.min.y <= max_y && area.max.y >= min.y {
1178 Some(area.min.x - 1)
1179 } else {
1180 None
1181 }
1182 })
1183 .min()
1184 .unwrap_or(bounds.max.x);
1185
1186 let area = Aabr {
1187 min,
1188 max: Vec2::new(max_x, max_y),
1189 };
1190 avoid.push(area);
1191 room.detail_areas.push(area);
1192 y = max_y + 1;
1193 }
1194 x += 1;
1195 }
1196 }
1197
1198 for room in rooms.values_mut() {
1200 let room_aabr = to_aabr(room.bounds);
1201 let table = |pos: Vec2<i32>, aabr: Aabr<i32>| Detail::Table {
1202 pos,
1203 chairs: Dir::iter()
1204 .filter(|dir| aabr.contains_point(pos + dir.to_vec2()))
1205 .collect(),
1206 };
1207 match room.kind {
1208 RoomKind::Garden | RoomKind::Seating => room.detail_areas.retain(|&aabr| {
1209 if aabr.size().reduce_max() > 1 && rng.gen_bool(0.7) {
1210 room.details.push(table(aabr.center(), aabr));
1211 false
1212 } else {
1213 true
1214 }
1215 }),
1216 RoomKind::Cellar => {},
1217 RoomKind::Stage => {
1218 let mut best = None;
1219 let mut best_score = 0;
1220 for (i, aabr) in room.detail_areas.iter().enumerate() {
1221 let edges = Dir::iter()
1222 .filter(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr))
1223 .count() as i32;
1224 let test_score = edges * aabr.size().product();
1225 if best_score < test_score {
1226 best_score = test_score;
1227 best = Some(i);
1228 }
1229 }
1230 if let Some(aabr) = best.map(|i| room.detail_areas.swap_remove(i)) {
1231 room.details.push(Detail::Stage { aabr })
1232 }
1233 room.detail_areas.retain(|&aabr| {
1234 if aabr.size().reduce_max() > 1 && rng.gen_bool(0.8) {
1235 room.details.push(table(aabr.center(), aabr));
1236 false
1237 } else {
1238 true
1239 }
1240 });
1241 },
1242 RoomKind::Bar => {
1243 let mut best = None;
1244 let mut best_score = 0;
1245 for (i, aabr) in room.detail_areas.iter().enumerate() {
1246 let test_score = Dir::iter()
1247 .any(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr))
1248 as i32
1249 * aabr.size().product();
1250 if best_score < test_score {
1251 best_score = test_score;
1252 best = Some(i);
1253 }
1254 }
1255 if let Some(aabr) = best.map(|i| room.detail_areas.swap_remove(i)) {
1256 room.details.push(Detail::Bar { aabr })
1257 }
1258 room.detail_areas.retain(|&aabr| {
1259 if aabr.size().reduce_max() > 1 && rng.gen_bool(0.1) {
1260 room.details.push(table(aabr.center(), aabr));
1261 false
1262 } else {
1263 true
1264 }
1265 });
1266 },
1267 RoomKind::Entrance => {},
1268 }
1269 }
1270
1271 Self {
1272 name,
1273 rooms,
1274 walls,
1275 roofs,
1276 door_tile,
1277 door_wpos,
1278 bounds,
1279 }
1280 }
1281}
1282
1283fn aabb(mut aabb: Aabb<i32>) -> Aabb<i32> {
1284 aabb.make_valid();
1285 aabb.max += 1;
1286 aabb
1287}
1288
1289impl Structure for Tavern {
1290 #[cfg(feature = "use-dyn-lib")]
1291 const UPDATE_FN: &'static [u8] = b"render_tavern\0";
1292
1293 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_tavern")]
1294 fn render_inner(&self, _site: &Site, land: &Land, painter: &crate::site2::Painter) {
1295 let field = RandomField::new(740384);
1296 let field_choose = RandomField::new(134598);
1297
1298 const DOWN: i32 = 6;
1299
1300 let mut offset = 0;
1301 let mut choose = |slice: &[Rgb<u8>]| -> Rgb<u8> {
1302 offset += 1;
1303 *field
1304 .choose(self.door_wpos + offset, slice)
1305 .expect("Color slice should not be empty.")
1306 };
1307
1308 let detail_fill = Fill::Brick(
1309 BlockKind::Rock,
1310 choose(&[
1311 Rgb::new(55, 65, 64),
1312 Rgb::new(46, 62, 100),
1313 Rgb::new(46, 100, 62),
1314 Rgb::new(100, 100, 105),
1315 ]),
1316 15,
1317 );
1318 let wall_fill = Fill::Brick(
1319 BlockKind::Wood,
1320 choose(&[
1321 Rgb::new(160, 53, 34),
1322 Rgb::new(147, 51, 29),
1323 Rgb::new(147, 101, 69),
1324 Rgb::new(90, 90, 95),
1325 Rgb::new(170, 140, 52),
1326 ]),
1327 20,
1328 );
1329 let wall_detail_fill = Fill::Brick(
1330 BlockKind::Wood,
1331 choose(&[Rgb::new(108, 100, 79), Rgb::new(150, 150, 150)]),
1332 25,
1333 );
1334 let floor_fill = Fill::Brick(
1335 BlockKind::Wood,
1336 choose(&[Rgb::new(42, 44, 43), Rgb::new(56, 18, 10)]),
1337 10,
1338 );
1339 let roof_fill = Fill::Brick(
1340 BlockKind::Wood,
1341 choose(&[
1342 Rgb::new(21, 43, 48),
1343 Rgb::new(11, 23, 38),
1344 Rgb::new(45, 28, 21),
1345 Rgb::new(10, 55, 40),
1346 Rgb::new(5, 35, 15),
1347 Rgb::new(40, 5, 11),
1348 Rgb::new(55, 45, 11),
1349 ]),
1350 20,
1351 );
1352 let simple_roof_fill = Fill::Brick(
1353 BlockKind::Wood,
1354 choose(&[Rgb::new(106, 73, 64), Rgb::new(85, 52, 43)]),
1355 20,
1356 );
1357
1358 let get_kind = |room| self.rooms.get(room).kind;
1359 let get_door_stair = |wall: &Wall, door: Aabr<i32>| {
1360 let filter = |room: &Id<Room>| self.rooms[*room].bounds.min.z > wall.base_alt;
1361 wall.to
1362 .filter(filter)
1363 .zip(Some(wall.to_dir))
1364 .or(wall.from.filter(filter).zip(Some(-wall.to_dir)))
1365 .map(|(room, to_dir)| {
1366 let room = &self.rooms[room];
1367
1368 let max = door.max + to_dir.to_vec2() * (room.bounds.min.z - wall.base_alt + 1);
1369 (door.min, max, room, to_dir)
1370 })
1371 };
1372
1373 for roof in self.roofs.values() {
1375 match roof.style {
1376 RoofStyle::Flat => {
1377 painter
1378 .aabb(aabb(Aabb {
1379 min: roof.bounds.min.with_z(roof.min_z),
1380 max: roof.bounds.max.with_z(roof.min_z),
1381 }))
1382 .fill(roof_fill.clone());
1383 },
1384 RoofStyle::Floor => {
1385 painter
1386 .aabb(aabb(Aabb {
1387 min: roof.bounds.min.with_z(roof.min_z),
1388 max: roof.bounds.max.with_z(roof.min_z),
1389 }))
1390 .fill(floor_fill.clone());
1391 },
1392 RoofStyle::FlatBars { dir } => painter
1393 .aabb(aabb(Aabb {
1394 min: dir
1395 .select_aabr_with(roof.bounds, roof.bounds.min)
1396 .with_z(roof.min_z),
1397 max: dir
1398 .select_aabr_with(roof.bounds, roof.bounds.max)
1399 .with_z(roof.min_z),
1400 }))
1401 .repeat(
1402 -dir.to_vec3() * 2,
1403 (dir.select(roof.bounds.size()) as u32 + 3) / 2,
1404 )
1405 .fill(simple_roof_fill.clone()),
1406 RoofStyle::LeanTo { dir, max_z } => {
1407 painter
1408 .aabb(aabb(Aabb {
1409 min: roof.bounds.min.with_z(roof.min_z),
1410 max: roof.bounds.max.with_z(roof.min_z),
1411 }))
1412 .fill(roof_fill.clone());
1413 painter
1414 .ramp(
1415 aabb(Aabb {
1416 min: roof.bounds.min.with_z(roof.min_z),
1417 max: roof.bounds.max.with_z(max_z),
1418 }),
1419 dir,
1420 )
1421 .fill(roof_fill.clone());
1422 for d in [dir.orthogonal(), -dir.orthogonal()] {
1423 painter
1424 .ramp(
1425 aabb(Aabb {
1426 min: (d.select_aabr_with(roof.bounds, roof.bounds.min)
1427 - d.to_vec2())
1428 .with_z(roof.min_z - 1),
1429 max: (d.select_aabr_with(roof.bounds, roof.bounds.max)
1430 - d.to_vec2())
1431 .with_z(max_z - 1),
1432 }),
1433 dir,
1434 )
1435 .fill(wall_fill.clone());
1436 painter
1437 .ramp(
1438 aabb(Aabb {
1439 min: d
1440 .select_aabr_with(roof.bounds, roof.bounds.min)
1441 .with_z(roof.min_z - 1),
1442 max: d
1443 .select_aabr_with(roof.bounds, roof.bounds.max)
1444 .with_z(max_z - 1),
1445 }),
1446 dir,
1447 )
1448 .clear();
1449 }
1450 },
1451 RoofStyle::Gable { dir, max_z } => {
1452 painter
1453 .gable(
1454 aabb(Aabb {
1455 min: roof.bounds.min.with_z(roof.min_z),
1456 max: roof.bounds.max.with_z(max_z),
1457 }),
1458 max_z - roof.min_z + 1,
1459 dir,
1460 )
1461 .fill(roof_fill.clone());
1462 for dir in [dir, -dir] {
1463 painter
1464 .gable(
1465 aabb(Aabb {
1466 min: (dir.select_aabr_with(roof.bounds, roof.bounds.min + 1)
1467 - dir.to_vec2())
1468 .with_z(roof.min_z),
1469 max: (dir.select_aabr_with(roof.bounds, roof.bounds.max - 1)
1470 - dir.to_vec2())
1471 .with_z(max_z - 1),
1472 }),
1473 max_z - roof.min_z,
1474 dir,
1475 )
1476 .fill(wall_fill.clone());
1477 painter
1478 .aabb(aabb(Aabb {
1479 min: (dir.select_aabr_with(roof.bounds, roof.bounds.min + 1)
1480 - dir.to_vec2())
1481 .with_z(roof.min_z),
1482 max: (dir.select_aabr_with(roof.bounds, roof.bounds.max - 1)
1483 - dir.to_vec2())
1484 .with_z(roof.min_z),
1485 }))
1486 .fill(wall_detail_fill.clone());
1487 let center_bounds = Aabr {
1488 min: (dir.select_aabr_with(roof.bounds, roof.bounds.center())
1489 - dir.to_vec2()),
1490 max: (dir.select_aabr_with(
1491 roof.bounds,
1492 (roof.bounds.min + roof.bounds.max + 1) / 2,
1493 ) - dir.to_vec2()),
1494 };
1495 painter
1496 .aabb(aabb(Aabb {
1497 min: center_bounds.min.with_z(roof.min_z),
1498 max: center_bounds.max.with_z(max_z - 1),
1499 }))
1500 .fill(wall_detail_fill.clone());
1501 for d in [dir.orthogonal(), -dir.orthogonal()] {
1502 let hgt = max_z - roof.min_z;
1503 let half_size = d.select(roof.bounds.size() + 1) / 2;
1504 let e = half_size - hgt + 1;
1505 let e = e - e % 2;
1506 let f = half_size - e;
1507 let hgt = (hgt - 1).min(e - f % 2) - (d.signum() - 1) / 2;
1508 let mut aabr = Aabr {
1509 min: d.select_aabr_with(center_bounds, center_bounds.min),
1510 max: d.select_aabr_with(center_bounds, center_bounds.max)
1511 + d.to_vec2() * hgt,
1512 }
1513 .made_valid();
1514 aabr.max += 1;
1515 painter
1516 .plane(
1517 aabr,
1518 aabr.min
1519 .with_z(if d.signum() < 0 {
1520 roof.min_z + hgt
1521 } else {
1522 roof.min_z
1523 })
1524 .as_(),
1525 d.to_vec2().as_(),
1526 )
1527 .fill(wall_detail_fill.clone());
1528 }
1529 painter
1530 .gable(
1531 aabb(Aabb {
1532 min: dir
1533 .select_aabr_with(roof.bounds, roof.bounds.min + 1)
1534 .with_z(roof.min_z),
1535 max: dir
1536 .select_aabr_with(roof.bounds, roof.bounds.max - 1)
1537 .with_z(max_z - 1),
1538 }),
1539 max_z - roof.min_z,
1540 dir,
1541 )
1542 .clear();
1543 }
1544 },
1545 RoofStyle::Hip { max_z } => {
1546 painter
1547 .pyramid(aabb(Aabb {
1548 min: roof.bounds.min.with_z(roof.min_z),
1549 max: roof.bounds.max.with_z(max_z),
1550 }))
1551 .fill(roof_fill.clone());
1552 },
1553 }
1554 }
1555
1556 for room in self.rooms.values() {
1558 painter
1559 .aabb(aabb(Aabb {
1560 min: room.bounds.min.with_z(room.bounds.min.z - DOWN),
1561 max: room.bounds.max.with_z(room.bounds.min.z - 1),
1562 }))
1563 .fill(floor_fill.clone());
1564 }
1565 for wall in self.walls.values() {
1567 let wall_aabb = Aabb {
1568 min: wall.start.with_z(wall.base_alt),
1569 max: wall.end.with_z(wall.top_alt),
1570 };
1571 let wall_dir = Dir::from_vec2(wall.end - wall.start);
1572 match (wall.from.map(get_kind), wall.to.map(get_kind)) {
1573 (Some(RoomKind::Garden), None) | (None, Some(RoomKind::Garden)) => {
1574 let hgt = wall_aabb.min.z..=wall_aabb.max.z;
1575 painter
1576 .column(wall_aabb.min.xy(), hgt.clone())
1577 .fill(wall_detail_fill.clone());
1578 painter
1579 .column(wall_aabb.max.xy(), hgt)
1580 .fill(wall_detail_fill.clone());
1581 let z = (wall.base_alt + wall.top_alt) / 2;
1582
1583 painter
1584 .aabb(aabb(Aabb {
1585 min: (wall_aabb.min + wall_dir.to_vec2()).with_z(wall_aabb.min.z + 1),
1586 max: (wall_aabb.max - wall_dir.to_vec2()).with_z(wall_aabb.max.z - 1),
1587 }))
1588 .clear();
1589
1590 painter.rotated_sprite(
1591 wall_aabb.min.with_z(z) + wall_dir.to_vec2(),
1592 SpriteKind::WallSconce,
1593 wall_dir.sprite_ori(),
1594 );
1595 painter.rotated_sprite(
1596 wall_aabb.max.with_z(z) - wall_dir.to_vec2(),
1597 SpriteKind::WallSconce,
1598 wall_dir.opposite().sprite_ori(),
1599 );
1600 painter
1601 .aabb(aabb(Aabb {
1602 min: wall_aabb.min.with_z(wall_aabb.min.z - DOWN),
1603 max: wall_aabb.max.with_z(wall_aabb.min.z),
1604 }))
1605 .fill(wall_detail_fill.clone());
1606 painter
1607 .aabb(aabb(Aabb {
1608 min: wall_aabb.min.with_z(wall_aabb.max.z),
1609 max: wall_aabb.max,
1610 }))
1611 .fill(wall_detail_fill.clone());
1612 },
1613 (Some(RoomKind::Garden), Some(RoomKind::Garden)) => {
1614 painter
1615 .aabb(aabb(Aabb {
1616 min: wall_aabb.min.with_z(wall_aabb.min.z - DOWN),
1617 max: wall_aabb.max.with_z(wall_aabb.min.z - 1),
1618 }))
1619 .fill(floor_fill.clone());
1620 painter.aabb(aabb(wall_aabb)).clear();
1621 },
1622 (None, None) => {},
1623 _ => {
1624 painter
1625 .aabb(aabb(Aabb {
1626 min: wall_aabb.min.with_z(wall_aabb.min.z - DOWN),
1627 max: wall_aabb.max,
1628 }))
1629 .fill(wall_fill.clone());
1630 painter
1631 .column(wall.start, wall.base_alt - DOWN..=wall.top_alt)
1632 .fill(wall_detail_fill.clone());
1633 painter
1634 .column(wall.end, wall.base_alt - DOWN..=wall.top_alt)
1635 .fill(wall_detail_fill.clone());
1636 },
1637 }
1638 if let Some(door) = wall.door_bounds() {
1639 let orth = wall.to_dir.orthogonal();
1640 if let Some((min, max, room, to_dir)) = get_door_stair(wall, door) {
1641 painter
1642 .aabb(aabb(Aabb {
1643 min: (min + to_dir.to_vec2() - orth.to_vec2())
1644 .with_z(wall.base_alt - 1),
1645 max: (max + orth.to_vec2()).with_z(room.bounds.min.z - 1),
1646 }))
1647 .fill(floor_fill.clone());
1648 }
1649 }
1650 }
1651
1652 for room in self.rooms.values() {
1654 painter.aabb(aabb(room.bounds)).clear();
1655
1656 let room_aabr = Aabr {
1657 min: room.bounds.min.xy(),
1658 max: room.bounds.max.xy(),
1659 };
1660 match room.kind {
1661 RoomKind::Garden => {},
1662 RoomKind::Cellar => {
1663 for aabr in room.detail_areas.iter().copied() {
1664 for dir in Dir::iter()
1665 .filter(|dir| dir.select_aabr(aabr) == dir.select_aabr(room_aabr))
1666 {
1667 let pos = dir
1668 .select_aabr_with(aabr, aabr.center())
1669 .with_z(room.bounds.center().z + 1);
1670
1671 painter.rotated_sprite(
1672 pos,
1673 SpriteKind::WallLampSmall,
1674 dir.opposite().sprite_ori(),
1675 );
1676
1677 for x in dir.orthogonal().select(aabr.min)
1678 ..=dir.orthogonal().select(aabr.max)
1679 {
1680 let pos = dir.select_aabr_with(aabr, x).with_z(room.bounds.min.z);
1681 if field.chance(pos, 0.3) {
1682 let sprite = field_choose
1683 .choose(pos, &[
1684 SpriteKind::Crate,
1685 SpriteKind::Barrel,
1686 SpriteKind::BarrelWoodWater,
1687 ])
1688 .unwrap();
1689 painter.owned_resource_sprite(pos, *sprite, 0);
1690 }
1691 }
1692 }
1693 }
1694 },
1695 RoomKind::Stage => {
1696 for aabr in room.detail_areas.iter().copied() {
1697 for dir in Dir::iter().filter(|dir| {
1698 dir.select_aabr(aabr) == dir.select_aabr(room_aabr)
1699 && dir.rotated_cw().select_aabr(aabr)
1700 == dir.rotated_cw().select_aabr(room_aabr)
1701 }) {
1702 let pos = dir.select_aabr_with(
1703 aabr,
1704 Vec2::broadcast(dir.rotated_cw().select_aabr(aabr)),
1705 );
1706 painter.sprite(pos.with_z(room.bounds.min.z), SpriteKind::StreetLamp);
1707 }
1708 }
1709 },
1710 RoomKind::Bar | RoomKind::Seating => {
1711 for aabr in room.detail_areas.iter().copied() {
1712 for dir in Dir::iter()
1713 .filter(|dir| dir.select_aabr(aabr) == dir.select_aabr(room_aabr))
1714 {
1715 let pos = dir
1716 .select_aabr_with(aabr, aabr.center())
1717 .with_z(room.bounds.center().z);
1718 let orth = dir.orthogonal();
1719 if room.walls[dir].iter().any(|wall| {
1720 let wall = &self.walls[*wall];
1721 (orth.select(wall.start)..=orth.select(wall.end))
1722 .contains(&orth.select(pos))
1723 && (wall.from.is_none() || wall.to.is_none())
1724 }) {
1725 continue;
1726 }
1727
1728 painter.rotated_sprite(
1729 pos,
1730 SpriteKind::WallLampSmall,
1731 dir.opposite().sprite_ori(),
1732 );
1733 }
1734 }
1735 },
1736 RoomKind::Entrance => {
1737 for aabr in room.detail_areas.iter() {
1738 let edges = Dir::iter()
1739 .filter(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr))
1740 .count();
1741 let hanger_pos = if edges == 2 {
1742 let pos = aabr.center().with_z(room.bounds.min.z);
1743 painter.sprite(pos, SpriteKind::CoatRack);
1744 Some(pos)
1745 } else {
1746 None
1747 };
1748
1749 for dir in Dir::iter()
1750 .filter(|dir| dir.select_aabr(*aabr) == dir.select_aabr(room_aabr))
1751 {
1752 let pos = dir
1753 .select_aabr_with(*aabr, aabr.center())
1754 .with_z(room.bounds.center().z + 1);
1755 if hanger_pos.is_some_and(|p| p.xy() != pos.xy()) {
1756 painter.rotated_sprite(
1757 pos,
1758 SpriteKind::WallLampSmall,
1759 dir.opposite().sprite_ori(),
1760 );
1761 }
1762 }
1763 }
1764 },
1765 }
1766 for detail in room.details.iter() {
1767 match *detail {
1768 Detail::Bar { aabr } => {
1769 for dir in Dir::iter() {
1770 let edge = dir.select_aabr(aabr);
1771 let rot_dir = if field.chance(aabr.center().with_z(0), 0.5) {
1772 dir.rotated_cw()
1773 } else {
1774 dir.rotated_ccw()
1775 };
1776 let rot_edge = rot_dir.select_aabr(aabr);
1777 match (
1778 edge == dir.select_aabr(room_aabr),
1779 rot_edge == rot_dir.select_aabr(room_aabr),
1780 ) {
1781 (false, _) => {
1782 let (min, max) = (
1783 dir.select_aabr_with(
1784 aabr,
1785 Vec2::broadcast(rot_dir.select_aabr(aabr)),
1786 ),
1787 dir.select_aabr_with(
1788 aabr,
1789 Vec2::broadcast(rot_dir.opposite().select_aabr(aabr)),
1790 ),
1791 );
1792 painter
1793 .aabb(aabb(Aabb {
1794 min: (min - rot_dir.to_vec2())
1795 .with_z(room.bounds.min.z),
1796 max: max.with_z(room.bounds.min.z),
1797 }))
1798 .fill(wall_detail_fill.clone());
1799 painter
1800 .aabb(aabb(Aabb {
1801 min: min.with_z(room.bounds.min.z + 3),
1802 max: max.with_z(room.bounds.max.z),
1803 }))
1804 .fill(wall_detail_fill.clone());
1805 },
1806 (true, true) => {
1807 painter.sprite(
1808 dir.abs().vec2(edge, rot_edge).with_z(room.bounds.min.z),
1809 SpriteKind::CookingPot,
1810 );
1811 },
1812 (true, false) => {},
1813 }
1814 }
1815 },
1816 Detail::Stage { aabr } => {
1817 painter
1818 .aabb(aabb(Aabb {
1819 min: aabr.min.with_z(room.bounds.min.z),
1820 max: aabr.max.with_z(room.bounds.min.z),
1821 }))
1822 .fill(detail_fill.clone());
1823 painter
1824 .aabb(aabb(Aabb {
1825 min: (aabr.min + 1).with_z(room.bounds.min.z),
1826 max: (aabr.max - 1).with_z(room.bounds.min.z),
1827 }))
1828 .fill(wall_fill.clone());
1829 for dir in Dir::iter().filter(|dir| {
1830 dir.select_aabr(aabr) != dir.select_aabr(room_aabr)
1831 && dir.rotated_cw().select_aabr(aabr)
1832 != dir.rotated_cw().select_aabr(room_aabr)
1833 }) {
1834 let pos = dir.select_aabr_with(
1835 aabr,
1836 Vec2::broadcast(dir.rotated_cw().select_aabr(aabr)),
1837 );
1838 painter
1839 .column(pos, room.bounds.min.z..=room.bounds.max.z)
1840 .fill(wall_detail_fill.clone());
1841
1842 for dir in Dir::iter() {
1843 painter.rotated_sprite(
1844 pos.with_z(room.bounds.center().z + 1) + dir.to_vec2(),
1845 SpriteKind::WallSconce,
1846 dir.sprite_ori(),
1847 );
1848 }
1849 }
1850 },
1851 Detail::Table { pos, chairs } => {
1852 let pos = pos.with_z(room.bounds.min.z);
1853 painter.sprite(pos, SpriteKind::TableDining);
1854 for dir in chairs.into_iter() {
1855 painter.rotated_sprite(
1856 pos + dir.to_vec2(),
1857 SpriteKind::ChairSingle,
1858 dir.opposite().sprite_ori(),
1859 );
1860 }
1861 },
1862 }
1863 }
1864 }
1865
1866 for wall in self.walls.values() {
1868 let kinds = (wall.from.map(get_kind), wall.to.map(get_kind));
1869 let in_dir_room = if let (Some(room), to @ None) | (None, to @ Some(room)) = kinds {
1870 let in_dir = if to.is_none() {
1871 -wall.to_dir
1872 } else {
1873 wall.to_dir
1874 };
1875
1876 Some((in_dir, room))
1877 } else {
1878 None
1879 };
1880 if let Some((in_dir, room)) = in_dir_room {
1881 let width = in_dir.orthogonal().select(wall.end - wall.start).abs();
1882 let wall_center = (wall.start + wall.end) / 2;
1883 let door_dist = wall.door_bounds().map_or(i32::MAX, |door| {
1884 (door.min - wall_center)
1885 .map(|x| x.abs())
1886 .reduce_max()
1887 .max((door.max - wall_center).map(|x| x.abs()).reduce_max())
1888 });
1889 match room {
1890 RoomKind::Garden => {
1891 if door_dist >= 2 {
1892 painter.rotated_sprite(
1893 wall_center.with_z(wall.base_alt + 1),
1894 SpriteKind::Planter,
1895 in_dir.sprite_ori(),
1896 );
1897 }
1898 },
1899 _ => {
1900 if width >= 5
1901 && door_dist > 3
1902 && wall.base_alt >= land.get_alt_approx(wall_center) as i32
1903 {
1904 painter
1905 .aabb(aabb(Aabb {
1906 min: (wall_center + in_dir.rotated_ccw().to_vec2())
1907 .with_z(wall.base_alt + 1),
1908 max: (wall_center + in_dir.rotated_cw().to_vec2())
1909 .with_z(wall.base_alt + 2),
1910 }))
1911 .fill(Fill::sprite_ori(SpriteKind::Window1, in_dir.sprite_ori()));
1912 }
1913 },
1914 }
1915 }
1916 if let Some(door) = wall.door_bounds()
1917 && !matches!(kinds, (Some(RoomKind::Garden), Some(RoomKind::Garden)))
1918 {
1919 let orth = wall.to_dir.orthogonal();
1920 painter
1921 .aabb(aabb(Aabb {
1922 min: (door.min - orth.to_vec2()).with_z(wall.base_alt),
1923 max: (door.max + orth.to_vec2()).with_z(wall.base_alt + 3),
1924 }))
1925 .fill(detail_fill.clone());
1926 painter
1927 .aabb(aabb(Aabb {
1928 min: (door.min - orth.to_vec2()).with_z(wall.base_alt - 1),
1929 max: (door.max + orth.to_vec2()).with_z(wall.base_alt - 1),
1930 }))
1931 .fill(floor_fill.clone());
1932 painter
1933 .aabb(aabb(Aabb {
1934 min: (door.min + wall.to_dir.to_vec2()).with_z(wall.base_alt),
1935 max: (door.max - wall.to_dir.to_vec2()).with_z(wall.base_alt + 2),
1936 }))
1937 .clear();
1938 if let Some((min, max, room, to_dir)) = get_door_stair(wall, door) {
1939 painter
1941 .ramp(
1942 aabb(Aabb {
1943 min: (min - to_dir.to_vec2() * 3).with_z(wall.base_alt),
1944 max: max.with_z(room.bounds.min.z + 2),
1945 }),
1946 to_dir,
1947 )
1948 .without(
1950 painter
1951 .ramp(
1952 aabb(Aabb {
1953 min: (min + to_dir.to_vec2() * 2).with_z(wall.base_alt),
1954 max: max.with_z(room.bounds.min.z - 1),
1955 }),
1956 to_dir,
1957 )
1958 )
1959 .clear();
1960 }
1961 if let Some((in_dir, _room)) = in_dir_room {
1962 let sprite = match in_dir.rotated_cw().select(door.size()) {
1963 2.. => SpriteKind::DoorWide,
1964 _ => SpriteKind::Door,
1965 };
1966 painter.rotated_sprite(
1967 in_dir
1968 .rotated_cw()
1969 .select_aabr_with(door, door.min)
1970 .with_z(wall.base_alt),
1971 sprite,
1972 in_dir.sprite_ori(),
1973 );
1974 painter.rotated_sprite(
1975 in_dir
1976 .rotated_ccw()
1977 .select_aabr_with(door, door.min)
1978 .with_z(wall.base_alt),
1979 sprite,
1980 in_dir.opposite().sprite_ori(),
1981 );
1982
1983 let dir = match field.chance(door.min.with_z(wall.base_alt), 0.5) {
1984 true => in_dir.rotated_cw(),
1985 false => in_dir.rotated_ccw(),
1986 };
1987
1988 let pos =
1989 dir.select_aabr_with(door, door.min) + dir.to_vec2() - in_dir.to_vec2();
1990
1991 painter.rotated_sprite_with_cfg(
1992 pos.with_z(wall.base_alt + 2),
1993 SpriteKind::HangingSign,
1994 in_dir.opposite().sprite_ori(),
1995 SpriteCfg {
1996 unlock: None,
1997 content: Some(Content::Plain(self.name.clone())),
1998 },
1999 );
2000 }
2001 }
2002 }
2003
2004 for roof in self.roofs.values() {
2006 if let Some((stairs_aabb, dir)) = roof.stairs {
2007 painter
2008 .aabb(aabb(dir.to_dir3().trim_aabb(
2009 Aabb {
2010 min: stairs_aabb.min.with_z(roof.min_z),
2011 max: stairs_aabb.max.with_z(roof.min_z),
2012 },
2013 (dir.to_dir3().select(stairs_aabb.size()) - 4).max(0),
2014 )))
2015 .clear();
2016 painter
2017 .ramp(aabb(stairs_aabb), dir)
2018 .fill(floor_fill.clone());
2019 painter
2020 .ramp(
2021 aabb(Dir3::NegZ.trim_aabb(dir.to_dir3().trim_aabb(stairs_aabb, 1), 1)),
2022 dir,
2023 )
2024 .clear();
2025 }
2026 }
2027 }
2028}