about summary refs log tree commit diff stats
Commit message (Expand)AuthorAgeFilesLines
* [Client] Keyholders in locationsStar Rauchenberger2025-08-311-0/+19
* [Data] Couple of renamesStar Rauchenberger2025-08-314-2/+4
* [Apworld] Added options to slot dataStar Rauchenberger2025-08-311-0/+11
* [Apworld] Better handling of White EndingStar Rauchenberger2025-08-311-2/+14
* Handled cyan doorsStar Rauchenberger2025-08-3113-17/+37
* [Data] Small tweaksStar Rauchenberger2025-08-318-22/+34
* [Client] Handle locations needing specific answersStar Rauchenberger2025-08-301-2/+9
* [Client] Potentially fixed crash when loading corrupted localdataStar Rauchenberger2025-08-302-6/+4
* [Data] Small tweaksStar Rauchenberger2025-08-307-2/+16
* [Data] Bad logic in the_owl due to missing warpStar Rauchenberger2025-08-305-22/+23
* Changed how door location names are formattedStar Rauchenberger2025-08-30491-692/+798
* [Apworld] Require CC access + letters for CC color doorsStar Rauchenberger2025-08-301-0/+4
* [Data] Wondrous gallery painting goes to huge roomStar Rauchenberger2025-08-301-1/+1
* [Data] Made proxies with the same answer as the panel explicitStar Rauchenberger2025-08-3022-37/+42
* [Data] Added logic for Tenacious Color PaintingStar Rauchenberger2025-08-302-1/+9
* [Data] S1 Door is not a location anymoreStar Rauchenberger2025-08-291-3/+1
* [Client] Added textclientStar Rauchenberger2025-08-294-0/+145
* [Client] Save connection settings to diskStar Rauchenberger2025-08-292-2/+37
* [Client] Last received item is rememberedStar Rauchenberger2025-08-291-1/+33
* [Client] Added ending locationsStar Rauchenberger2025-08-293-0/+22
* [Client] Added mastery checksStar Rauchenberger2025-08-291-0/+12
* [Client] Various fixesStar Rauchenberger2025-08-296-13/+101
* [Data] Fix castle stairs (again)Star Rauchenberger2025-08-291-2/+2
* [Data] Replace move_paintings with receiversStar Rauchenberger2025-08-287-22/+51
* [Client] Added messages overlayStar Rauchenberger2025-08-284-6/+166
* Client is starting to work!Star Rauchenberger2025-08-2813-0/+1270
* Couple of logic errorsStar Rauchenberger2025-08-283-3/+3
* Renamed Painting and Keyholder protosStar Rauchenberger2025-08-273-8/+8
* Added requirements to Green Ending doorStar Rauchenberger2025-08-271-2/+27
* Letter requirements in apworldStar Rauchenberger2025-08-273-7/+66
* Set apworld victory conditionStar Rauchenberger2025-08-274-28/+76
* Prevent assigning ap_id==0Star Rauchenberger2025-08-272-2/+2
* Added control_centerStar Rauchenberger2025-08-2728-15/+860
* Added the_wordsStar Rauchenberger2025-08-268-3/+157
* Added the_wondrousStar Rauchenberger2025-08-269-3/+191
* Added the_wiseStar Rauchenberger2025-08-269-5/+351
* Added the_unkemptStar Rauchenberger2025-08-2617-4/+1023
* Added the_treeStar Rauchenberger2025-08-269-8/+441
* Added the_towerStar Rauchenberger2025-08-269-3/+837
* Added the_three_doorsStar Rauchenberger2025-08-2611-2/+398
* Added the_tenaciousStar Rauchenberger2025-08-2514-7/+201
* Added the_talentedStar Rauchenberger2025-08-258-1/+284
* Added the_symbolicStar Rauchenberger2025-08-2528-1/+1682
* Ok another Lime Pyramid connectionStar Rauchenberger2025-08-242-0/+25
* Connected up Lime PyramidStar Rauchenberger2025-08-242-0/+81
* Fixed typo in Blue Smiley Annex connectionStar Rauchenberger2025-08-241-1/+1
* Added daedalusStar Rauchenberger2025-08-24162-32/+10732
* Fixed a couple of missing connectionsStar Rauchenberger2025-08-213-0/+18
* Added the_unyieldingStar Rauchenberger2025-08-2146-12/+2370
* Converted puzzle symbols to an enumStar Rauchenberger2025-08-20208-973/+997
ap-tracker/blame/tracker_state.cpp?h=v0.10.2&id=08ffb400114029569b4043b4f4c5a3f2af9b37b8'>^
08ffb40 ^






70f1c62 ^


d7212d7 ^



70f1c62 ^



d7212d7 ^
70f1c62 ^














325ea53 ^
70f1c62 ^









d7212d7 ^
70f1c62 ^
325ea53 ^
dc4a143 ^



