From e290161d58c98e73ea185855e79efad19cb111a2 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 8 Mar 2025 00:29:17 -0500 Subject: Added postgame detection --- src/ap_state.cpp | 6 ++ src/ap_state.h | 2 + src/area_popup.cpp | 11 ++++ src/game_data.cpp | 4 +- src/global.cpp | 16 ++--- src/global.h | 2 + src/tracker_panel.cpp | 12 +++- src/tracker_state.cpp | 164 +++++++++++++++++++++++++++++++++++++++++--------- src/tracker_state.h | 6 ++ 9 files changed, 186 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 2236d6a..cbe622a 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp @@ -82,6 +82,7 @@ struct APState { bool pilgrimage_allows_paintings = false; SunwarpAccess sunwarp_access = kSUNWARP_ACCESS_NORMAL; bool sunwarp_shuffle = false; + bool postgame_shuffle = true; std::map painting_mapping; std::set painting_codomain; @@ -146,6 +147,7 @@ struct APState { sunwarp_access = kSUNWARP_ACCESS_NORMAL; sunwarp_shuffle = false; sunwarp_mapping.clear(); + postgame_shuffle = true; } apclient->set_room_info_handler( @@ -470,6 +472,8 @@ struct APState { : kSUNWARP_ACCESS_NORMAL; sunwarp_shuffle = slot_data.contains("shuffle_sunwarps") && slot_data["shuffle_sunwarps"].get() == 1; + postgame_shuffle = slot_data.contains("shuffle_postgame") && + slot_data["shuffle_postgame"].get() == 1; if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { painting_mapping.clear(); @@ -798,6 +802,8 @@ std::map AP_GetSunwarpMapping() { return GetState().sunwarp_mapping; } +bool AP_IsPostgameShuffle() { return GetState().postgame_shuffle; } + bool AP_HasReachedGoal() { return GetState().HasReachedGoal(); } std::optional> AP_GetPlayerPosition() { diff --git a/src/ap_state.h b/src/ap_state.h index 2da0b8e..e2ff22b 100644 --- a/src/ap_state.h +++ b/src/ap_state.h @@ -100,6 +100,8 @@ bool AP_IsSunwarpShuffle(); std::map AP_GetSunwarpMapping(); +bool AP_IsPostgameShuffle(); + bool AP_HasReachedGoal(); std::optional> AP_GetPlayerPosition(); diff --git a/src/area_popup.cpp b/src/area_popup.cpp index 9c7406b..d7f45b6 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp @@ -52,6 +52,9 @@ void AreaPopup::UpdateIndicators() { for (int section_id = 0; section_id < map_area.locations.size(); section_id++) { const Location& location = map_area.locations.at(section_id); + if (IsLocationPostgame(location.ap_location_id)) { + continue; + } if (tracker_panel->IsPanelsMode()) { if (!location.single_panel) { @@ -78,6 +81,10 @@ void AreaPopup::UpdateIndicators() { if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { for (int painting_id : map_area.paintings) { + if (IsPaintingPostgame(painting_id)) { + continue; + } + const PaintingExit& painting = GD_GetPaintingExit(painting_id); wxSize item_extent = mem_dc.GetTextExtent(painting.display_name); int item_height = @@ -149,6 +156,10 @@ void AreaPopup::UpdateIndicators() { if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { for (int painting_id : map_area.paintings) { + if (IsPaintingPostgame(painting_id)) { + continue; + } + const PaintingExit& painting = GD_GetPaintingExit(painting_id); bool reachable = IsPaintingReachable(painting_id); diff --git a/src/game_data.cpp b/src/game_data.cpp index e92e6a2..5fbd244 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp @@ -609,7 +609,7 @@ struct GameData { // Only locations for the panels are kept here. std::map> locations_by_name; - for (const Panel &panel : panels_) { + for (Panel &panel : panels_) { int room_id = panel.room; std::string room_name = rooms_[room_id].name; @@ -625,6 +625,8 @@ struct GameData { area_name = location_name.substr(0, divider_pos); section_name = location_name.substr(divider_pos + 3); } + } else { + panel.location_name = location_name; } if (fold_areas.count(area_name)) { diff --git a/src/global.cpp b/src/global.cpp index 1eb3f8d..63f4a19 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -26,17 +26,19 @@ std::string GetAbsolutePath(std::string_view path) { return (GetExecutableDirectory() / path).string(); } -bool IsLocationWinCondition(const Location& location) { +std::string GetWinCondition() { switch (AP_GetVictoryCondition()) { case kTHE_END: - return location.ap_location_name == - "Orange Tower Seventh Floor - THE END"; + return "Orange Tower Seventh Floor - THE END"; case kTHE_MASTER: - return location.ap_location_name == - "Orange Tower Seventh Floor - THE MASTER"; + return "Orange Tower Seventh Floor - THE MASTER"; case kLEVEL_2: - return location.ap_location_name == "Second Room - LEVEL 2"; + return "Second Room - LEVEL 2"; case kPILGRIMAGE: - return location.ap_location_name == "Pilgrim Antechamber - PILGRIM"; + return "Pilgrim Antechamber - PILGRIM"; } } + +bool IsLocationWinCondition(const Location& location) { + return location.ap_location_name == GetWinCondition(); +} diff --git a/src/global.h b/src/global.h index 31ebde3..bdfa7ae 100644 --- a/src/global.h +++ b/src/global.h @@ -10,6 +10,8 @@ const std::filesystem::path& GetExecutableDirectory(); std::string GetAbsolutePath(std::string_view path); +std::string GetWinCondition(); + bool IsLocationWinCondition(const Location& location); #endif /* end of include guard: GLOBAL_H_44945DBA */ diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 81b58cc..b4e6697 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp @@ -203,7 +203,9 @@ void TrackerPanel::Redraw() { for (AreaIndicator &area : areas_) { const MapArea &map_area = GD_GetMapArea(area.area_id); - if (panels_mode_) { + if (IsAreaPostgame(area.area_id)) { + area.active = false; + } else if (panels_mode_) { area.active = map_area.has_single_panel; } else if (!AP_IsLocationVisible(map_area.classification) && !(map_area.hunt && GetTrackerConfig().show_hunt_panels) && @@ -221,7 +223,9 @@ void TrackerPanel::Redraw() { bool has_unreachable_unchecked = false; for (const Location §ion : map_area.locations) { bool has_unchecked = false; - if (IsLocationWinCondition(section)) { + if (IsLocationPostgame(section.ap_location_id)) { + // Nope. + } else if (IsLocationWinCondition(section)) { has_unchecked = !AP_HasReachedGoal(); } else if (panels_mode_) { if (section.single_panel) { @@ -249,6 +253,10 @@ void TrackerPanel::Redraw() { if (AP_IsPaintingShuffle() && !panels_mode_) { for (int painting_id : map_area.paintings) { + if (IsPaintingPostgame(painting_id)) { + continue; + } + const PaintingExit &painting = GD_GetPaintingExit(painting_id); bool reachable = IsPaintingReachable(painting_id); if (!reachable || !AP_IsPaintingChecked(painting.internal_id)) { diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index f7244a7..bcee1d6 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp @@ -12,6 +12,7 @@ #include "ap_state.h" #include "game_data.h" +#include "global.h" #include "logger.h" namespace { @@ -25,6 +26,7 @@ struct Requirements { std::set rooms; // maybe bool mastery = false; // maybe bool panel_hunt = false; // maybe + bool postgame = false; void Merge(const Requirements& rhs) { if (rhs.disabled) { @@ -45,6 +47,7 @@ struct Requirements { } mastery = mastery || rhs.mastery; panel_hunt = panel_hunt || rhs.panel_hunt; + postgame = postgame || rhs.postgame; } }; @@ -146,6 +149,10 @@ class RequirementCalculator { } } + if (panel_obj.location_name == GetWinCondition()) { + requirements.postgame = true; + } + panels_[panel_id] = requirements; } @@ -165,6 +172,11 @@ struct TrackerState { RequirementCalculator requirements; std::map> door_reports; bool pilgrimage_doable = false; + + // If these are empty, it actually means everything is non-postgame. + std::set non_postgame_areas; + std::set non_postgame_locations; + std::set non_postgame_paintings; }; enum Decision { kYes, kNo, kMaybe }; @@ -179,6 +191,11 @@ class StateCalculator; struct StateCalculatorOptions { int start; bool pilgrimage = false; + + // Treats all items as collected and all paintings as checked, but postgame + // areas cannot be reached. + bool postgame_detection = false; + StateCalculator* parent = nullptr; }; @@ -234,7 +251,8 @@ class StateCalculator { PaintingExit cur_painting = GD_GetPaintingExit(painting_id); if (painting_mapping_.count(cur_painting.internal_id) && - checked_paintings_.count(cur_painting.internal_id)) { + (checked_paintings_.count(cur_painting.internal_id) || + options_.postgame_detection)) { Exit painting_exit; PaintingExit target_painting = GD_GetPaintingExit(GD_GetPaintingByName( @@ -419,43 +437,49 @@ class StateCalculator { return kNo; } + if (reqs.postgame && options_.postgame_detection) { + return kNo; + } + Decision final_decision = kYes; - for (int door_id : reqs.doors) { - const Door& door_obj = GD_GetDoor(door_id); - Decision decision = IsNonGroupedDoorReachable(door_obj); + if (!options_.postgame_detection) { + for (int door_id : reqs.doors) { + const Door& door_obj = GD_GetDoor(door_id); + Decision decision = IsNonGroupedDoorReachable(door_obj); - if (report) { - (*report)[door_obj.item_name] = (decision == kYes); - } + if (report) { + (*report)[door_obj.item_name] = (decision == kYes); + } - if (decision != kYes) { - final_decision = decision; + if (decision != kYes) { + final_decision = decision; + } } - } - for (int panel_door_id : reqs.panel_doors) { - const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_door_id); - Decision decision = IsNonGroupedDoorReachable(panel_door_obj); + for (int panel_door_id : reqs.panel_doors) { + const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_door_id); + Decision decision = IsNonGroupedDoorReachable(panel_door_obj); - if (report) { - (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] = - (decision == kYes); - } + if (report) { + (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] = + (decision == kYes); + } - if (decision != kYes) { - final_decision = decision; + if (decision != kYes) { + final_decision = decision; + } } - } - for (int item_id : reqs.items) { - bool has_item = AP_HasItem(item_id); - if (report) { - (*report)[AP_GetItemName(item_id)] = has_item; - } + for (int item_id : reqs.items) { + bool has_item = AP_HasItem(item_id); + if (report) { + (*report)[AP_GetItemName(item_id)] = has_item; + } - if (!has_item) { - final_decision = kNo; + if (!has_item) { + final_decision = kNo; + } } } @@ -658,6 +682,62 @@ class StateCalculator { void ResetReachabilityRequirements() { std::lock_guard reachability_guard(GetState().reachability_mutex); GetState().requirements.Reset(); + + if (AP_IsPostgameShuffle()) { + GetState().non_postgame_areas.clear(); + GetState().non_postgame_locations.clear(); + GetState().non_postgame_paintings.clear(); + } else { + StateCalculator postgame_calculator( + {.start = GD_GetRoomByName("Menu"), .postgame_detection = true}); + postgame_calculator.Calculate(); + + std::set& non_postgame_areas = GetState().non_postgame_areas; + non_postgame_areas.clear(); + + std::set& non_postgame_locations = GetState().non_postgame_locations; + non_postgame_locations.clear(); + + const std::set& reachable_rooms = + postgame_calculator.GetReachableRooms(); + const std::set& solveable_panels = + postgame_calculator.GetSolveablePanels(); + + for (const MapArea& map_area : GD_GetMapAreas()) { + bool area_reachable = false; + + for (const Location& location_section : map_area.locations) { + bool reachable = reachable_rooms.count(location_section.room); + if (reachable) { + for (int panel_id : location_section.panels) { + reachable &= (solveable_panels.count(panel_id) == 1); + } + } + + if (!reachable && IsLocationWinCondition(location_section)) { + reachable = true; + } + + if (reachable) { + non_postgame_locations.insert(location_section.ap_location_id); + area_reachable = true; + } + } + + for (int painting_id : map_area.paintings) { + if (postgame_calculator.GetReachablePaintings().count(painting_id)) { + area_reachable = true; + } + } + + if (area_reachable) { + non_postgame_areas.insert(map_area.id); + } + } + + GetState().non_postgame_paintings = + postgame_calculator.GetReachablePaintings(); + } } void RecalculateReachability() { @@ -736,3 +816,33 @@ bool IsPilgrimageDoable() { return GetState().pilgrimage_doable; } + +bool IsAreaPostgame(int area_id) { + std::lock_guard reachability_guard(GetState().reachability_mutex); + + if (GetState().non_postgame_areas.empty()) { + return false; + } else { + return !GetState().non_postgame_areas.count(area_id); + } +} + +bool IsLocationPostgame(int location_id) { + std::lock_guard reachability_guard(GetState().reachability_mutex); + + if (GetState().non_postgame_locations.empty()) { + return false; + } else { + return !GetState().non_postgame_locations.count(location_id); + } +} + +bool IsPaintingPostgame(int painting_id) { + std::lock_guard reachability_guard(GetState().reachability_mutex); + + if (GetState().non_postgame_paintings.empty()) { + return false; + } else { + return !GetState().non_postgame_paintings.count(painting_id); + } +} diff --git a/src/tracker_state.h b/src/tracker_state.h index a8f155d..8f1002f 100644 --- a/src/tracker_state.h +++ b/src/tracker_state.h @@ -18,4 +18,10 @@ const std::map& GetDoorRequirements(int door_id); bool IsPilgrimageDoable(); +bool IsAreaPostgame(int area_id); + +bool IsLocationPostgame(int location_id); + +bool IsPaintingPostgame(int painting_id); + #endif /* end of include guard: TRACKER_STATE_H_8639BC90 */ -- cgit 1.4.1