diff options
| -rw-r--r-- | src/ap_state.cpp | 53 | ||||
| -rw-r--r-- | src/ap_state.h | 4 | ||||
| -rw-r--r-- | src/area_popup.cpp | 3 | ||||
| -rw-r--r-- | src/game_data.cpp | 31 | ||||
| -rw-r--r-- | src/game_data.h | 2 | ||||
| -rw-r--r-- | src/tracker_frame.cpp | 12 | ||||
| -rw-r--r-- | src/tracker_frame.h | 3 | ||||
| -rw-r--r-- | src/tracker_panel.cpp | 3 |
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 | |||
| 37 | constexpr int CONNECTION_TIMEOUT = 50000; // 50 seconds | 38 | constexpr int CONNECTION_TIMEOUT = 50000; // 50 seconds |
| 38 | constexpr int CONNECTION_BACKOFF_INTERVAL = 100; | 39 | constexpr int CONNECTION_BACKOFF_INTERVAL = 100; |
| 39 | 40 | ||
| 41 | constexpr int PANEL_COUNT = 803; | ||
| 42 | constexpr int PANEL_BITFIELD_LENGTH = 48; | ||
| 43 | constexpr int PANEL_BITFIELDS = 17; | ||
| 44 | |||
| 40 | namespace { | 45 | namespace { |
| 41 | 46 | ||
| 42 | const std::set<long> kNonProgressionItems = { | 47 | const 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 | ||
| 723 | bool AP_HasCheckedHuntPanel(int location_id) { | ||
| 724 | return GetState().HasCheckedHuntPanel(location_id); | ||
| 725 | } | ||
| 726 | |||
| 727 | bool AP_HasItem(int item_id, int quantity) { | 738 | bool 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 | |||
| 907 | bool 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 | ||
| 58 | bool AP_HasCheckedGameLocation(int location_id); | 58 | bool AP_HasCheckedGameLocation(int location_id); |
| 59 | 59 | ||
| 60 | bool 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. |
| 64 | bool AP_HasItem(int item_id, int quantity = 1); | 62 | bool AP_HasItem(int item_id, int quantity = 1); |
| @@ -122,4 +120,6 @@ bool AP_HasReachedGoal(); | |||
| 122 | 120 | ||
| 123 | std::optional<std::tuple<int, int>> AP_GetPlayerPosition(); | 121 | std::optional<std::tuple<int, int>> AP_GetPlayerPosition(); |
| 124 | 122 | ||
| 123 | bool 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 | ||
| 960 | int GD_GetPanelBySolveIndex(int solve_index) { | ||
| 961 | return GetState().panel_by_solve_index_.at(solve_index); | ||
| 962 | } | ||
| 963 | |||
| 941 | const std::vector<PaintingExit> &GD_GetPaintings() { | 964 | const 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 | ||
| 62 | struct ProgressiveRequirement { | 63 | struct ProgressiveRequirement { |
| @@ -176,6 +177,7 @@ const std::vector<Door>& GD_GetDoors(); | |||
| 176 | const Door& GD_GetDoor(int door_id); | 177 | const Door& GD_GetDoor(int door_id); |
| 177 | int GD_GetDoorByName(const std::string& name); | 178 | int GD_GetDoorByName(const std::string& name); |
| 178 | const Panel& GD_GetPanel(int panel_id); | 179 | const Panel& GD_GetPanel(int panel_id); |
| 180 | int GD_GetPanelBySolveIndex(int solve_index); | ||
| 179 | const PanelDoor& GD_GetPanelDoor(int panel_door_id); | 181 | const PanelDoor& GD_GetPanelDoor(int panel_door_id); |
| 180 | const std::vector<PaintingExit>& GD_GetPaintings(); | 182 | const std::vector<PaintingExit>& GD_GetPaintings(); |
| 181 | const PaintingExit& GD_GetPaintingExit(int painting_id); | 183 | const 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) { |