325ea53 ^
dc4a143 ^
325ea53 ^
dc4a143 ^
d7212d7 ^
325ea53 ^
dc4a143 ^




70f1c62 ^






d7212d7 ^
70f1c62 ^









d7212d7 ^
70f1c62 ^



d7212d7 ^
70f1c62 ^

d7212d7 ^

70f1c62 ^



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189


                          
              
              
                



                      










                                                     



                                                                               
                                                 




                                               


                                       
                                                      











                                                                    











                                                            
                            
                                               
                                    








                                                                                
                                             
 
                                                                   


                                                
 


                                                                
       
     

                
                                                        
                                            
                                           
          
                                                   


                                                                            
                                                                






                          


   



                                  



                                 
                                                                           














                                                                       
                                                          









                                                           
                                                                      
                                                     
                                                              



                                             
                                     
                                                                   
                                                             
                                 
                                                                     
                                                           




                                                    






                                  
                                                    









                                                                           
                                                                     



     
                                                       

                                                   

                                           



                 
#include "tracker_state.h"

#include <list>
#include <map>
#include <set>
#include <tuple>

#include "ap_state.h"
#include "game_data.h"

namespace {

struct TrackerState {
  std::map<std::tuple<int, int>, bool> reachability;
};

TrackerState& GetState() {
  static TrackerState* instance = new TrackerState();
  return *instance;
}

bool IsDoorReachable_Helper(int door_id, const std::set<int>& reachable_rooms);

bool IsPanelReachable_Helper(int panel_id,
                             const std::set<int>& reachable_rooms) {
  const Panel& panel_obj = GD_GetPanel(panel_id);

  if (!reachable_rooms.count(panel_obj.room)) {
    return false;
  }

  if (panel_obj.name == "THE MASTER") {
    int achievements_accessible = 0;

    for (int achieve_id : GD_GetAchievementPanels()) {
      if (IsPanelReachable_Helper(achieve_id, reachable_rooms)) {
        achievements_accessible++;

        if (achievements_accessible >= AP_GetMasteryRequirement()) {
          break;
        }
      }
    }

    return (achievements_accessible >= AP_GetMasteryRequirement());
  }

  for (int room_id : panel_obj.required_rooms) {
    if (!reachable_rooms.count(room_id)) {
      return false;
    }
  }

  for (int door_id : panel_obj.required_doors) {
    if (!IsDoorReachable_Helper(door_id, reachable_rooms)) {
      return false;
    }
  }

  if (AP_IsColorShuffle()) {
    for (LingoColor color : panel_obj.colors) {
      if (!AP_HasColorItem(color)) {
        return false;
      }
    }
  }

  return true;
}

bool IsDoorReachable_Helper(int door_id, const std::set<int>& 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;
  }
}

}  // namespace

void RecalculateReachability() {
  GetState().reachability.clear();

  std::set<int> reachable_rooms;

  std::list<Exit> flood_boundary;
  flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")});

  bool reachable_changed = true;
  while (reachable_changed) {
    reachable_changed = false;

    std::list<Exit> new_boundary;
    for (const Exit& room_exit : flood_boundary) {
      if (reachable_rooms.count(room_exit.destination_room)) {
        continue;
      }

      bool valid_transition = false;
      if (room_exit.door.has_value()) {
        if (IsDoorReachable_Helper(*room_exit.door, reachable_rooms)) {
          valid_transition = true;
        } else if (AP_GetDoorShuffleMode() == kNO_DOORS) {
          new_boundary.push_back(room_exit);
        }
      } else {
        valid_transition = true;
      }

      if (valid_transition) {
        reachable_rooms.insert(room_exit.destination_room);
        reachable_changed = true;

        const Room& room_obj = GD_GetRoom(room_exit.destination_room);
        for (const Exit& out_edge : room_obj.exits) {
          if (!out_edge.painting || !AP_IsPaintingShuffle()) {
            new_boundary.push_back(out_edge);
          }
        }

        if (AP_IsPaintingShuffle()) {
          for (const PaintingExit& out_edge : room_obj.paintings) {
            if (AP_GetPaintingMapping().count(out_edge.id)) {
              Exit painting_exit;
              painting_exit.destination_room = GD_GetRoomForPainting(
                  AP_GetPaintingMapping().at(out_edge.id));
              painting_exit.door = out_edge.door;

              new_boundary.push_back(painting_exit);
            }
          }
        }
      }
    }

    flood_boundary = new_boundary;
  }

  for (const MapArea& map_area : GD_GetMapAreas()) {
    for (int 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);
        }
      }

      GetState().reachability[{map_area.id, section_id}] = reachable;
    }
  }
}

bool IsLocationReachable(int area_id, int section_id) {
  std::tuple<int, int> key = {area_id, section_id};

  if (GetState().reachability.count(key)) {
    return GetState().reachability.at(key);
  } else {
    return false;
  }
}