about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2023-08-25 19:44:02 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2023-08-25 19:44:02 -0400
commitbaf43ede759f9ff0ca8c71de764e0389469f9ae1 (patch)
treea8526a339cff418da8674af8c8bdbc44c1ab3183 /src
parent58ea40015ddfdbce984f109fe32f291afd0408bd (diff)
downloadlingo-ap-tracker-baf43ede759f9ff0ca8c71de764e0389469f9ae1.tar.gz
lingo-ap-tracker-baf43ede759f9ff0ca8c71de764e0389469f9ae1.tar.bz2
lingo-ap-tracker-baf43ede759f9ff0ca8c71de764e0389469f9ae1.zip
Rewrote how panel solvability is determined
This fixes an edge case with LEVEL 2, and likely fixes an undiscovered issue with no-doors Pilgrimage.
Diffstat (limited to 'src')
-rw-r--r--src/game_data.cpp5
-rw-r--r--src/game_data.h1
-rw-r--r--src/tracker_state.cpp160
3 files changed, 102 insertions, 64 deletions
diff --git a/src/game_data.cpp b/src/game_data.cpp index fafc88c..4393373 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp
@@ -255,6 +255,7 @@ struct GameData {
255 if (door_it.second["event"]) { 255 if (door_it.second["event"]) {
256 door_obj.skip_location = door_it.second["event"].as<bool>(); 256 door_obj.skip_location = door_it.second["event"].as<bool>();
257 door_obj.skip_item = door_it.second["event"].as<bool>(); 257 door_obj.skip_item = door_it.second["event"].as<bool>();
258 door_obj.is_event = door_it.second["event"].as<bool>();
258 } 259 }
259 260
260 if (door_it.second["item_name"]) { 261 if (door_it.second["item_name"]) {
@@ -427,6 +428,7 @@ struct GameData {
427 int fake_pilgrim_panel_id = 428 int fake_pilgrim_panel_id =
428 AddOrGetPanel("Starting Room", "!! Fake Pilgrimage Panel"); 429 AddOrGetPanel("Starting Room", "!! Fake Pilgrimage Panel");
429 Panel &fake_pilgrim_panel_obj = panels_[fake_pilgrim_panel_id]; 430 Panel &fake_pilgrim_panel_obj = panels_[fake_pilgrim_panel_id];
431 fake_pilgrim_panel_obj.non_counting = true;
430 432
431 for (const auto &config_node : pilgrimage_config) { 433 for (const auto &config_node : pilgrimage_config) {
432 fake_pilgrim_panel_obj.required_doors.push_back( 434 fake_pilgrim_panel_obj.required_doors.push_back(
@@ -438,10 +440,13 @@ struct GameData {
438 AddOrGetDoor("Starting Room", "!! Fake Pilgrimage Door"); 440 AddOrGetDoor("Starting Room", "!! Fake Pilgrimage Door");
439 Door &fake_pilgrim_door_obj = doors_[fake_pilgrim_door_id]; 441 Door &fake_pilgrim_door_obj = doors_[fake_pilgrim_door_id];
440 fake_pilgrim_door_obj.panels.push_back(fake_pilgrim_panel_id); 442 fake_pilgrim_door_obj.panels.push_back(fake_pilgrim_panel_id);
443 fake_pilgrim_door_obj.skip_location = true;
441 fake_pilgrim_door_obj.skip_item = true; 444 fake_pilgrim_door_obj.skip_item = true;
445 fake_pilgrim_door_obj.is_event = true;
442 446
443 int starting_room_id = AddOrGetRoom("Starting Room"); 447 int starting_room_id = AddOrGetRoom("Starting Room");
444 Room &starting_room_obj = rooms_[starting_room_id]; 448 Room &starting_room_obj = rooms_[starting_room_id];
449 starting_room_obj.panels.push_back(fake_pilgrim_panel_id);
445 starting_room_obj.exits.push_back( 450 starting_room_obj.exits.push_back(
446 Exit{.destination_room = AddOrGetRoom("Pilgrim Antechamber"), 451 Exit{.destination_room = AddOrGetRoom("Pilgrim Antechamber"),
447 .door = fake_pilgrim_door_id}); 452 .door = fake_pilgrim_door_id});
diff --git a/src/game_data.h b/src/game_data.h index 31a0c87..959d5c8 100644 --- a/src/game_data.h +++ b/src/game_data.h
@@ -47,6 +47,7 @@ struct Door {
47 std::string group_name; 47 std::string group_name;
48 bool skip_location = false; 48 bool skip_location = false;
49 bool skip_item = false; 49 bool skip_item = false;
50 bool is_event = false;
50 std::vector<int> panels; 51 std::vector<int> panels;
51 bool exclude_reduce = true; 52 bool exclude_reduce = true;
52 std::vector<ProgressiveRequirement> progressives; 53 std::vector<ProgressiveRequirement> progressives;
diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 557e551..e462b29 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp
@@ -3,12 +3,11 @@
3#include <list> 3#include <list>
4#include <map> 4#include <map>
5#include <set> 5#include <set>
6#include <tuple>
7#include <sstream> 6#include <sstream>
7#include <tuple>
8 8
9#include "ap_state.h" 9#include "ap_state.h"
10#include "game_data.h" 10#include "game_data.h"
11#include "logger.h"
12 11
13namespace { 12namespace {
14 13
@@ -16,26 +15,63 @@ struct TrackerState {
16 std::map<std::tuple<int, int>, bool> reachability; 15 std::map<std::tuple<int, int>, bool> reachability;
17}; 16};
18 17
18enum Decision { kYes, kNo, kMaybe };
19
19TrackerState& GetState() { 20TrackerState& GetState() {
20 static TrackerState* instance = new TrackerState(); 21 static TrackerState* instance = new TrackerState();
21 return *instance; 22 return *instance;
22} 23}
23 24
24bool IsDoorReachable_Helper(int door_id, const std::set<int>& reachable_rooms); 25Decision IsDoorReachable_Helper(int door_id,
26 const std::set<int>& reachable_rooms,
27 const std::set<int>& solveable_panels) {
28 const Door& door_obj = GD_GetDoor(door_id);
29
30 if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) {
31 if (!reachable_rooms.count(door_obj.room)) {
32 return kMaybe;
33 }
34
35 for (int panel_id : door_obj.panels) {
36 if (!solveable_panels.count(panel_id)) {
37 return kMaybe;
38 }
39 }
40
41 return kYes;
42 } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS &&
43 !door_obj.group_name.empty()) {
44 return AP_HasItem(door_obj.group_name) ? kYes : kNo;
45 } else {
46 bool has_item = AP_HasItem(door_obj.item_name);
47
48 if (!has_item) {
49 for (const ProgressiveRequirement& prog_req : door_obj.progressives) {
50 if (AP_HasItem(prog_req.item_name, prog_req.quantity)) {
51 has_item = true;
52 break;
53 }
54 }
55 }
25 56
26bool IsPanelReachable_Helper(int panel_id, 57 return has_item ? kYes : kNo;
27 const std::set<int>& reachable_rooms) { 58 }
59}
60
61Decision IsPanelReachable_Helper(int panel_id,
62 const std::set<int>& reachable_rooms,
63 const std::set<int>& solveable_panels) {
28 const Panel& panel_obj = GD_GetPanel(panel_id); 64 const Panel& panel_obj = GD_GetPanel(panel_id);
29 65
30 if (!reachable_rooms.count(panel_obj.room)) { 66 if (!reachable_rooms.count(panel_obj.room)) {
31 return false; 67 return kMaybe;
32 } 68 }
33 69
34 if (panel_obj.name == "THE MASTER") { 70 if (panel_obj.name == "THE MASTER") {
35 int achievements_accessible = 0; 71 int achievements_accessible = 0;
36 72
37 for (int achieve_id : GD_GetAchievementPanels()) { 73 for (int achieve_id : GD_GetAchievementPanels()) {
38 if (IsPanelReachable_Helper(achieve_id, reachable_rooms)) { 74 if (solveable_panels.count(achieve_id)) {
39 achievements_accessible++; 75 achievements_accessible++;
40 76
41 if (achievements_accessible >= AP_GetMasteryRequirement()) { 77 if (achievements_accessible >= AP_GetMasteryRequirement()) {
@@ -44,89 +80,60 @@ bool IsPanelReachable_Helper(int panel_id,
44 } 80 }
45 } 81 }
46 82
47 return (achievements_accessible >= AP_GetMasteryRequirement()); 83 return (achievements_accessible >= AP_GetMasteryRequirement()) ? kYes
84 : kMaybe;
48 } 85 }
49 86
50 if (panel_obj.name == "LEVEL 2" && AP_GetVictoryCondition() == kLEVEL_2) { 87 if (panel_obj.name == "ANOTHER TRY" && AP_GetVictoryCondition() == kLEVEL_2) {
51 int counting_panels_accessible = 0; 88 int counting_panels_accessible = 0;
52 89
53 for (int reachable_room : reachable_rooms) { 90 for (int solved_panel_id : solveable_panels) {
54 const Room& room = GD_GetRoom(reachable_room); 91 const Panel& solved_panel = GD_GetPanel(solved_panel_id);
55 92
56 for (int roomed_panel_id : room.panels) { 93 if (!solved_panel.non_counting) {
57 const Panel& roomed_panel = GD_GetPanel(roomed_panel_id); 94 counting_panels_accessible++;
58
59 if (!roomed_panel.non_counting &&
60 IsPanelReachable_Helper(roomed_panel_id, reachable_rooms)) {
61 counting_panels_accessible++;
62 }
63 } 95 }
64 } 96 }
65 97
66 return (counting_panels_accessible >= AP_GetLevel2Requirement()); 98 return (counting_panels_accessible >= AP_GetLevel2Requirement() - 1)
99 ? kYes
100 : kMaybe;
67 } 101 }
68 102
69 for (int room_id : panel_obj.required_rooms) { 103 for (int room_id : panel_obj.required_rooms) {
70 if (!reachable_rooms.count(room_id)) { 104 if (!reachable_rooms.count(room_id)) {
71 return false; 105 return kMaybe;
72 } 106 }
73 } 107 }
74 108
75 for (int door_id : panel_obj.required_doors) { 109 for (int door_id : panel_obj.required_doors) {
76 if (!IsDoorReachable_Helper(door_id, reachable_rooms)) { 110 Decision door_reachable =
77 return false; 111 IsDoorReachable_Helper(door_id, reachable_rooms, solveable_panels);
112 if (door_reachable == kNo) {
113 const Door& door_obj = GD_GetDoor(door_id);
114 return (door_obj.is_event || AP_GetDoorShuffleMode() == kNO_DOORS)
115 ? kMaybe
116 : kNo;
117 } else if (door_reachable == kMaybe) {
118 return kMaybe;
78 } 119 }
79 } 120 }
80 121
81 for (int panel_id : panel_obj.required_panels) { 122 for (int panel_id : panel_obj.required_panels) {
82 if (!IsPanelReachable_Helper(panel_id, reachable_rooms)) { 123 if (!solveable_panels.count(panel_id)) {
83 return false; 124 return kMaybe;
84 } 125 }
85 } 126 }
86 127
87 if (AP_IsColorShuffle()) { 128 if (AP_IsColorShuffle()) {
88 for (LingoColor color : panel_obj.colors) { 129 for (LingoColor color : panel_obj.colors) {
89 if (!AP_HasColorItem(color)) { 130 if (!AP_HasColorItem(color)) {
90 return false; 131 return kNo;
91 } 132 }
92 } 133 }
93 } 134 }
94 135
95 return true; 136 return kYes;
96}
97
98bool IsDoorReachable_Helper(int door_id, const std::set<int>& reachable_rooms) {
99 const Door& door_obj = GD_GetDoor(door_id);
100
101 if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) {
102 if (!reachable_rooms.count(door_obj.room)) {
103 return false;
104 }
105
106 for (int panel_id : door_obj.panels) {
107 if (!IsPanelReachable_Helper(panel_id, reachable_rooms)) {
108 return false;
109 }
110 }
111
112 return true;
113 } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS &&
114 !door_obj.group_name.empty()) {
115 return AP_HasItem(door_obj.group_name);
116 } else {
117 bool has_item = AP_HasItem(door_obj.item_name);
118
119 if (!has_item) {
120 for (const ProgressiveRequirement& prog_req : door_obj.progressives) {
121 if (AP_HasItem(prog_req.item_name, prog_req.quantity)) {
122 has_item = true;
123 break;
124 }
125 }
126 }
127
128 return has_item;
129 }
130} 137}
131 138
132} // namespace 139} // namespace
@@ -135,7 +142,9 @@ void RecalculateReachability() {
135 GetState().reachability.clear(); 142 GetState().reachability.clear();
136 143
137 std::set<int> reachable_rooms; 144 std::set<int> reachable_rooms;
145 std::set<int> solveable_panels;
138 146
147 std::list<int> panel_boundary;
139 std::list<Exit> flood_boundary; 148 std::list<Exit> flood_boundary;
140 flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")}); 149 flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")});
141 150
@@ -143,6 +152,22 @@ void RecalculateReachability() {
143 while (reachable_changed) { 152 while (reachable_changed) {
144 reachable_changed = false; 153 reachable_changed = false;
145 154
155 std::list<int> new_panel_boundary;
156 for (int panel_id : panel_boundary) {
157 if (solveable_panels.count(panel_id)) {
158 continue;
159 }
160
161 Decision panel_reachable =
162 IsPanelReachable_Helper(panel_id, reachable_rooms, solveable_panels);
163 if (panel_reachable == kYes) {
164 solveable_panels.insert(panel_id);
165 reachable_changed = true;
166 } else if (panel_reachable == kMaybe) {
167 new_panel_boundary.push_back(panel_id);
168 }
169 }
170
146 std::list<Exit> new_boundary; 171 std::list<Exit> new_boundary;
147 for (const Exit& room_exit : flood_boundary) { 172 for (const Exit& room_exit : flood_boundary) {
148 if (reachable_rooms.count(room_exit.destination_room)) { 173 if (reachable_rooms.count(room_exit.destination_room)) {
@@ -151,9 +176,11 @@ void RecalculateReachability() {
151 176
152 bool valid_transition = false; 177 bool valid_transition = false;
153 if (room_exit.door.has_value()) { 178 if (room_exit.door.has_value()) {
154 if (IsDoorReachable_Helper(*room_exit.door, reachable_rooms)) { 179 Decision door_reachable = IsDoorReachable_Helper(
180 *room_exit.door, reachable_rooms, solveable_panels);
181 if (door_reachable == kYes) {
155 valid_transition = true; 182 valid_transition = true;
156 } else { 183 } else if (door_reachable == kMaybe) {
157 new_boundary.push_back(room_exit); 184 new_boundary.push_back(room_exit);
158 } 185 }
159 } else { 186 } else {
@@ -183,20 +210,25 @@ void RecalculateReachability() {
183 } 210 }
184 } 211 }
185 } 212 }
213
214 for (int panel_id : room_obj.panels) {
215 new_panel_boundary.push_back(panel_id);
216 }
186 } 217 }
187 } 218 }
188 219
189 flood_boundary = new_boundary; 220 flood_boundary = new_boundary;
221 panel_boundary = new_panel_boundary;
190 } 222 }
191 223
192 for (const MapArea& map_area : GD_GetMapAreas()) { 224 for (const MapArea& map_area : GD_GetMapAreas()) {
193 for (int section_id = 0; section_id < map_area.locations.size(); 225 for (size_t section_id = 0; section_id < map_area.locations.size();
194 section_id++) { 226 section_id++) {
195 const Location& location_section = map_area.locations.at(section_id); 227 const Location& location_section = map_area.locations.at(section_id);
196 bool reachable = reachable_rooms.count(location_section.room); 228 bool reachable = reachable_rooms.count(location_section.room);
197 if (reachable) { 229 if (reachable) {
198 for (int panel_id : location_section.panels) { 230 for (int panel_id : location_section.panels) {
199 reachable &= IsPanelReachable_Helper(panel_id, reachable_rooms); 231 reachable &= (solveable_panels.count(panel_id) == 1);
200 } 232 }
201 } 233 }
202 234