about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2024-06-06 13:53:20 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2024-06-06 13:53:20 -0400
commit67a2efe7be6f4872adca8d944ebf403046472a98 (patch)
tree63f30a28c2c32ab03f9dfccc1c50567a0f03d3cc /src
parent78ac9905e222c26758e95b098d2e3a3e74a48839 (diff)
downloadlingo-ap-tracker-67a2efe7be6f4872adca8d944ebf403046472a98.tar.gz
lingo-ap-tracker-67a2efe7be6f4872adca8d944ebf403046472a98.tar.bz2
lingo-ap-tracker-67a2efe7be6f4872adca8d944ebf403046472a98.zip
Proper painting reachability detection
Diffstat (limited to 'src')
-rw-r--r--src/area_popup.cpp16
-rw-r--r--src/game_data.cpp34
-rw-r--r--src/game_data.h11
-rw-r--r--src/tracker_panel.cpp7
-rw-r--r--src/tracker_state.cpp67
-rw-r--r--src/tracker_state.h2
6 files changed, 103 insertions, 34 deletions
diff --git a/src/area_popup.cpp b/src/area_popup.cpp index b5c1ccb..58d8897 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp
@@ -66,8 +66,9 @@ void AreaPopup::UpdateIndicators() {
66 } 66 }
67 67
68 if (AP_IsPaintingShuffle()) { 68 if (AP_IsPaintingShuffle()) {
69 for (const PaintingExit& painting : map_area.paintings) { 69 for (int painting_id : map_area.paintings) {
70 wxSize item_extent = mem_dc.GetTextExtent(painting.id); 70 const PaintingExit& painting = GD_GetPaintingExit(painting_id);
71 wxSize item_extent = mem_dc.GetTextExtent(painting.internal_id); // TODO: Replace with a friendly name.
71 int item_height = std::max(32, item_extent.GetHeight()) + 10; 72 int item_height = std::max(32, item_extent.GetHeight()) + 10;
72 acc_height += item_height; 73 acc_height += item_height;
73 74
@@ -123,18 +124,19 @@ void AreaPopup::UpdateIndicators() {
123 } 124 }
124 125
125 if (AP_IsPaintingShuffle()) { 126 if (AP_IsPaintingShuffle()) {
126 for (const PaintingExit& painting : map_area.paintings) { 127 for (int painting_id : map_area.paintings) {
127 bool checked = AP_IsPaintingChecked(painting.id); 128 const PaintingExit& painting = GD_GetPaintingExit(painting_id);
129 bool checked = AP_IsPaintingChecked(painting.internal_id);
128 wxBitmap* eye_ptr = checked ? &checked_eye_ : &unchecked_eye_; 130 wxBitmap* eye_ptr = checked ? &checked_eye_ : &unchecked_eye_;
129 131
130 mem_dc.DrawBitmap(*eye_ptr, {10, cur_height}); 132 mem_dc.DrawBitmap(*eye_ptr, {10, cur_height});
131 133
132 bool reachable = painting.door ? IsDoorOpen(*painting.door) : true; 134 bool reachable = IsPaintingReachable(painting_id);
133 const wxColour* text_color = reachable ? wxWHITE : wxRED; 135 const wxColour* text_color = reachable ? wxWHITE : wxRED;
134 mem_dc.SetTextForeground(*text_color); 136 mem_dc.SetTextForeground(*text_color);
135 137
136 wxSize item_extent = mem_dc.GetTextExtent(painting.id); 138 wxSize item_extent = mem_dc.GetTextExtent(painting.internal_id); // TODO: Replace with friendly name.
137 mem_dc.DrawText(painting.id, 139 mem_dc.DrawText(painting.internal_id,
138 {10 + 32 + 10, 140 {10 + 32 + 10,
139 cur_height + (32 - mem_dc.GetFontMetrics().height) / 2}); 141 cur_height + (32 - mem_dc.GetFontMetrics().height) / 2});
140 142
diff --git a/src/game_data.cpp b/src/game_data.cpp index 4dd69e2..71b8629 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp
@@ -48,11 +48,13 @@ struct GameData {
48 std::vector<Panel> panels_; 48 std::vector<Panel> panels_;
49 std::vector<MapArea> map_areas_; 49 std::vector<MapArea> map_areas_;
50 std::vector<SubwayItem> subway_items_; 50 std::vector<SubwayItem> subway_items_;
51 std::vector<PaintingExit> paintings_;
51 52
52 std::map<std::string, int> room_by_id_; 53 std::map<std::string, int> room_by_id_;
53 std::map<std::string, int> door_by_id_; 54 std::map<std::string, int> door_by_id_;
54 std::map<std::string, int> panel_by_id_; 55 std::map<std::string, int> panel_by_id_;
55 std::map<std::string, int> area_by_id_; 56 std::map<std::string, int> area_by_id_;
57 std::map<std::string, int> painting_by_id_;
56 58
57 std::vector<int> door_definition_order_; 59 std::vector<int> door_definition_order_;
58 60
@@ -419,13 +421,13 @@ struct GameData {
419 421
420 if (room_it.second["paintings"]) { 422 if (room_it.second["paintings"]) {
421 for (const auto &painting : room_it.second["paintings"]) { 423 for (const auto &painting : room_it.second["paintings"]) {
422 std::string painting_id = painting["id"].as<std::string>(); 424 std::string internal_id = painting["id"].as<std::string>();
423 room_by_painting_[painting_id] = room_id;
424 425
425 if ((!painting["exit_only"] || !painting["exit_only"].as<bool>()) && 426 if ((!painting["exit_only"] || !painting["exit_only"].as<bool>()) &&
426 (!painting["disable"] || !painting["disable"].as<bool>())) { 427 (!painting["disable"] || !painting["disable"].as<bool>())) {
427 PaintingExit painting_exit; 428 int painting_id = AddOrGetPainting(internal_id);
428 painting_exit.id = painting_id; 429 PaintingExit &painting_exit = paintings_[painting_id];
430 painting_exit.room = room_id;
429 431
430 if (painting["required_door"]) { 432 if (painting["required_door"]) {
431 std::string rd_room = rooms_[room_id].name; 433 std::string rd_room = rooms_[room_id].name;
@@ -437,7 +439,7 @@ struct GameData {
437 rd_room, painting["required_door"]["door"].as<std::string>()); 439 rd_room, painting["required_door"]["door"].as<std::string>());
438 } 440 }
439 441
440 rooms_[room_id].paintings.push_back(painting_exit); 442 rooms_[room_id].paintings.push_back(painting_exit.id);
441 } 443 }
442 } 444 }
443 } 445 }
@@ -606,8 +608,8 @@ struct GameData {
606 int area_id = AddOrGetArea(area_name); 608 int area_id = AddOrGetArea(area_name);
607 MapArea &map_area = map_areas_[area_id]; 609 MapArea &map_area = map_areas_[area_id];
608 610
609 for (const PaintingExit &painting : room.paintings) { 611 for (int painting_id : room.paintings) {
610 map_area.paintings.push_back(painting); 612 map_area.paintings.push_back(painting_id);
611 } 613 }
612 } 614 }
613 } 615 }
@@ -738,6 +740,16 @@ struct GameData {
738 740
739 return area_by_id_[area]; 741 return area_by_id_[area];
740 } 742 }
743
744 int AddOrGetPainting(std::string internal_id) {
745 if (!painting_by_id_.count(internal_id)) {
746 int painting_id = paintings_.size();
747 painting_by_id_[internal_id] = painting_id;
748 paintings_.push_back({.id = painting_id, .internal_id = internal_id});
749 }
750
751 return painting_by_id_[internal_id];
752 }
741}; 753};
742 754
743GameData &GetState() { 755GameData &GetState() {
@@ -773,8 +785,12 @@ const Panel &GD_GetPanel(int panel_id) {
773 return GetState().panels_.at(panel_id); 785 return GetState().panels_.at(panel_id);
774} 786}
775 787
776int GD_GetRoomForPainting(const std::string &painting_id) { 788const PaintingExit &GD_GetPaintingExit(int painting_id) {
777 return GetState().room_by_painting_.at(painting_id); 789 return GetState().paintings_.at(painting_id);
790}
791
792int GD_GetPaintingByName(const std::string &name) {
793 return GetState().painting_by_id_.at(name);
778} 794}
779 795
780const std::vector<int> &GD_GetAchievementPanels() { 796const std::vector<int> &GD_GetAchievementPanels() {
diff --git a/src/game_data.h b/src/game_data.h index 68ba5e4..e0942f7 100644 --- a/src/game_data.h +++ b/src/game_data.h
@@ -87,14 +87,16 @@ struct Exit {
87}; 87};
88 88
89struct PaintingExit { 89struct PaintingExit {
90 std::string id; 90 int id;
91 int room;
92 std::string internal_id;
91 std::optional<int> door; 93 std::optional<int> door;
92}; 94};
93 95
94struct Room { 96struct Room {
95 std::string name; 97 std::string name;
96 std::vector<Exit> exits; 98 std::vector<Exit> exits;
97 std::vector<PaintingExit> paintings; 99 std::vector<int> paintings;
98 std::vector<int> sunwarps; 100 std::vector<int> sunwarps;
99 std::vector<int> panels; 101 std::vector<int> panels;
100}; 102};
@@ -113,7 +115,7 @@ struct MapArea {
113 int id; 115 int id;
114 std::string name; 116 std::string name;
115 std::vector<Location> locations; 117 std::vector<Location> locations;
116 std::vector<PaintingExit> paintings; 118 std::vector<int> paintings;
117 int map_x; 119 int map_x;
118 int map_y; 120 int map_y;
119 int classification = 0; 121 int classification = 0;
@@ -152,7 +154,8 @@ const std::vector<Door>& GD_GetDoors();
152const Door& GD_GetDoor(int door_id); 154const Door& GD_GetDoor(int door_id);
153int GD_GetDoorByName(const std::string& name); 155int GD_GetDoorByName(const std::string& name);
154const Panel& GD_GetPanel(int panel_id); 156const Panel& GD_GetPanel(int panel_id);
155int GD_GetRoomForPainting(const std::string& painting_id); 157const PaintingExit& GD_GetPaintingExit(int painting_id);
158int GD_GetPaintingByName(const std::string& name);
156const std::vector<int>& GD_GetAchievementPanels(); 159const std::vector<int>& GD_GetAchievementPanels();
157int GD_GetItemIdForColor(LingoColor color); 160int GD_GetItemIdForColor(LingoColor color);
158const std::vector<int>& GD_GetSunwarpDoors(); 161const std::vector<int>& GD_GetSunwarpDoors();
diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 0385f89..f0810c9 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp
@@ -172,9 +172,10 @@ void TrackerPanel::Redraw() {
172 } 172 }
173 173
174 if (AP_IsPaintingShuffle()) { 174 if (AP_IsPaintingShuffle()) {
175 for (const PaintingExit &painting : map_area.paintings) { 175 for (int painting_id : map_area.paintings) {
176 if (!AP_IsPaintingChecked(painting.id)) { 176 const PaintingExit &painting = GD_GetPaintingExit(painting_id);
177 bool reachable = painting.door ? IsDoorOpen(*painting.door) : true; 177 if (!AP_IsPaintingChecked(painting.internal_id)) {
178 bool reachable = IsPaintingReachable(painting_id);
178 179
179 if (reachable) { 180 if (reachable) {
180 has_reachable_unchecked = true; 181 has_reachable_unchecked = true;
diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 187a4a8..46bdbec 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp
@@ -141,6 +141,7 @@ class RequirementCalculator {
141struct TrackerState { 141struct TrackerState {
142 std::map<int, bool> reachability; 142 std::map<int, bool> reachability;
143 std::set<int> reachable_doors; 143 std::set<int> reachable_doors;
144 std::set<int> reachable_paintings;
144 std::mutex reachability_mutex; 145 std::mutex reachability_mutex;
145 RequirementCalculator requirements; 146 RequirementCalculator requirements;
146 std::map<int, std::map<std::string, bool>> door_reports; 147 std::map<int, std::map<std::string, bool>> door_reports;
@@ -170,6 +171,7 @@ class StateCalculator {
170 171
171 void Calculate() { 172 void Calculate() {
172 std::list<int> panel_boundary; 173 std::list<int> panel_boundary;
174 std::list<int> painting_boundary;
173 std::list<Exit> flood_boundary; 175 std::list<Exit> flood_boundary;
174 flood_boundary.push_back({.destination_room = options_.start}); 176 flood_boundary.push_back({.destination_room = options_.start});
175 177
@@ -177,6 +179,8 @@ class StateCalculator {
177 while (reachable_changed) { 179 while (reachable_changed) {
178 reachable_changed = false; 180 reachable_changed = false;
179 181
182 std::list<Exit> new_boundary;
183
180 std::list<int> new_panel_boundary; 184 std::list<int> new_panel_boundary;
181 for (int panel_id : panel_boundary) { 185 for (int panel_id : panel_boundary) {
182 if (solveable_panels_.count(panel_id)) { 186 if (solveable_panels_.count(panel_id)) {
@@ -192,7 +196,33 @@ class StateCalculator {
192 } 196 }
193 } 197 }
194 198
195 std::list<Exit> new_boundary; 199 std::list<int> new_painting_boundary;
200 for (int painting_id : painting_boundary) {
201 if (reachable_paintings_.count(painting_id)) {
202 continue;
203 }
204
205 Decision painting_reachable = IsPaintingReachable(painting_id);
206 if (painting_reachable == kYes) {
207 reachable_paintings_.insert(painting_id);
208 reachable_changed = true;
209
210 PaintingExit cur_painting = GD_GetPaintingExit(painting_id);
211 if (AP_GetPaintingMapping().count(cur_painting.internal_id) &&
212 AP_GetCheckedPaintings().count(cur_painting.internal_id)) {
213 Exit painting_exit;
214 PaintingExit target_painting =
215 GD_GetPaintingExit(GD_GetPaintingByName(
216 AP_GetPaintingMapping().at(cur_painting.internal_id)));
217 painting_exit.destination_room = target_painting.room;
218
219 new_boundary.push_back(painting_exit);
220 }
221 } else if (painting_reachable == kMaybe) {
222 new_painting_boundary.push_back(painting_id);
223 }
224 }
225
196 for (const Exit& room_exit : flood_boundary) { 226 for (const Exit& room_exit : flood_boundary) {
197 if (reachable_rooms_.count(room_exit.destination_room)) { 227 if (reachable_rooms_.count(room_exit.destination_room)) {
198 continue; 228 continue;
@@ -227,16 +257,8 @@ class StateCalculator {
227 } 257 }
228 258
229 if (AP_IsPaintingShuffle()) { 259 if (AP_IsPaintingShuffle()) {
230 for (const PaintingExit& out_edge : room_obj.paintings) { 260 for (int out_edge : room_obj.paintings) {
231 if (AP_GetPaintingMapping().count(out_edge.id) && 261 new_painting_boundary.push_back(out_edge);
232 AP_GetCheckedPaintings().count(out_edge.id)) {
233 Exit painting_exit;
234 painting_exit.destination_room = GD_GetRoomForPainting(
235 AP_GetPaintingMapping().at(out_edge.id));
236 painting_exit.door = out_edge.door;
237
238 new_boundary.push_back(painting_exit);
239 }
240 } 262 }
241 } 263 }
242 264
@@ -285,6 +307,7 @@ class StateCalculator {
285 307
286 flood_boundary = new_boundary; 308 flood_boundary = new_boundary;
287 panel_boundary = new_panel_boundary; 309 panel_boundary = new_panel_boundary;
310 painting_boundary = new_painting_boundary;
288 } 311 }
289 312
290 // Now that we know the full reachable area, let's make sure all doors are 313 // Now that we know the full reachable area, let's make sure all doors are
@@ -302,6 +325,10 @@ class StateCalculator {
302 325
303 const std::set<int>& GetSolveablePanels() const { return solveable_panels_; } 326 const std::set<int>& GetSolveablePanels() const { return solveable_panels_; }
304 327
328 const std::set<int>& GetReachablePaintings() const {
329 return reachable_paintings_;
330 }
331
305 const std::map<int, std::map<std::string, bool>>& GetDoorReports() const { 332 const std::map<int, std::map<std::string, bool>>& GetDoorReports() const {
306 return door_report_; 333 return door_report_;
307 } 334 }
@@ -450,6 +477,15 @@ class StateCalculator {
450 return AreRequirementsSatisfied(GetState().requirements.GetPanel(panel_id)); 477 return AreRequirementsSatisfied(GetState().requirements.GetPanel(panel_id));
451 } 478 }
452 479
480 Decision IsPaintingReachable(int painting_id) {
481 const PaintingExit& painting = GD_GetPaintingExit(painting_id);
482 if (painting.door) {
483 return IsDoorReachable(*painting.door);
484 }
485
486 return kYes;
487 }
488
453 Decision IsExitUsable(const Exit& room_exit) { 489 Decision IsExitUsable(const Exit& room_exit) {
454 if (room_exit.type == EntranceType::kPilgrimage) { 490 if (room_exit.type == EntranceType::kPilgrimage) {
455 if (options_.pilgrimage || !AP_IsPilgrimageEnabled()) { 491 if (options_.pilgrimage || !AP_IsPilgrimageEnabled()) {
@@ -533,6 +569,7 @@ class StateCalculator {
533 std::set<int> reachable_rooms_; 569 std::set<int> reachable_rooms_;
534 std::map<int, Decision> door_decisions_; 570 std::map<int, Decision> door_decisions_;
535 std::set<int> solveable_panels_; 571 std::set<int> solveable_panels_;
572 std::set<int> reachable_paintings_;
536 std::map<int, std::map<std::string, bool>> door_report_; 573 std::map<int, std::map<std::string, bool>> door_report_;
537}; 574};
538 575
@@ -573,6 +610,7 @@ void RecalculateReachability() {
573 } 610 }
574 } 611 }
575 612
613 std::set<int> reachable_paintings = state_calculator.GetReachablePaintings();
576 std::map<int, std::map<std::string, bool>> door_reports = 614 std::map<int, std::map<std::string, bool>> door_reports =
577 state_calculator.GetDoorReports(); 615 state_calculator.GetDoorReports();
578 616
@@ -580,6 +618,7 @@ void RecalculateReachability() {
580 std::lock_guard reachability_guard(GetState().reachability_mutex); 618 std::lock_guard reachability_guard(GetState().reachability_mutex);
581 std::swap(GetState().reachability, new_reachability); 619 std::swap(GetState().reachability, new_reachability);
582 std::swap(GetState().reachable_doors, new_reachable_doors); 620 std::swap(GetState().reachable_doors, new_reachable_doors);
621 std::swap(GetState().reachable_paintings, reachable_paintings);
583 std::swap(GetState().door_reports, door_reports); 622 std::swap(GetState().door_reports, door_reports);
584 } 623 }
585} 624}
@@ -600,6 +639,12 @@ bool IsDoorOpen(int door_id) {
600 return GetState().reachable_doors.count(door_id); 639 return GetState().reachable_doors.count(door_id);
601} 640}
602 641
642bool IsPaintingReachable(int painting_id) {
643 std::lock_guard reachability_guard(GetState().reachability_mutex);
644
645 return GetState().reachable_paintings.count(painting_id);
646}
647
603const std::map<std::string, bool>& GetDoorRequirements(int door_id) { 648const std::map<std::string, bool>& GetDoorRequirements(int door_id) {
604 std::lock_guard reachability_guard(GetState().reachability_mutex); 649 std::lock_guard reachability_guard(GetState().reachability_mutex);
605 650
diff --git a/src/tracker_state.h b/src/tracker_state.h index 7acb0f2..c7857a0 100644 --- a/src/tracker_state.h +++ b/src/tracker_state.h
@@ -12,6 +12,8 @@ bool IsLocationReachable(int location_id);
12 12
13bool IsDoorOpen(int door_id); 13bool IsDoorOpen(int door_id);
14 14
15bool IsPaintingReachable(int painting_id);
16
15const std::map<std::string, bool>& GetDoorRequirements(int door_id); 17const std::map<std::string, bool>& GetDoorRequirements(int door_id);
16 18
17#endif /* end of include guard: TRACKER_STATE_H_8639BC90 */ 19#endif /* end of include guard: TRACKER_STATE_H_8639BC90 */