192 has_reciprocal_door) {
195 const auto& doors = room.
GetDoors();
196 for (
size_t door_index = 0; door_index < doors.size(); ++door_index) {
197 const auto& door = doors[door_index];
198 if (IsExitDoorType(door.type)) {
206 if (has_reciprocal_door &&
207 !has_reciprocal_door(neighbor,
OppositeDir(door.direction))) {
216 link.
door_index =
static_cast<int>(door_index);
218 result.
links.push_back(link);
242 std::array<bool, 4> slot_consumed{
false,
false,
false,
false};
243 std::array<int16_t, 4> slot_object_id{
244 static_cast<int16_t
>(-1),
static_cast<int16_t
>(-1),
245 static_cast<int16_t
>(-1),
static_cast<int16_t
>(-1)};
248 if (!IsHeaderBackedInterroomStaircaseObject(
object.id_)) {
251 if (next_slot >= 4) {
259 slot_consumed[next_slot] =
true;
260 slot_object_id[next_slot] =
object.id_;
264 for (
int slot = 0; slot < 4; ++slot) {
265 const int stair_room =
static_cast<int>(room.
staircase_room(slot));
266 const bool header_valid =
268 if (slot_consumed[slot]) {
277 result.
links.push_back(link);
287 }
else if (header_valid) {
297 const int holewarp_room =
static_cast<int>(room.
holewarp());
304 result.
links.push_back(link);
459 std::set<int> scoped_room_ids;
460 std::map<int, const core::DungeonRoom*> scoped_rooms_by_id;
461 if (
const auto* dungeon =
464 for (
const auto& dungeon_room : dungeon->rooms) {
468 scoped_room_ids.insert(dungeon_room.id);
469 scoped_rooms_by_id[dungeon_room.id] = &dungeon_room;
472 if (!dungeon_room.floor.empty() &&
480 std::map<std::pair<int, int>,
int> occupied_slots;
481 std::queue<int> to_visit;
482 std::set<std::tuple<int, int, DungeonConnectedLinkType, int, int16_t>>
485 auto track_room_bounds = [&](
int room_id) {
486 const auto& placement = graph.
room_positions[
static_cast<size_t>(room_id)];
493 auto registry_placement_for =
494 [&](
int room_id) -> std::optional<std::pair<int, int>> {
495 const auto it = scoped_rooms_by_id.find(room_id);
496 if (it == scoped_rooms_by_id.end() || !it->second->has_grid_position) {
499 return std::make_pair(it->second->grid_col, it->second->grid_row);
502 auto place_room = [&](
int room_id, std::pair<int, int> desired_placement,
503 bool connected_to_start) {
507 const size_t index =
static_cast<size_t>(room_id);
509 if (connected_to_start &&
517 std::pair<int, int> placement = desired_placement;
518 const auto occupied = occupied_slots.find(placement);
519 if (occupied != occupied_slots.end() && occupied->second != room_id) {
520 placement = FindConnectedTransportPlacement(
521 occupied_slots, desired_placement.first, desired_placement.second);
525 graph.
room_positions[index] = {placement.first, placement.second,
true,
527 occupied_slots[placement] = room_id;
529 if (!connected_to_start) {
536 track_room_bounds(room_id);
540 auto in_dungeon_scope = [&](
int candidate_room_id) {
544 return scoped_room_ids.find(candidate_room_id) != scoped_room_ids.end();
549 registry_placement_for(start_room_id).value_or(std::make_pair(0, 0)),
551 to_visit.push(start_room_id);
553 while (!to_visit.empty()) {
554 const int room_id = to_visit.front();
562 const auto outgoing_diagnostics =
568 for (
const auto& issue : outgoing_diagnostics.staircase_issues) {
571 const auto& outgoing_links = outgoing_diagnostics.links;
573 for (
const auto& link : outgoing_links) {
582 if (!in_dungeon_scope(link.to_room_id)) {
583 if (seen_links.insert(MakeConnectedLinkKey(link)).second) {
593 if (seen_links.insert(MakeConnectedLinkKey(link)).second) {
594 graph.
links.push_back(link);
597 if (!graph.
room_mask[
static_cast<size_t>(link.to_room_id)]) {
598 const auto& source_placement =
600 const std::pair<int, int> placement =
601 registry_placement_for(link.to_room_id)
603 ? FindConnectedDoorPlacement(
604 occupied_slots, source_placement.col,
605 source_placement.row, link.direction)
606 : FindConnectedTransportPlacement(
607 occupied_slots, source_placement.col,
608 source_placement.row));
609 place_room(link.to_room_id, placement,
true);
610 to_visit.push(link.to_room_id);
620 const auto start_placement =
622 for (
int room_id : scoped_room_ids) {
623 if (graph.
room_mask[
static_cast<size_t>(room_id)]) {
626 const std::pair<int, int> placement =
627 registry_placement_for(room_id).value_or(
628 FindConnectedTransportPlacement(
629 occupied_slots, start_placement.col, start_placement.row));
630 place_room(room_id, placement,
false);