171 const std::set<uint8_t>& warp_tag_ids) {
174 const auto& doors = room.
GetDoors();
175 for (
size_t door_index = 0; door_index < doors.size(); ++door_index) {
176 const auto& door = doors[door_index];
185 if (has_reciprocal_door &&
186 !has_reciprocal_door(neighbor,
OppositeDir(door.direction))) {
195 link.
door_index =
static_cast<int>(door_index);
197 result.
links.push_back(link);
205 std::array<bool, 4> slot_consumed{
false,
false,
false,
false};
206 std::array<int16_t, 4> slot_object_id{
207 static_cast<int16_t
>(-1),
static_cast<int16_t
>(-1),
208 static_cast<int16_t
>(-1),
static_cast<int16_t
>(-1)};
211 if (!IsHeaderBackedInterroomStaircaseObject(
object.id_)) {
214 if (next_slot >= 4) {
222 slot_consumed[next_slot] =
true;
223 slot_object_id[next_slot] =
object.id_;
227 for (
int slot = 0; slot < 4; ++slot) {
232 const bool header_valid =
234 if (slot_consumed[slot]) {
243 result.
links.push_back(link);
254 !warp_tag_ids.contains(
static_cast<uint8_t
>(room.
tag1())) &&
255 !warp_tag_ids.contains(
static_cast<uint8_t
>(room.
tag2()))) {
269 const auto& facts = link_facts ? *link_facts : loaded_facts;
271 room_id, [&](
int id) {
return id == room_id ? &facts :
nullptr; },
273 for (
const auto& shared : shared_links) {
274 if (!shared.strong || shared.to < 0 ||
285 result.
links.push_back(link);
445 std::set<int> scoped_room_ids;
446 std::map<int, const core::DungeonRoom*> scoped_rooms_by_id;
447 if (
const auto* dungeon =
450 for (
const auto& dungeon_room : dungeon->rooms) {
454 scoped_room_ids.insert(dungeon_room.id);
455 scoped_rooms_by_id[dungeon_room.id] = &dungeon_room;
458 if (!dungeon_room.floor.empty() &&
466 const auto custom_objects =
469 const std::set<uint8_t> warp_tags =
471 : std::set<uint8_t>{};
477 pit_table = &rom_pit_table;
479 auto collect_diagnostics = [&](
int room_id,
zelda3::Room& room) {
486 const auto loaded_facts =
493 facts.ok() ? &*facts : &loaded_facts, warp_tags);
496 std::map<std::pair<int, int>,
int> occupied_slots;
497 std::queue<int> to_visit;
498 std::set<std::tuple<int, int, DungeonConnectedLinkType, int, int16_t>>
501 auto track_room_bounds = [&](
int room_id) {
502 const auto& placement = graph.
room_positions[
static_cast<size_t>(room_id)];
509 auto registry_placement_for =
510 [&](
int room_id) -> std::optional<std::pair<int, int>> {
511 const auto it = scoped_rooms_by_id.find(room_id);
512 if (it == scoped_rooms_by_id.end() || !it->second->has_grid_position) {
515 return std::make_pair(it->second->grid_col, it->second->grid_row);
518 auto place_room = [&](
int room_id, std::pair<int, int> desired_placement,
519 bool connected_to_start) {
523 const size_t index =
static_cast<size_t>(room_id);
525 if (connected_to_start &&
533 std::pair<int, int> placement = desired_placement;
534 const auto occupied = occupied_slots.find(placement);
535 if (occupied != occupied_slots.end() && occupied->second != room_id) {
536 placement = FindConnectedTransportPlacement(
537 occupied_slots, desired_placement.first, desired_placement.second);
541 graph.
room_positions[index] = {placement.first, placement.second,
true,
543 occupied_slots[placement] = room_id;
545 if (!connected_to_start) {
552 track_room_bounds(room_id);
556 auto in_dungeon_scope = [&](
int candidate_room_id) {
560 return scoped_room_ids.find(candidate_room_id) != scoped_room_ids.end();
565 registry_placement_for(start_room_id).value_or(std::make_pair(0, 0)),
567 to_visit.push(start_room_id);
569 while (!to_visit.empty()) {
570 const int room_id = to_visit.front();
578 const auto outgoing_diagnostics = collect_diagnostics(room_id, *room);
580 for (
const auto& issue : outgoing_diagnostics.staircase_issues) {
583 const auto& outgoing_links = outgoing_diagnostics.links;
585 for (
const auto& link : outgoing_links) {
594 if (!in_dungeon_scope(link.to_room_id)) {
595 if (seen_links.insert(MakeConnectedLinkKey(link)).second) {
605 if (seen_links.insert(MakeConnectedLinkKey(link)).second) {
606 graph.
links.push_back(link);
609 if (!graph.
room_mask[
static_cast<size_t>(link.to_room_id)]) {
610 const auto& source_placement =
612 const std::pair<int, int> placement =
613 registry_placement_for(link.to_room_id)
615 ? FindConnectedDoorPlacement(
616 occupied_slots, source_placement.col,
617 source_placement.row, link.direction)
618 : FindConnectedTransportPlacement(
619 occupied_slots, source_placement.col,
620 source_placement.row));
621 place_room(link.to_room_id, placement,
true);
622 to_visit.push(link.to_room_id);
632 const auto start_placement =
634 for (
int room_id : scoped_room_ids) {
635 if (graph.
room_mask[
static_cast<size_t>(room_id)]) {
646 const std::pair<int, int> placement =
647 registry_placement_for(room_id).value_or(
648 FindConnectedTransportPlacement(
649 occupied_slots, start_placement.col, start_placement.row));
650 place_room(room_id, placement,
false);