From efa0587b4399a45faecf5aa941ff75a40595a124 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 26 Feb 2024 19:15:20 -0500 Subject: Added real pilgrimage detection --- src/tracker_state.cpp | 417 ++++++++++++++++++++++++++++---------------------- 1 file changed, 237 insertions(+), 180 deletions(-) (limited to 'src/tracker_state.cpp') diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 43f84b4..cc941ef 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp @@ -24,242 +24,299 @@ TrackerState& GetState() { return *instance; } -Decision IsDoorReachable_Helper(int door_id, - const std::set& reachable_rooms, - const std::set& solveable_panels) { - const Door& door_obj = GD_GetDoor(door_id); +struct StateCalculatorOptions { + std::string start = "Menu"; + bool pilgrimage = false; +}; - if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { - if (!reachable_rooms.count(door_obj.room)) { - return kMaybe; - } +class StateCalculator { + public: + StateCalculator() = default; - for (int panel_id : door_obj.panels) { - if (!solveable_panels.count(panel_id)) { - return kMaybe; - } - } + explicit StateCalculator(StateCalculatorOptions options) + : options_(options) {} - return kYes; - } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && - !door_obj.group_name.empty()) { - return AP_HasItem(door_obj.group_ap_item_id) ? kYes : kNo; - } else { - bool has_item = AP_HasItem(door_obj.ap_item_id); + void Calculate() { + std::list panel_boundary; + std::list flood_boundary; + flood_boundary.push_back( + {.destination_room = GD_GetRoomByName(options_.start)}); + + bool reachable_changed = true; + while (reachable_changed) { + reachable_changed = false; - if (!has_item) { - for (const ProgressiveRequirement& prog_req : door_obj.progressives) { - if (AP_HasItem(prog_req.ap_item_id, prog_req.quantity)) { - has_item = true; - break; + std::list new_panel_boundary; + for (int panel_id : panel_boundary) { + if (solveable_panels_.count(panel_id)) { + continue; + } + + Decision panel_reachable = IsPanelReachable(panel_id); + if (panel_reachable == kYes) { + solveable_panels_.insert(panel_id); + reachable_changed = true; + } else if (panel_reachable == kMaybe) { + new_panel_boundary.push_back(panel_id); } } - } - return has_item ? kYes : kNo; - } -} + std::list new_boundary; + for (const Exit& room_exit : flood_boundary) { + if (reachable_rooms_.count(room_exit.destination_room)) { + continue; + } -Decision IsPanelReachable_Helper(int panel_id, - const std::set& reachable_rooms, - const std::set& solveable_panels) { - const Panel& panel_obj = GD_GetPanel(panel_id); + bool valid_transition = false; - if (!reachable_rooms.count(panel_obj.room)) { - return kMaybe; - } + Decision exit_usable = IsExitUsable(room_exit); + if (exit_usable == kYes) { + valid_transition = true; + } else if (exit_usable == kMaybe) { + new_boundary.push_back(room_exit); + } + + if (valid_transition) { + reachable_rooms_.insert(room_exit.destination_room); + reachable_changed = true; + + const Room& room_obj = GD_GetRoom(room_exit.destination_room); + for (const Exit& out_edge : room_obj.exits) { + if (out_edge.type != EntranceType::kPainting || + !AP_IsPaintingShuffle()) { + new_boundary.push_back(out_edge); + } + } + + if (AP_IsPaintingShuffle()) { + for (const PaintingExit& out_edge : room_obj.paintings) { + if (AP_GetPaintingMapping().count(out_edge.id)) { + Exit painting_exit; + painting_exit.destination_room = GD_GetRoomForPainting( + AP_GetPaintingMapping().at(out_edge.id)); + painting_exit.door = out_edge.door; + + new_boundary.push_back(painting_exit); + } + } + } - if (panel_obj.name == "THE MASTER") { - int achievements_accessible = 0; + if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") { + new_boundary.push_back( + {.destination_room = GD_GetRoomByName("Outside The Undeterred"), + .type = EntranceType::kPainting}); + } - for (int achieve_id : GD_GetAchievementPanels()) { - if (solveable_panels.count(achieve_id)) { - achievements_accessible++; + if (AP_IsPilgrimageEnabled()) { + if (room_obj.name == "Hub Room") { + new_boundary.push_back( + {.destination_room = GD_GetRoomByName("Pilgrim Antechamber"), + .type = EntranceType::kPilgrimage}); + } + } else { + if (room_obj.name == "Starting Room") { + new_boundary.push_back( + {.destination_room = GD_GetRoomByName("Pilgrim Antechamber"), + .door = + GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"), + .type = EntranceType::kPainting}); + } + } - if (achievements_accessible >= AP_GetMasteryRequirement()) { - break; + for (int panel_id : room_obj.panels) { + new_panel_boundary.push_back(panel_id); + } } } - } - return (achievements_accessible >= AP_GetMasteryRequirement()) ? kYes - : kMaybe; + flood_boundary = new_boundary; + panel_boundary = new_panel_boundary; + } } - if ((panel_obj.name == "ANOTHER TRY" || panel_obj.name == "LEVEL 2") && - AP_GetLevel2Requirement() > 1) { - int counting_panels_accessible = 0; + const std::set& GetReachableRooms() const { return reachable_rooms_; } - for (int solved_panel_id : solveable_panels) { - const Panel& solved_panel = GD_GetPanel(solved_panel_id); + const std::set& GetSolveablePanels() const { return solveable_panels_; } - if (!solved_panel.non_counting) { - counting_panels_accessible++; + private: + Decision IsDoorReachable(int door_id) { + const Door& door_obj = GD_GetDoor(door_id); + + if (!AP_IsPilgrimageEnabled() && + door_obj.item_name == "Pilgrim Room - Sun Painting") { + return AP_HasItem(door_obj.ap_item_id) ? kYes : kNo; + } else if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { + if (!reachable_rooms_.count(door_obj.room)) { + return kMaybe; } - } - return (counting_panels_accessible >= AP_GetLevel2Requirement() - 1) - ? kYes - : kMaybe; - } + for (int panel_id : door_obj.panels) { + if (!solveable_panels_.count(panel_id)) { + return kMaybe; + } + } - for (int room_id : panel_obj.required_rooms) { - if (!reachable_rooms.count(room_id)) { - return kMaybe; - } - } + return kYes; + } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && + !door_obj.group_name.empty()) { + return AP_HasItem(door_obj.group_ap_item_id) ? kYes : kNo; + } else { + bool has_item = AP_HasItem(door_obj.ap_item_id); + + if (!has_item) { + for (const ProgressiveRequirement& prog_req : door_obj.progressives) { + if (AP_HasItem(prog_req.ap_item_id, prog_req.quantity)) { + has_item = true; + break; + } + } + } - for (int door_id : panel_obj.required_doors) { - Decision door_reachable = - IsDoorReachable_Helper(door_id, reachable_rooms, solveable_panels); - if (door_reachable == kNo) { - const Door& door_obj = GD_GetDoor(door_id); - return (door_obj.is_event || AP_GetDoorShuffleMode() == kNO_DOORS) - ? kMaybe - : kNo; - } else if (door_reachable == kMaybe) { - return kMaybe; + return has_item ? kYes : kNo; } } - for (int panel_id : panel_obj.required_panels) { - if (!solveable_panels.count(panel_id)) { + Decision IsPanelReachable(int panel_id) { + const Panel& panel_obj = GD_GetPanel(panel_id); + + if (!reachable_rooms_.count(panel_obj.room)) { return kMaybe; } - } - if (AP_IsColorShuffle()) { - for (LingoColor color : panel_obj.colors) { - if (!AP_HasItem(GD_GetItemIdForColor(color))) { - return kNo; - } - } - } + if (panel_obj.name == "THE MASTER") { + int achievements_accessible = 0; - return kYes; -} + for (int achieve_id : GD_GetAchievementPanels()) { + if (solveable_panels_.count(achieve_id)) { + achievements_accessible++; -} // namespace + if (achievements_accessible >= AP_GetMasteryRequirement()) { + break; + } + } + } -void RecalculateReachability() { - std::set reachable_rooms; - std::set solveable_panels; + return (achievements_accessible >= AP_GetMasteryRequirement()) ? kYes + : kMaybe; + } - std::list panel_boundary; - std::list flood_boundary; - flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")}); + if ((panel_obj.name == "ANOTHER TRY" || panel_obj.name == "LEVEL 2") && + AP_GetLevel2Requirement() > 1) { + int counting_panels_accessible = 0; - if (AP_HasEarlyColorHallways()) { - flood_boundary.push_back( - {.destination_room = GD_GetRoomByName("Outside The Undeterred")}); - } + for (int solved_panel_id : solveable_panels_) { + const Panel& solved_panel = GD_GetPanel(solved_panel_id); - bool reachable_changed = true; - while (reachable_changed) { - reachable_changed = false; + if (!solved_panel.non_counting) { + counting_panels_accessible++; + } + } + + return (counting_panels_accessible >= AP_GetLevel2Requirement() - 1) + ? kYes + : kMaybe; + } - std::list new_panel_boundary; - for (int panel_id : panel_boundary) { - if (solveable_panels.count(panel_id)) { - continue; + for (int room_id : panel_obj.required_rooms) { + if (!reachable_rooms_.count(room_id)) { + return kMaybe; } + } - Decision panel_reachable = - IsPanelReachable_Helper(panel_id, reachable_rooms, solveable_panels); - if (panel_reachable == kYes) { - solveable_panels.insert(panel_id); - reachable_changed = true; - } else if (panel_reachable == kMaybe) { - new_panel_boundary.push_back(panel_id); + for (int door_id : panel_obj.required_doors) { + Decision door_reachable = IsDoorReachable(door_id); + if (door_reachable == kNo) { + const Door& door_obj = GD_GetDoor(door_id); + return (door_obj.is_event || AP_GetDoorShuffleMode() == kNO_DOORS) + ? kMaybe + : kNo; + } else if (door_reachable == kMaybe) { + return kMaybe; } } - std::list new_boundary; - for (const Exit& room_exit : flood_boundary) { - if (reachable_rooms.count(room_exit.destination_room)) { - continue; + for (int panel_id : panel_obj.required_panels) { + if (!solveable_panels_.count(panel_id)) { + return kMaybe; } + } - bool valid_transition = false; - if (room_exit.door.has_value()) { - Decision door_reachable = kMaybe; - if (room_exit.sunwarp) { - if (AP_GetSunwarpAccess() == kSUNWARP_ACCESS_NORMAL) { - door_reachable = kYes; - } else if (AP_GetSunwarpAccess() == kSUNWARP_ACCESS_DISABLED) { - door_reachable = kNo; - } else { - door_reachable = IsDoorReachable_Helper( - *room_exit.door, reachable_rooms, solveable_panels); - } - } else { - door_reachable = IsDoorReachable_Helper( - *room_exit.door, reachable_rooms, solveable_panels); - } - if (door_reachable == kYes) { - valid_transition = true; - } else if (door_reachable == kMaybe) { - new_boundary.push_back(room_exit); - } - } else if (room_exit.pilgrimage) { - Decision pilgrimage_reachable = kYes; - if (AP_GetSunwarpAccess() == kSUNWARP_ACCESS_DISABLED) { - pilgrimage_reachable = kNo; + if (AP_IsColorShuffle()) { + for (LingoColor color : panel_obj.colors) { + if (!AP_HasItem(GD_GetItemIdForColor(color))) { + return kNo; } - if (pilgrimage_reachable == kYes) { - for (int door_id : GD_GetPilgrimageDoors( - AP_GetSunwarpAccess() == kSUNWARP_ACCESS_UNLOCK || - AP_GetSunwarpAccess() == kSUNWARP_ACCESS_PROGRESSIVE)) { - pilgrimage_reachable = IsDoorReachable_Helper( - door_id, reachable_rooms, solveable_panels); - if (pilgrimage_reachable != kYes) { - break; - } - } - } - if (pilgrimage_reachable == kYes) { - valid_transition = true; - } else if (pilgrimage_reachable == kMaybe) { - new_boundary.push_back(room_exit); - } - } else { - valid_transition = true; } + } - if (valid_transition) { - reachable_rooms.insert(room_exit.destination_room); - reachable_changed = true; + return kYes; + } - const Room& room_obj = GD_GetRoom(room_exit.destination_room); - for (const Exit& out_edge : room_obj.exits) { - if (!out_edge.painting || !AP_IsPaintingShuffle()) { - new_boundary.push_back(out_edge); - } + Decision IsExitUsable(const Exit& room_exit) { + if (room_exit.type == EntranceType::kPilgrimage) { + if (options_.pilgrimage || !AP_IsPilgrimageEnabled()) { + return kNo; + } + + static const std::vector> + pilgrimage_pairs = { + {"Crossroads", "Hot Crusts Area"}, + // {"Orange Tower Third Floor", "Orange Tower Third Floor"}, + {"Outside The Initiated", "Orange Tower First Floor"}, + {"Outside The Undeterred", "Orange Tower Fourth Floor"}, + {"Color Hunt", "Outside The Agreeable"}}; + + for (const auto& [from_room, to_room] : pilgrimage_pairs) { + StateCalculator pilgrimage_calculator( + {.start = from_room, .pilgrimage = true}); + pilgrimage_calculator.Calculate(); + + if (!pilgrimage_calculator.GetReachableRooms().count( + GD_GetRoomByName(to_room))) { + return kMaybe; } + } - if (AP_IsPaintingShuffle()) { - for (const PaintingExit& out_edge : room_obj.paintings) { - if (AP_GetPaintingMapping().count(out_edge.id)) { - Exit painting_exit; - painting_exit.destination_room = GD_GetRoomForPainting( - AP_GetPaintingMapping().at(out_edge.id)); - painting_exit.door = out_edge.door; + return kYes; + } - new_boundary.push_back(painting_exit); - } - } - } + if (options_.pilgrimage) { + if (room_exit.type == EntranceType::kWarp || + room_exit.type == EntranceType::kSunwarp) { + return kNo; + } + } - for (int panel_id : room_obj.panels) { - new_panel_boundary.push_back(panel_id); - } + if (room_exit.type == EntranceType::kSunwarp) { + if (AP_GetSunwarpAccess() == kSUNWARP_ACCESS_NORMAL) { + return kYes; + } else if (AP_GetSunwarpAccess() == kSUNWARP_ACCESS_DISABLED) { + return kNo; } } - flood_boundary = new_boundary; - panel_boundary = new_panel_boundary; + if (room_exit.door.has_value()) { + return IsDoorReachable(*room_exit.door); + } + + return kYes; } + StateCalculatorOptions options_; + + std::set reachable_rooms_; + std::set solveable_panels_; +}; + +} // namespace + +void RecalculateReachability() { + StateCalculator state_calculator; + state_calculator.Calculate(); + + const std::set& reachable_rooms = state_calculator.GetReachableRooms(); + const std::set& solveable_panels = state_calculator.GetSolveablePanels(); + std::map new_reachability; for (const MapArea& map_area : GD_GetMapAreas()) { for (size_t section_id = 0; section_id < map_area.locations.size(); -- cgit 1.4.1