From baf43ede759f9ff0ca8c71de764e0389469f9ae1 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 25 Aug 2023 19:44:02 -0400 Subject: Rewrote how panel solvability is determined This fixes an edge case with LEVEL 2, and likely fixes an undiscovered issue with no-doors Pilgrimage. --- src/game_data.cpp | 5 ++ src/game_data.h | 1 + src/tracker_state.cpp | 160 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 102 insertions(+), 64 deletions(-) diff --git a/src/game_data.cpp b/src/game_data.cpp index fafc88c..4393373 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp @@ -255,6 +255,7 @@ struct GameData { if (door_it.second["event"]) { door_obj.skip_location = door_it.second["event"].as(); door_obj.skip_item = door_it.second["event"].as(); + door_obj.is_event = door_it.second["event"].as(); } if (door_it.second["item_name"]) { @@ -427,6 +428,7 @@ struct GameData { int fake_pilgrim_panel_id = AddOrGetPanel("Starting Room", "!! Fake Pilgrimage Panel"); Panel &fake_pilgrim_panel_obj = panels_[fake_pilgrim_panel_id]; + fake_pilgrim_panel_obj.non_counting = true; for (const auto &config_node : pilgrimage_config) { fake_pilgrim_panel_obj.required_doors.push_back( @@ -438,10 +440,13 @@ struct GameData { AddOrGetDoor("Starting Room", "!! Fake Pilgrimage Door"); Door &fake_pilgrim_door_obj = doors_[fake_pilgrim_door_id]; fake_pilgrim_door_obj.panels.push_back(fake_pilgrim_panel_id); + fake_pilgrim_door_obj.skip_location = true; fake_pilgrim_door_obj.skip_item = true; + fake_pilgrim_door_obj.is_event = true; int starting_room_id = AddOrGetRoom("Starting Room"); Room &starting_room_obj = rooms_[starting_room_id]; + starting_room_obj.panels.push_back(fake_pilgrim_panel_id); starting_room_obj.exits.push_back( Exit{.destination_room = AddOrGetRoom("Pilgrim Antechamber"), .door = fake_pilgrim_door_id}); diff --git a/src/game_data.h b/src/game_data.h index 31a0c87..959d5c8 100644 --- a/src/game_data.h +++ b/src/game_data.h @@ -47,6 +47,7 @@ struct Door { std::string group_name; bool skip_location = false; bool skip_item = false; + bool is_event = false; std::vector panels; bool exclude_reduce = true; std::vector progressives; diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 557e551..e462b29 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp @@ -3,12 +3,11 @@ #include #include #include -#include #include +#include #include "ap_state.h" #include "game_data.h" -#include "logger.h" namespace { @@ -16,26 +15,63 @@ struct TrackerState { std::map, bool> reachability; }; +enum Decision { kYes, kNo, kMaybe }; + TrackerState& GetState() { static TrackerState* instance = new TrackerState(); return *instance; } -bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms); +Decision IsDoorReachable_Helper(int door_id, + const std::set& reachable_rooms, + const std::set& solveable_panels) { + const Door& door_obj = GD_GetDoor(door_id); + + if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { + if (!reachable_rooms.count(door_obj.room)) { + return kMaybe; + } + + for (int panel_id : door_obj.panels) { + if (!solveable_panels.count(panel_id)) { + return kMaybe; + } + } + + return kYes; + } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && + !door_obj.group_name.empty()) { + return AP_HasItem(door_obj.group_name) ? kYes : kNo; + } else { + bool has_item = AP_HasItem(door_obj.item_name); + + if (!has_item) { + for (const ProgressiveRequirement& prog_req : door_obj.progressives) { + if (AP_HasItem(prog_req.item_name, prog_req.quantity)) { + has_item = true; + break; + } + } + } -bool IsPanelReachable_Helper(int panel_id, - const std::set& reachable_rooms) { + return has_item ? kYes : kNo; + } +} + +Decision IsPanelReachable_Helper(int panel_id, + const std::set& reachable_rooms, + const std::set& solveable_panels) { const Panel& panel_obj = GD_GetPanel(panel_id); if (!reachable_rooms.count(panel_obj.room)) { - return false; + return kMaybe; } if (panel_obj.name == "THE MASTER") { int achievements_accessible = 0; for (int achieve_id : GD_GetAchievementPanels()) { - if (IsPanelReachable_Helper(achieve_id, reachable_rooms)) { + if (solveable_panels.count(achieve_id)) { achievements_accessible++; if (achievements_accessible >= AP_GetMasteryRequirement()) { @@ -44,89 +80,60 @@ bool IsPanelReachable_Helper(int panel_id, } } - return (achievements_accessible >= AP_GetMasteryRequirement()); + return (achievements_accessible >= AP_GetMasteryRequirement()) ? kYes + : kMaybe; } - if (panel_obj.name == "LEVEL 2" && AP_GetVictoryCondition() == kLEVEL_2) { + if (panel_obj.name == "ANOTHER TRY" && AP_GetVictoryCondition() == kLEVEL_2) { int counting_panels_accessible = 0; - for (int reachable_room : reachable_rooms) { - const Room& room = GD_GetRoom(reachable_room); + for (int solved_panel_id : solveable_panels) { + const Panel& solved_panel = GD_GetPanel(solved_panel_id); - for (int roomed_panel_id : room.panels) { - const Panel& roomed_panel = GD_GetPanel(roomed_panel_id); - - if (!roomed_panel.non_counting && - IsPanelReachable_Helper(roomed_panel_id, reachable_rooms)) { - counting_panels_accessible++; - } + if (!solved_panel.non_counting) { + counting_panels_accessible++; } } - return (counting_panels_accessible >= AP_GetLevel2Requirement()); + return (counting_panels_accessible >= AP_GetLevel2Requirement() - 1) + ? kYes + : kMaybe; } for (int room_id : panel_obj.required_rooms) { if (!reachable_rooms.count(room_id)) { - return false; + return kMaybe; } } for (int door_id : panel_obj.required_doors) { - if (!IsDoorReachable_Helper(door_id, reachable_rooms)) { - return false; + 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; } } for (int panel_id : panel_obj.required_panels) { - if (!IsPanelReachable_Helper(panel_id, reachable_rooms)) { - return false; + if (!solveable_panels.count(panel_id)) { + return kMaybe; } } if (AP_IsColorShuffle()) { for (LingoColor color : panel_obj.colors) { if (!AP_HasColorItem(color)) { - return false; + return kNo; } } } - return true; -} - -bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms) { - const Door& door_obj = GD_GetDoor(door_id); - - if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { - if (!reachable_rooms.count(door_obj.room)) { - return false; - } - - for (int panel_id : door_obj.panels) { - if (!IsPanelReachable_Helper(panel_id, reachable_rooms)) { - return false; - } - } - - return true; - } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && - !door_obj.group_name.empty()) { - return AP_HasItem(door_obj.group_name); - } else { - bool has_item = AP_HasItem(door_obj.item_name); - - if (!has_item) { - for (const ProgressiveRequirement& prog_req : door_obj.progressives) { - if (AP_HasItem(prog_req.item_name, prog_req.quantity)) { - has_item = true; - break; - } - } - } - - return has_item; - } + return kYes; } } // namespace @@ -135,7 +142,9 @@ void RecalculateReachability() { GetState().reachability.clear(); std::set reachable_rooms; + std::set solveable_panels; + std::list panel_boundary; std::list flood_boundary; flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")}); @@ -143,6 +152,22 @@ void RecalculateReachability() { while (reachable_changed) { reachable_changed = false; + std::list new_panel_boundary; + for (int panel_id : panel_boundary) { + if (solveable_panels.count(panel_id)) { + continue; + } + + 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); + } + } + std::list new_boundary; for (const Exit& room_exit : flood_boundary) { if (reachable_rooms.count(room_exit.destination_room)) { @@ -151,9 +176,11 @@ void RecalculateReachability() { bool valid_transition = false; if (room_exit.door.has_value()) { - if (IsDoorReachable_Helper(*room_exit.door, reachable_rooms)) { + Decision door_reachable = IsDoorReachable_Helper( + *room_exit.door, reachable_rooms, solveable_panels); + if (door_reachable == kYes) { valid_transition = true; - } else { + } else if (door_reachable == kMaybe) { new_boundary.push_back(room_exit); } } else { @@ -183,20 +210,25 @@ void RecalculateReachability() { } } } + + for (int panel_id : room_obj.panels) { + new_panel_boundary.push_back(panel_id); + } } } flood_boundary = new_boundary; + panel_boundary = new_panel_boundary; } for (const MapArea& map_area : GD_GetMapAreas()) { - for (int section_id = 0; section_id < map_area.locations.size(); + for (size_t section_id = 0; section_id < map_area.locations.size(); section_id++) { const Location& location_section = map_area.locations.at(section_id); bool reachable = reachable_rooms.count(location_section.room); if (reachable) { for (int panel_id : location_section.panels) { - reachable &= IsPanelReachable_Helper(panel_id, reachable_rooms); + reachable &= (solveable_panels.count(panel_id) == 1); } } -- cgit 1.4.1