diff options
Diffstat (limited to 'src/tracker_state.cpp')
-rw-r--r-- | src/tracker_state.cpp | 164 |
1 files changed, 137 insertions, 27 deletions
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 @@ | |||
12 | 12 | ||
13 | #include "ap_state.h" | 13 | #include "ap_state.h" |
14 | #include "game_data.h" | 14 | #include "game_data.h" |
15 | #include "global.h" | ||
15 | #include "logger.h" | 16 | #include "logger.h" |
16 | 17 | ||
17 | namespace { | 18 | namespace { |
@@ -25,6 +26,7 @@ struct Requirements { | |||
25 | std::set<int> rooms; // maybe | 26 | std::set<int> rooms; // maybe |
26 | bool mastery = false; // maybe | 27 | bool mastery = false; // maybe |
27 | bool panel_hunt = false; // maybe | 28 | bool panel_hunt = false; // maybe |
29 | bool postgame = false; | ||
28 | 30 | ||
29 | void Merge(const Requirements& rhs) { | 31 | void Merge(const Requirements& rhs) { |
30 | if (rhs.disabled) { | 32 | if (rhs.disabled) { |
@@ -45,6 +47,7 @@ struct Requirements { | |||
45 | } | 47 | } |
46 | mastery = mastery || rhs.mastery; | 48 | mastery = mastery || rhs.mastery; |
47 | panel_hunt = panel_hunt || rhs.panel_hunt; | 49 | panel_hunt = panel_hunt || rhs.panel_hunt; |
50 | postgame = postgame || rhs.postgame; | ||
48 | } | 51 | } |
49 | }; | 52 | }; |
50 | 53 | ||
@@ -146,6 +149,10 @@ class RequirementCalculator { | |||
146 | } | 149 | } |
147 | } | 150 | } |
148 | 151 | ||
152 | if (panel_obj.location_name == GetWinCondition()) { | ||
153 | requirements.postgame = true; | ||
154 | } | ||
155 | |||
149 | panels_[panel_id] = requirements; | 156 | panels_[panel_id] = requirements; |
150 | } | 157 | } |
151 | 158 | ||
@@ -165,6 +172,11 @@ struct TrackerState { | |||
165 | RequirementCalculator requirements; | 172 | RequirementCalculator requirements; |
166 | std::map<int, std::map<std::string, bool>> door_reports; | 173 | std::map<int, std::map<std::string, bool>> door_reports; |
167 | bool pilgrimage_doable = false; | 174 | bool pilgrimage_doable = false; |
175 | |||
176 | // If these are empty, it actually means everything is non-postgame. | ||
177 | std::set<int> non_postgame_areas; | ||
178 | std::set<int> non_postgame_locations; | ||
179 | std::set<int> non_postgame_paintings; | ||
168 | }; | 180 | }; |
169 | 181 | ||
170 | enum Decision { kYes, kNo, kMaybe }; | 182 | enum Decision { kYes, kNo, kMaybe }; |
@@ -179,6 +191,11 @@ class StateCalculator; | |||
179 | struct StateCalculatorOptions { | 191 | struct StateCalculatorOptions { |
180 | int start; | 192 | int start; |
181 | bool pilgrimage = false; | 193 | bool pilgrimage = false; |
194 | |||
195 | // Treats all items as collected and all paintings as checked, but postgame | ||
196 | // areas cannot be reached. | ||
197 | bool postgame_detection = false; | ||
198 | |||
182 | StateCalculator* parent = nullptr; | 199 | StateCalculator* parent = nullptr; |
183 | }; | 200 | }; |
184 | 201 | ||
@@ -234,7 +251,8 @@ class StateCalculator { | |||
234 | 251 | ||
235 | PaintingExit cur_painting = GD_GetPaintingExit(painting_id); | 252 | PaintingExit cur_painting = GD_GetPaintingExit(painting_id); |
236 | if (painting_mapping_.count(cur_painting.internal_id) && | 253 | if (painting_mapping_.count(cur_painting.internal_id) && |
237 | checked_paintings_.count(cur_painting.internal_id)) { | 254 | (checked_paintings_.count(cur_painting.internal_id) || |
255 | options_.postgame_detection)) { | ||
238 | Exit painting_exit; | 256 | Exit painting_exit; |
239 | PaintingExit target_painting = | 257 | PaintingExit target_painting = |
240 | GD_GetPaintingExit(GD_GetPaintingByName( | 258 | GD_GetPaintingExit(GD_GetPaintingByName( |
@@ -419,43 +437,49 @@ class StateCalculator { | |||
419 | return kNo; | 437 | return kNo; |
420 | } | 438 | } |
421 | 439 | ||
440 | if (reqs.postgame && options_.postgame_detection) { | ||
441 | return kNo; | ||
442 | } | ||
443 | |||
422 | Decision final_decision = kYes; | 444 | Decision final_decision = kYes; |
423 | 445 | ||
424 | for (int door_id : reqs.doors) { | 446 | if (!options_.postgame_detection) { |
425 | const Door& door_obj = GD_GetDoor(door_id); | 447 | for (int door_id : reqs.doors) { |
426 | Decision decision = IsNonGroupedDoorReachable(door_obj); | 448 | const Door& door_obj = GD_GetDoor(door_id); |
449 | Decision decision = IsNonGroupedDoorReachable(door_obj); | ||
427 | 450 | ||
428 | if (report) { | 451 | if (report) { |
429 | (*report)[door_obj.item_name] = (decision == kYes); | 452 | (*report)[door_obj.item_name] = (decision == kYes); |
430 | } | 453 | } |
431 | 454 | ||
432 | if (decision != kYes) { | 455 | if (decision != kYes) { |
433 | final_decision = decision; | 456 | final_decision = decision; |
457 | } | ||
434 | } | 458 | } |
435 | } | ||
436 | 459 | ||
437 | for (int panel_door_id : reqs.panel_doors) { | 460 | for (int panel_door_id : reqs.panel_doors) { |
438 | const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_door_id); | 461 | const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_door_id); |
439 | Decision decision = IsNonGroupedDoorReachable(panel_door_obj); | 462 | Decision decision = IsNonGroupedDoorReachable(panel_door_obj); |
440 | 463 | ||
441 | if (report) { | 464 | if (report) { |
442 | (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] = | 465 | (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] = |
443 | (decision == kYes); | 466 | (decision == kYes); |
444 | } | 467 | } |
445 | 468 | ||
446 | if (decision != kYes) { | 469 | if (decision != kYes) { |
447 | final_decision = decision; | 470 | final_decision = decision; |
471 | } | ||
448 | } | 472 | } |
449 | } | ||
450 | 473 | ||
451 | for (int item_id : reqs.items) { | 474 | for (int item_id : reqs.items) { |
452 | bool has_item = AP_HasItem(item_id); | 475 | bool has_item = AP_HasItem(item_id); |
453 | if (report) { | 476 | if (report) { |
454 | (*report)[AP_GetItemName(item_id)] = has_item; | 477 | (*report)[AP_GetItemName(item_id)] = has_item; |
455 | } | 478 | } |
456 | 479 | ||
457 | if (!has_item) { | 480 | if (!has_item) { |
458 | final_decision = kNo; | 481 | final_decision = kNo; |
482 | } | ||
459 | } | 483 | } |
460 | } | 484 | } |
461 | 485 | ||
@@ -658,6 +682,62 @@ class StateCalculator { | |||
658 | void ResetReachabilityRequirements() { | 682 | void ResetReachabilityRequirements() { |
659 | std::lock_guard reachability_guard(GetState().reachability_mutex); | 683 | std::lock_guard reachability_guard(GetState().reachability_mutex); |
660 | GetState().requirements.Reset(); | 684 | GetState().requirements.Reset(); |
685 | |||
686 | if (AP_IsPostgameShuffle()) { | ||
687 | GetState().non_postgame_areas.clear(); | ||
688 | GetState().non_postgame_locations.clear(); | ||
689 | GetState().non_postgame_paintings.clear(); | ||
690 | } else { | ||
691 | StateCalculator postgame_calculator( | ||
692 | {.start = GD_GetRoomByName("Menu"), .postgame_detection = true}); | ||
693 | postgame_calculator.Calculate(); | ||
694 | |||
695 | std::set<int>& non_postgame_areas = GetState().non_postgame_areas; | ||
696 | non_postgame_areas.clear(); | ||
697 | |||
698 | std::set<int>& non_postgame_locations = GetState().non_postgame_locations; | ||
699 | non_postgame_locations.clear(); | ||
700 | |||
701 | const std::set<int>& reachable_rooms = | ||
702 | postgame_calculator.GetReachableRooms(); | ||
703 | const std::set<int>& solveable_panels = | ||
704 | postgame_calculator.GetSolveablePanels(); | ||
705 | |||
706 | for (const MapArea& map_area : GD_GetMapAreas()) { | ||
707 | bool area_reachable = false; | ||
708 | |||
709 | for (const Location& location_section : map_area.locations) { | ||
710 | bool reachable = reachable_rooms.count(location_section.room); | ||
711 | if (reachable) { | ||
712 | for (int panel_id : location_section.panels) { | ||
713 | reachable &= (solveable_panels.count(panel_id) == 1); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | if (!reachable && IsLocationWinCondition(location_section)) { | ||
718 | reachable = true; | ||
719 | } | ||
720 | |||
721 | if (reachable) { | ||
722 | non_postgame_locations.insert(location_section.ap_location_id); | ||
723 | area_reachable = true; | ||
724 | } | ||
725 | } | ||
726 | |||
727 | for (int painting_id : map_area.paintings) { | ||
728 | if (postgame_calculator.GetReachablePaintings().count(painting_id)) { | ||
729 | area_reachable = true; | ||
730 | } | ||
731 | } | ||
732 | |||
733 | if (area_reachable) { | ||
734 | non_postgame_areas.insert(map_area.id); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | GetState().non_postgame_paintings = | ||
739 | postgame_calculator.GetReachablePaintings(); | ||
740 | } | ||
661 | } | 741 | } |
662 | 742 | ||
663 | void RecalculateReachability() { | 743 | void RecalculateReachability() { |
@@ -736,3 +816,33 @@ bool IsPilgrimageDoable() { | |||
736 | 816 | ||
737 | return GetState().pilgrimage_doable; | 817 | return GetState().pilgrimage_doable; |
738 | } | 818 | } |
819 | |||
820 | bool IsAreaPostgame(int area_id) { | ||
821 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
822 | |||
823 | if (GetState().non_postgame_areas.empty()) { | ||
824 | return false; | ||
825 | } else { | ||
826 | return !GetState().non_postgame_areas.count(area_id); | ||
827 | } | ||
828 | } | ||
829 | |||
830 | bool IsLocationPostgame(int location_id) { | ||
831 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
832 | |||
833 | if (GetState().non_postgame_locations.empty()) { | ||
834 | return false; | ||
835 | } else { | ||
836 | return !GetState().non_postgame_locations.count(location_id); | ||
837 | } | ||
838 | } | ||
839 | |||
840 | bool IsPaintingPostgame(int painting_id) { | ||
841 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
842 | |||
843 | if (GetState().non_postgame_paintings.empty()) { | ||
844 | return false; | ||
845 | } else { | ||
846 | return !GetState().non_postgame_paintings.count(painting_id); | ||
847 | } | ||
848 | } | ||