about summary refs log tree commit diff stats
path: root/src/tracker_state.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tracker_state.cpp')
-rw-r--r--src/tracker_state.cpp164
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
17namespace { 18namespace {
@@ -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
170enum Decision { kYes, kNo, kMaybe }; 182enum Decision { kYes, kNo, kMaybe };
@@ -179,6 +191,11 @@ class StateCalculator;
179struct StateCalculatorOptions { 191struct 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 {
658void ResetReachabilityRequirements() { 682void 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
663void RecalculateReachability() { 743void RecalculateReachability() {
@@ -736,3 +816,33 @@ bool IsPilgrimageDoable() {
736 816
737 return GetState().pilgrimage_doable; 817 return GetState().pilgrimage_doable;
738} 818}
819
820bool 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
830bool 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
840bool 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}