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/tracker_state.cpp | 164 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 137 insertions(+), 27 deletions(-) (limited to 'src/tracker_state.cpp') 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); + } +} -- cgit 1.4.1