442 std::set<int> scoped_room_ids;
443 std::map<int, const core::DungeonRoom*> scoped_rooms_by_id;
444 if (
const auto* dungeon =
447 for (
const auto& dungeon_room : dungeon->rooms) {
451 scoped_room_ids.insert(dungeon_room.id);
452 scoped_rooms_by_id[dungeon_room.id] = &dungeon_room;
455 if (!dungeon_room.floor.empty() &&
463 std::map<std::pair<int, int>,
int> occupied_slots;
464 std::queue<int> to_visit;
465 std::set<std::tuple<int, int, DungeonConnectedLinkType, int, int16_t>>
468 auto track_room_bounds = [&](
int room_id) {
469 const auto& placement = graph.
room_positions[
static_cast<size_t>(room_id)];
476 auto registry_placement_for =
477 [&](
int room_id) -> std::optional<std::pair<int, int>> {
478 const auto it = scoped_rooms_by_id.find(room_id);
479 if (it == scoped_rooms_by_id.end() || !it->second->has_grid_position) {
482 return std::make_pair(it->second->grid_col, it->second->grid_row);
485 auto place_room = [&](
int room_id, std::pair<int, int> desired_placement,
486 bool connected_to_start) {
490 const size_t index =
static_cast<size_t>(room_id);
492 if (connected_to_start &&
500 std::pair<int, int> placement = desired_placement;
501 const auto occupied = occupied_slots.find(placement);
502 if (occupied != occupied_slots.end() && occupied->second != room_id) {
503 placement = FindConnectedTransportPlacement(
504 occupied_slots, desired_placement.first, desired_placement.second);
508 graph.
room_positions[index] = {placement.first, placement.second,
true,
510 occupied_slots[placement] = room_id;
512 if (!connected_to_start) {
519 track_room_bounds(room_id);
523 auto in_dungeon_scope = [&](
int candidate_room_id) {
527 return scoped_room_ids.find(candidate_room_id) != scoped_room_ids.end();
532 registry_placement_for(start_room_id).value_or(std::make_pair(0, 0)),
534 to_visit.push(start_room_id);
536 while (!to_visit.empty()) {
537 const int room_id = to_visit.front();
545 const auto outgoing_diagnostics =
551 for (
const auto& issue : outgoing_diagnostics.staircase_issues) {
554 const auto& outgoing_links = outgoing_diagnostics.links;
556 for (
const auto& link : outgoing_links) {
565 if (!in_dungeon_scope(link.to_room_id)) {
566 if (seen_links.insert(MakeConnectedLinkKey(link)).second) {
576 if (seen_links.insert(MakeConnectedLinkKey(link)).second) {
577 graph.
links.push_back(link);
580 if (!graph.
room_mask[
static_cast<size_t>(link.to_room_id)]) {
581 const auto& source_placement =
583 const std::pair<int, int> placement =
584 registry_placement_for(link.to_room_id)
586 ? FindConnectedDoorPlacement(
587 occupied_slots, source_placement.col,
588 source_placement.row, link.direction)
589 : FindConnectedTransportPlacement(
590 occupied_slots, source_placement.col,
591 source_placement.row));
592 place_room(link.to_room_id, placement,
true);
593 to_visit.push(link.to_room_id);
603 const auto start_placement =
605 for (
int room_id : scoped_room_ids) {
606 if (graph.
room_mask[
static_cast<size_t>(room_id)]) {
609 const std::pair<int, int> placement =
610 registry_placement_for(room_id).value_or(
611 FindConnectedTransportPlacement(
612 occupied_slots, start_placement.col, start_placement.row));
613 place_room(room_id, placement,
false);