about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2025-03-13 11:55:55 -0400
committerStar Rauchenberger <fefferburbia@gmail.com>2025-03-13 11:55:55 -0400
commitdacbe8e3fbda85f7c2e7e7b660795f2a080a9d25 (patch)
tree65f201e323d5d6a36291ec5a108c4b14649116da /src
parentf4945731a958371d206ccfa4a34486b263be5b21 (diff)
downloadlingo-ap-tracker-dacbe8e3fbda85f7c2e7e7b660795f2a080a9d25.tar.gz
lingo-ap-tracker-dacbe8e3fbda85f7c2e7e7b660795f2a080a9d25.tar.bz2
lingo-ap-tracker-dacbe8e3fbda85f7c2e7e7b660795f2a080a9d25.zip
Use sync fields for hunt panels
Diffstat (limited to 'src')
-rw-r--r--src/ap_state.cpp53
-rw-r--r--src/ap_state.h4
-rw-r--r--src/area_popup.cpp3
-rw-r--r--src/game_data.cpp31
-rw-r--r--src/game_data.h2
-rw-r--r--src/tracker_frame.cpp12
-rw-r--r--src/tracker_frame.h3
-rw-r--r--src/tracker_panel.cpp3
8 files changed, 81 insertions, 30 deletions
diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 023bf7f..8ba6633 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp
@@ -10,6 +10,7 @@
10#include <any> 10#include <any>
11#include <apclient.hpp> 11#include <apclient.hpp>
12#include <apuuid.hpp> 12#include <apuuid.hpp>
13#include <bitset>
13#include <chrono> 14#include <chrono>
14#include <exception> 15#include <exception>
15#include <filesystem> 16#include <filesystem>
@@ -37,6 +38,10 @@ constexpr int ITEM_HANDLING = 7; // <- all
37constexpr int CONNECTION_TIMEOUT = 50000; // 50 seconds 38constexpr int CONNECTION_TIMEOUT = 50000; // 50 seconds
38constexpr int CONNECTION_BACKOFF_INTERVAL = 100; 39constexpr int CONNECTION_BACKOFF_INTERVAL = 100;
39 40
41constexpr int PANEL_COUNT = 803;
42constexpr int PANEL_BITFIELD_LENGTH = 48;
43constexpr int PANEL_BITFIELDS = 17;
44
40namespace { 45namespace {
41 46
42const std::set<long> kNonProgressionItems = { 47const std::set<long> kNonProgressionItems = {
@@ -79,6 +84,7 @@ struct APState {
79 std::set<int64_t> checked_locations; 84 std::set<int64_t> checked_locations;
80 std::map<std::string, std::any> data_storage; 85 std::map<std::string, std::any> data_storage;
81 std::optional<std::tuple<int, int>> player_pos; 86 std::optional<std::tuple<int, int>> player_pos;
87 std::bitset<PANEL_COUNT> solved_panels;
82 88
83 DoorShuffleMode door_shuffle_mode = kNO_DOORS; 89 DoorShuffleMode door_shuffle_mode = kNO_DOORS;
84 bool group_doors = false; 90 bool group_doors = false;
@@ -142,6 +148,7 @@ struct APState {
142 checked_locations.clear(); 148 checked_locations.clear();
143 data_storage.clear(); 149 data_storage.clear();
144 player_pos = std::nullopt; 150 player_pos = std::nullopt;
151 solved_panels.reset();
145 victory_data_storage_key.clear(); 152 victory_data_storage_key.clear();
146 door_shuffle_mode = kNO_DOORS; 153 door_shuffle_mode = kNO_DOORS;
147 group_doors = false; 154 group_doors = false;
@@ -216,14 +223,6 @@ struct APState {
216 return checked_locations.count(location_id); 223 return checked_locations.count(location_id);
217 } 224 }
218 225
219 bool HasCheckedHuntPanel(int location_id) {
220 std::lock_guard state_guard(state_mutex);
221
222 std::string key =
223 fmt::format("{}Hunt|{}", data_storage_prefix, location_id);
224 return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key));
225 }
226
227 bool HasItem(int item_id, int quantity) { 226 bool HasItem(int item_id, int quantity) {
228 return inventory.count(item_id) && inventory.at(item_id) >= quantity; 227 return inventory.count(item_id) && inventory.at(item_id) >= quantity;
229 } 228 }
@@ -288,6 +287,12 @@ struct APState {
288 30; // CLIENT_GOAL 287 30; // CLIENT_GOAL
289 } 288 }
290 289
290 bool IsPanelSolved(int solve_index) {
291 std::lock_guard state_guard(state_mutex);
292
293 return solved_panels.test(solve_index);
294 }
295
291 private: 296 private:
292 void Initialize() { 297 void Initialize() {
293 if (!initialized) { 298 if (!initialized) {
@@ -300,11 +305,8 @@ struct APState {
300 "Achievement|{}", GD_GetPanel(panel_id).achievement_name)); 305 "Achievement|{}", GD_GetPanel(panel_id).achievement_name));
301 } 306 }
302 307
303 for (const MapArea& map_area : GD_GetMapAreas()) { 308 for (int i = 0; i < PANEL_BITFIELDS; i++) {
304 for (const Location& location : map_area.locations) { 309 tracked_data_storage_keys.push_back(fmt::format("Panels_{}", i));
305 tracked_data_storage_keys.push_back(
306 fmt::format("Hunt|{}", location.ap_location_id));
307 }
308 } 310 }
309 311
310 tracked_data_storage_keys.push_back("PlayerPos"); 312 tracked_data_storage_keys.push_back("PlayerPos");
@@ -610,8 +612,6 @@ struct APState {
610 612
611 if (key.find("Achievement|") != std::string::npos) { 613 if (key.find("Achievement|") != std::string::npos) {
612 state_update.achievements = true; 614 state_update.achievements = true;
613 } else if (key.find("Hunt|") != std::string::npos) {
614 state_update.hunt_panels = true;
615 } 615 }
616 } else if (value.is_number()) { 616 } else if (value.is_number()) {
617 data_storage[key] = value.get<int>(); 617 data_storage[key] = value.get<int>();
@@ -620,6 +620,21 @@ struct APState {
620 620
621 if (key == victory_data_storage_key) { 621 if (key == victory_data_storage_key) {
622 state_update.cleared_locations = true; 622 state_update.cleared_locations = true;
623 } else if (key.find("Panels_") != std::string::npos) {
624 int bitfield_num =
625 std::stoi(key.substr(data_storage_prefix.size() + 7));
626 uint64_t bitfield_value = value.get<uint64_t>();
627 for (int i = 0; i < PANEL_BITFIELD_LENGTH; i++) {
628 if ((bitfield_value & (1LL << i)) != 0) {
629 int solve_index = bitfield_num * PANEL_BITFIELD_LENGTH + i;
630
631 if (!solved_panels.test(solve_index)) {
632 state_update.panels.insert(solve_index);
633 }
634
635 solved_panels.set(solve_index);
636 }
637 }
623 } 638 }
624 } else if (value.is_object()) { 639 } else if (value.is_object()) {
625 if (key.ends_with("PlayerPos")) { 640 if (key.ends_with("PlayerPos")) {
@@ -720,10 +735,6 @@ bool AP_HasCheckedGameLocation(int location_id) {
720 return GetState().HasCheckedGameLocation(location_id); 735 return GetState().HasCheckedGameLocation(location_id);
721} 736}
722 737
723bool AP_HasCheckedHuntPanel(int location_id) {
724 return GetState().HasCheckedHuntPanel(location_id);
725}
726
727bool AP_HasItem(int item_id, int quantity) { 738bool AP_HasItem(int item_id, int quantity) {
728 return GetState().HasItem(item_id, quantity); 739 return GetState().HasItem(item_id, quantity);
729} 740}
@@ -892,3 +903,7 @@ std::optional<std::tuple<int, int>> AP_GetPlayerPosition() {
892 903
893 return GetState().player_pos; 904 return GetState().player_pos;
894} 905}
906
907bool AP_IsPanelSolved(int solve_index) {
908 return GetState().IsPanelSolved(solve_index);
909}
diff --git a/src/ap_state.h b/src/ap_state.h index 482c155..8641000 100644 --- a/src/ap_state.h +++ b/src/ap_state.h
@@ -57,8 +57,6 @@ std::string AP_GetSaveName();
57 57
58bool AP_HasCheckedGameLocation(int location_id); 58bool AP_HasCheckedGameLocation(int location_id);
59 59
60bool AP_HasCheckedHuntPanel(int location_id);
61
62// This doesn't lock the state mutex, for speed, so it must ONLY be called from 60// This doesn't lock the state mutex, for speed, so it must ONLY be called from
63// RecalculateReachability, which is only called from the APState thread anyway. 61// RecalculateReachability, which is only called from the APState thread anyway.
64bool AP_HasItem(int item_id, int quantity = 1); 62bool AP_HasItem(int item_id, int quantity = 1);
@@ -122,4 +120,6 @@ bool AP_HasReachedGoal();
122 120
123std::optional<std::tuple<int, int>> AP_GetPlayerPosition(); 121std::optional<std::tuple<int, int>> AP_GetPlayerPosition();
124 122
123bool AP_IsPanelSolved(int solve_index);
124
125#endif /* end of include guard: AP_STATE_H_664A4180 */ 125#endif /* end of include guard: AP_STATE_H_664A4180 */
diff --git a/src/area_popup.cpp b/src/area_popup.cpp index a8e6004..2c9d18d 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp
@@ -145,7 +145,8 @@ void AreaPopup::UpdateIndicators() {
145 } else { 145 } else {
146 checked = AP_HasCheckedGameLocation(location.ap_location_id) || 146 checked = AP_HasCheckedGameLocation(location.ap_location_id) ||
147 (location.hunt && 147 (location.hunt &&
148 AP_HasCheckedHuntPanel(location.ap_location_id)); 148 AP_IsPanelSolved(
149 GD_GetPanel(*location.single_panel).solve_index));
149 } 150 }
150 151
151 const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_; 152 const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_;
diff --git a/src/game_data.cpp b/src/game_data.cpp index 7c221e9..a5af66b 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp
@@ -33,6 +33,7 @@ struct GameData {
33 33
34 std::map<std::string, int> room_by_painting_; 34 std::map<std::string, int> room_by_painting_;
35 std::map<int, int> room_by_sunwarp_; 35 std::map<int, int> room_by_sunwarp_;
36 std::map<int, int> panel_by_solve_index_;
36 37
37 std::vector<int> achievement_panels_; 38 std::vector<int> achievement_panels_;
38 39
@@ -77,6 +78,8 @@ struct GameData {
77 78
78 rooms_.reserve(lingo_config.size() * 2); 79 rooms_.reserve(lingo_config.size() * 2);
79 80
81 std::vector<int> panel_location_ids;
82
80 for (const auto &room_it : lingo_config) { 83 for (const auto &room_it : lingo_config) {
81 int room_id = AddOrGetRoom(room_it.first.as<std::string>()); 84 int room_id = AddOrGetRoom(room_it.first.as<std::string>());
82 room_definition_order_.push_back(room_id); 85 room_definition_order_.push_back(room_id);
@@ -266,10 +269,11 @@ struct GameData {
266 ids_config["panels"][rooms_[room_id].name] && 269 ids_config["panels"][rooms_[room_id].name] &&
267 ids_config["panels"][rooms_[room_id].name] 270 ids_config["panels"][rooms_[room_id].name]
268 [panels_[panel_id].name]) { 271 [panels_[panel_id].name]) {
269 panels_[panel_id].ap_location_id = 272 int location_id = ids_config["panels"][rooms_[room_id].name]
270 ids_config["panels"][rooms_[room_id].name] 273 [panels_[panel_id].name]
271 [panels_[panel_id].name] 274 .as<int>();
272 .as<int>(); 275 panels_[panel_id].ap_location_id = location_id;
276 panel_location_ids.push_back(location_id);
273 } else { 277 } else {
274 TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", 278 TrackerLog(fmt::format("Missing AP location ID for panel {} - {}",
275 rooms_[room_id].name, 279 rooms_[room_id].name,
@@ -563,6 +567,21 @@ struct GameData {
563 } 567 }
564 } 568 }
565 569
570 // Determine the panel solve indices from the sorted location IDs.
571 std::sort(panel_location_ids.begin(), panel_location_ids.end());
572
573 std::map<int, int> solve_index_by_location_id;
574 for (int i = 0; i < panel_location_ids.size(); i++) {
575 solve_index_by_location_id[panel_location_ids[i]] = i;
576 }
577
578 for (Panel &panel : panels_) {
579 if (panel.ap_location_id != -1) {
580 panel.solve_index = solve_index_by_location_id[panel.ap_location_id];
581 panel_by_solve_index_[panel.solve_index] = panel.id;
582 }
583 }
584
566 map_areas_.reserve(areas_config.size()); 585 map_areas_.reserve(areas_config.size());
567 586
568 std::map<std::string, int> fold_areas; 587 std::map<std::string, int> fold_areas;
@@ -938,6 +957,10 @@ const Panel &GD_GetPanel(int panel_id) {
938 return GetState().panels_.at(panel_id); 957 return GetState().panels_.at(panel_id);
939} 958}
940 959
960int GD_GetPanelBySolveIndex(int solve_index) {
961 return GetState().panel_by_solve_index_.at(solve_index);
962}
963
941const std::vector<PaintingExit> &GD_GetPaintings() { 964const std::vector<PaintingExit> &GD_GetPaintings() {
942 return GetState().paintings_; 965 return GetState().paintings_;
943} 966}
diff --git a/src/game_data.h b/src/game_data.h index 0facd12..815ae2e 100644 --- a/src/game_data.h +++ b/src/game_data.h
@@ -57,6 +57,7 @@ struct Panel {
57 int ap_location_id = -1; 57 int ap_location_id = -1;
58 bool hunt = false; 58 bool hunt = false;
59 int panel_door = -1; 59 int panel_door = -1;
60 int solve_index = -1;
60}; 61};
61 62
62struct ProgressiveRequirement { 63struct ProgressiveRequirement {
@@ -176,6 +177,7 @@ const std::vector<Door>& GD_GetDoors();
176const Door& GD_GetDoor(int door_id); 177const Door& GD_GetDoor(int door_id);
177int GD_GetDoorByName(const std::string& name); 178int GD_GetDoorByName(const std::string& name);
178const Panel& GD_GetPanel(int panel_id); 179const Panel& GD_GetPanel(int panel_id);
180int GD_GetPanelBySolveIndex(int solve_index);
179const PanelDoor& GD_GetPanelDoor(int panel_door_id); 181const PanelDoor& GD_GetPanelDoor(int panel_door_id);
180const std::vector<PaintingExit>& GD_GetPaintings(); 182const std::vector<PaintingExit>& GD_GetPaintings();
181const PaintingExit& GD_GetPaintingExit(int painting_id); 183const PaintingExit& GD_GetPaintingExit(int painting_id);
diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index fa68582..84017a3 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp
@@ -9,6 +9,7 @@
9#include <wx/stdpaths.h> 9#include <wx/stdpaths.h>
10#include <wx/webrequest.h> 10#include <wx/webrequest.h>
11 11
12#include <algorithm>
12#include <nlohmann/json.hpp> 13#include <nlohmann/json.hpp>
13#include <sstream> 14#include <sstream>
14 15
@@ -358,10 +359,17 @@ void TrackerFrame::OnStateChanged(StateChangedEvent &event) {
358 359
359 return; 360 return;
360 } 361 }
362
363 bool hunt_panels = false;
364 if (GetTrackerConfig().show_hunt_panels) {
365 hunt_panels = std::any_of(
366 state.panels.begin(), state.panels.end(), [](int solve_index) {
367 return GD_GetPanel(GD_GetPanelBySolveIndex(solve_index)).hunt;
368 });
369 }
361 370
362 if (!state.items.empty() || !state.paintings.empty() || 371 if (!state.items.empty() || !state.paintings.empty() ||
363 state.cleared_locations || 372 state.cleared_locations || hunt_panels) {
364 (state.hunt_panels && GetTrackerConfig().show_hunt_panels)) {
365 // TODO: The only real reason to reset tracker_panel during an active 373 // TODO: The only real reason to reset tracker_panel during an active
366 // connection is if the hunt panels setting changes. If we remove hunt 374 // connection is if the hunt panels setting changes. If we remove hunt
367 // panels later, we can get rid of this. 375 // panels later, we can get rid of this.
diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 131c7b8..8eb4465 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h
@@ -8,6 +8,7 @@
8#endif 8#endif
9 9
10#include <memory> 10#include <memory>
11#include <set>
11 12
12#include "ap_state.h" 13#include "ap_state.h"
13#include "icons.h" 14#include "icons.h"
@@ -55,7 +56,7 @@ struct StateUpdate {
55 bool achievements = false; 56 bool achievements = false;
56 bool open_panels_tab = false; 57 bool open_panels_tab = false;
57 bool cleared_locations = false; 58 bool cleared_locations = false;
58 bool hunt_panels = false; 59 std::set<int> panels;
59 bool player_position = false; 60 bool player_position = false;
60 bool changed_settings = false; 61 bool changed_settings = false;
61}; 62};
diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 9adcb1f..d8ba054 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp
@@ -293,7 +293,8 @@ void TrackerPanel::Redraw() {
293 } else if (AP_IsLocationVisible(section.classification)) { 293 } else if (AP_IsLocationVisible(section.classification)) {
294 has_unchecked = !AP_HasCheckedGameLocation(section.ap_location_id); 294 has_unchecked = !AP_HasCheckedGameLocation(section.ap_location_id);
295 } else if (section.hunt && GetTrackerConfig().show_hunt_panels) { 295 } else if (section.hunt && GetTrackerConfig().show_hunt_panels) {
296 has_unchecked = !AP_HasCheckedHuntPanel(section.ap_location_id); 296 has_unchecked =
297 !AP_IsPanelSolved(GD_GetPanel(*section.single_panel).solve_index);
297 } 298 }
298 299
299 if (has_unchecked) { 300 if (has_unchecked) {