diff options
35 files changed, 411 insertions, 487 deletions
| diff --git a/.gitmodules b/.gitmodules index ebe016f..1a69477 100644 --- a/.gitmodules +++ b/.gitmodules | |||
| @@ -16,3 +16,6 @@ | |||
| 16 | [submodule "vendor/vcpkg"] | 16 | [submodule "vendor/vcpkg"] |
| 17 | path = vendor/vcpkg | 17 | path = vendor/vcpkg |
| 18 | url = https://github.com/Microsoft/vcpkg.git | 18 | url = https://github.com/Microsoft/vcpkg.git |
| 19 | [submodule "vendor/websocketpp"] | ||
| 20 | path = vendor/websocketpp | ||
| 21 | url = https://github.com/zaphoyd/websocketpp | ||
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0fdc2..e2444b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
| @@ -1,5 +1,61 @@ | |||
| 1 | # lingo-ap-tracker Releases | 1 | # lingo-ap-tracker Releases |
| 2 | 2 | ||
| 3 | ## v2.0.2 - 2025-05-24 | ||
| 4 | |||
| 5 | - Fixed issue connecting to the Archipelago 0.6.2 RC server. | ||
| 6 | |||
| 7 | Download: | ||
| 8 | [lingo-ap-tracker-v2.0.2-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v2.0.2-win64.zip)<br/> | ||
| 9 | Source: [v2.0.2](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v2.0.2) | ||
| 10 | |||
| 11 | ## v2.0.1 - 2025-04-06 | ||
| 12 | |||
| 13 | - The tracker now assumes postgame shuffle is enabled when the flag is not | ||
| 14 | present in slot data (as is the case with Archipelago 0.6.1 and earlier). | ||
| 15 | Players who have postgame shuffle disabled will unfortunately see locations | ||
| 16 | that are not in their world, until this problem can be fixed. | ||
| 17 | |||
| 18 | Download: | ||
| 19 | [lingo-ap-tracker-v2.0.1-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v2.0.1-win64.zip)<br/> | ||
| 20 | Source: [v2.0.1](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v2.0.1) | ||
| 21 | |||
| 22 | ## v2.0.0 - 2025-04-01 | ||
| 23 | |||
| 24 | - Compatibility update for Archipelago 0.6.0. | ||
| 25 | |||
| 26 | Download: | ||
| 27 | [lingo-ap-tracker-v2.0.0-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v2.0.0-win64.zip)<br/> | ||
| 28 | Source: [v2.0.0](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v2.0.0) | ||
| 29 | |||
| 30 | ## v1.0.0 - 2025-03-21 | ||
| 31 | |||
| 32 | After almost two years of development, we have finally hit version 1! | ||
| 33 | |||
| 34 | - The subway map now uses Kinrah's updated map image, including fully separated | ||
| 35 | paintings for places like The Wondrous and Orange Tower Sixth Floor, as well | ||
| 36 | as indicators for the color items. | ||
| 37 | - Paintings have friendly names now instead of the internal game IDs. They are | ||
| 38 | also distinguished from regular checks by using an owl icon instead of an eye. | ||
| 39 | - The tracker is now able to read your solved panel state from the multiworld | ||
| 40 | (requires v5.3.0 of the client). This obsoletes save file parsing and | ||
| 41 | receiving panel solves by connecting to the game. | ||
| 42 | - Numerous optimizations to reachability detection and rendering. | ||
| 43 | - Added a pane that shows all items received, similar to the web tracker. | ||
| 44 | - Added a pane that shows your currently revealed painting mapping, as well as a | ||
| 45 | button that reveals it all immediately. | ||
| 46 | - Added a pane that shows your slot options. | ||
| 47 | - Postgame shuffle being disabled is handled properly now, and removed locations | ||
| 48 | are no longer shown. | ||
| 49 | - Improved support for high-DPI screens. The tracker should no longer appear | ||
| 50 | "blurry" on a high-DPI screen, and should also be able to react properly to | ||
| 51 | being moved between screens with different DPIs. | ||
| 52 | - The update checker is now able to download and install tracker updates in | ||
| 53 | place. | ||
| 54 | |||
| 55 | Download: | ||
| 56 | [lingo-ap-tracker-v1.0.0-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v1.0.0-win64.zip)<br/> | ||
| 57 | Source: [v1.0.0](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v1.0.0) | ||
| 58 | |||
| 3 | ## v0.12.3 - 2025-03-03 | 59 | ## v0.12.3 - 2025-03-03 |
| 4 | 60 | ||
| 5 | - Fixed issue with non-ASCII connection details. | 61 | - Fixed issue with non-ASCII connection details. |
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 9432f2e..ef741fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | cmake_minimum_required (VERSION 3.1) | 1 | cmake_minimum_required (VERSION 3.20) |
| 2 | project (lingo_ap_tracker) | 2 | project (lingo_ap_tracker) |
| 3 | 3 | ||
| 4 | if (MSVC) | 4 | if (MSVC) |
| @@ -10,7 +10,6 @@ endif(MSVC) | |||
| 10 | find_package(wxWidgets CONFIG REQUIRED) | 10 | find_package(wxWidgets CONFIG REQUIRED) |
| 11 | find_package(OpenSSL REQUIRED) | 11 | find_package(OpenSSL REQUIRED) |
| 12 | find_package(yaml-cpp REQUIRED) | 12 | find_package(yaml-cpp REQUIRED) |
| 13 | find_package(websocketpp REQUIRED) | ||
| 14 | find_package(fmt REQUIRED) | 13 | find_package(fmt REQUIRED) |
| 15 | 14 | ||
| 16 | include_directories( | 15 | include_directories( |
| @@ -19,7 +18,7 @@ include_directories( | |||
| 19 | vendor/asio/asio/include | 18 | vendor/asio/asio/include |
| 20 | vendor/nlohmann | 19 | vendor/nlohmann |
| 21 | vendor/valijson/include | 20 | vendor/valijson/include |
| 22 | ${websocketpp_INCLUDE_DIRS} | 21 | vendor/websocketpp |
| 23 | vendor/wswrap/include | 22 | vendor/wswrap/include |
| 24 | ${yaml-cpp_INCLUDE_DIRS} | 23 | ${yaml-cpp_INCLUDE_DIRS} |
| 25 | ${OpenSSL_INCLUDE_DIRS} | 24 | ${OpenSSL_INCLUDE_DIRS} |
| @@ -49,7 +48,6 @@ set(SOURCE_FILES | |||
| 49 | "src/subway_map.cpp" | 48 | "src/subway_map.cpp" |
| 50 | "src/network_set.cpp" | 49 | "src/network_set.cpp" |
| 51 | "src/logger.cpp" | 50 | "src/logger.cpp" |
| 52 | "src/godot_variant.cpp" | ||
| 53 | "src/ipc_state.cpp" | 51 | "src/ipc_state.cpp" |
| 54 | "src/ipc_dialog.cpp" | 52 | "src/ipc_dialog.cpp" |
| 55 | "src/report_popup.cpp" | 53 | "src/report_popup.cpp" |
| @@ -69,7 +67,7 @@ endif(MSVC) | |||
| 69 | add_executable(lingo_ap_tracker ${SOURCE_FILES}) | 67 | add_executable(lingo_ap_tracker ${SOURCE_FILES}) |
| 70 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) | 68 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) |
| 71 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON) | 69 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON) |
| 72 | target_link_libraries(lingo_ap_tracker PRIVATE fmt::fmt OpenSSL::SSL OpenSSL::Crypto websocketpp::websocketpp wx::core wx::base wx::net yaml-cpp::yaml-cpp) | 70 | target_link_libraries(lingo_ap_tracker PRIVATE fmt::fmt OpenSSL::SSL OpenSSL::Crypto wx::core wx::base wx::net yaml-cpp::yaml-cpp) |
| 73 | 71 | ||
| 74 | set(SRC_DIR "${CMAKE_SOURCE_DIR}/assets") | 72 | set(SRC_DIR "${CMAKE_SOURCE_DIR}/assets") |
| 75 | set(DST_DIR "${CMAKE_BINARY_DIR}/$<CONFIG>/assets") | 73 | set(DST_DIR "${CMAKE_BINARY_DIR}/$<CONFIG>/assets") |
| diff --git a/VERSION b/VERSION index 1af39b3..b02d37b 100644 --- a/VERSION +++ b/VERSION | |||
| @@ -1 +1 @@ | |||
| v0.12.3 \ No newline at end of file | v2.0.2 \ No newline at end of file | ||
| diff --git a/VERSION.yaml b/VERSION.yaml index c4ad3ec..8f86a39 100644 --- a/VERSION.yaml +++ b/VERSION.yaml | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | version: v0.12.3 | 1 | version: v2.0.2 |
| 2 | packages: | 2 | packages: |
| 3 | win64: | 3 | win64: |
| 4 | url: "https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.12.3-win64.zip" | 4 | url: "https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v2.0.2-win64.zip" |
| 5 | checksum: "0c0d8310d1ecae4f1b8de933661ba14f63aac011cd2b22947aff7085d997f0db" | 5 | checksum: "dfb2fce2f5b14c09f4af7e7fcf6478e420a070db9fb23f9f9ad196f2db4b7518" |
| 6 | files: | 6 | files: |
| 7 | - fmt.dll | 7 | - fmt.dll |
| 8 | - jpeg62.dll | 8 | - jpeg62.dll |
| @@ -20,7 +20,7 @@ packages: | |||
| 20 | - zlib1.dll | 20 | - zlib1.dll |
| 21 | - assets/areas.yaml | 21 | - assets/areas.yaml |
| 22 | - assets/checked.png | 22 | - assets/checked.png |
| 23 | # TODO: next release will contain the checked owl | 23 | - assets/checked_owl.png |
| 24 | - assets/ids.yaml | 24 | - assets/ids.yaml |
| 25 | - assets/lingo_map.png | 25 | - assets/lingo_map.png |
| 26 | - assets/LL1.yaml | 26 | - assets/LL1.yaml |
| diff --git a/assets/areas.yaml b/assets/areas.yaml index cbcf23a..a615e2c 100755 --- a/assets/areas.yaml +++ b/assets/areas.yaml | |||
| @@ -279,6 +279,8 @@ | |||
| 279 | map: [1368, 2103] | 279 | map: [1368, 2103] |
| 280 | Art Gallery: | 280 | Art Gallery: |
| 281 | map: [2474, 1366] | 281 | map: [2474, 1366] |
| 282 | Art Gallery (First Floor): | ||
| 283 | fold_into: Art Gallery | ||
| 282 | Art Gallery (Second Floor): | 284 | Art Gallery (Second Floor): |
| 283 | fold_into: Art Gallery | 285 | fold_into: Art Gallery |
| 284 | Art Gallery (Third Floor): | 286 | Art Gallery (Third Floor): |
| diff --git a/assets/subway.yaml b/assets/subway.yaml index 080a139..8c0df38 100644 --- a/assets/subway.yaml +++ b/assets/subway.yaml | |||
| @@ -1042,3 +1042,21 @@ | |||
| 1042 | - pos: [815, 1002] | 1042 | - pos: [815, 1002] |
| 1043 | room: Challenge Room | 1043 | room: Challenge Room |
| 1044 | door: Welcome Door | 1044 | door: Welcome Door |
| 1045 | - pos: [104, 1208] | ||
| 1046 | special: color_black | ||
| 1047 | - pos: [104, 1249] | ||
| 1048 | special: color_red | ||
| 1049 | - pos: [104, 1290] | ||
| 1050 | special: color_yellow | ||
| 1051 | - pos: [104, 1330] | ||
| 1052 | special: color_blue | ||
| 1053 | - pos: [104, 1371] | ||
| 1054 | special: color_purple | ||
| 1055 | - pos: [104, 1411] | ||
| 1056 | special: color_orange | ||
| 1057 | - pos: [104, 1451] | ||
| 1058 | special: color_green | ||
| 1059 | - pos: [104, 1491] | ||
| 1060 | special: color_brown | ||
| 1061 | - pos: [104, 1531] | ||
| 1062 | special: color_gray | ||
| diff --git a/src/achievements_pane.cpp b/src/achievements_pane.cpp index b255f3b..d23c434 100644 --- a/src/achievements_pane.cpp +++ b/src/achievements_pane.cpp | |||
| @@ -8,13 +8,14 @@ AchievementsPane::AchievementsPane(wxWindow* parent) | |||
| 8 | AppendColumn("Achievement"); | 8 | AppendColumn("Achievement"); |
| 9 | 9 | ||
| 10 | for (int panel_id : GD_GetAchievementPanels()) { | 10 | for (int panel_id : GD_GetAchievementPanels()) { |
| 11 | achievement_names_.push_back(GD_GetPanel(panel_id).achievement_name); | 11 | const Panel& panel = GD_GetPanel(panel_id); |
| 12 | achievements_.emplace_back(panel.achievement_name, panel.solve_index); | ||
| 12 | } | 13 | } |
| 13 | 14 | ||
| 14 | std::sort(std::begin(achievement_names_), std::end(achievement_names_)); | 15 | std::sort(std::begin(achievements_), std::end(achievements_)); |
| 15 | 16 | ||
| 16 | for (int i = 0; i < achievement_names_.size(); i++) { | 17 | for (int i = 0; i < achievements_.size(); i++) { |
| 17 | InsertItem(i, achievement_names_.at(i)); | 18 | InsertItem(i, std::get<0>(achievements_.at(i))); |
| 18 | } | 19 | } |
| 19 | 20 | ||
| 20 | SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER); | 21 | SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER); |
| @@ -23,8 +24,8 @@ AchievementsPane::AchievementsPane(wxWindow* parent) | |||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | void AchievementsPane::UpdateIndicators() { | 26 | void AchievementsPane::UpdateIndicators() { |
| 26 | for (int i = 0; i < achievement_names_.size(); i++) { | 27 | for (int i = 0; i < achievements_.size(); i++) { |
| 27 | if (AP_HasAchievement(achievement_names_.at(i))) { | 28 | if (AP_IsPanelSolved(std::get<1>(achievements_.at(i)))) { |
| 28 | SetItemTextColour(i, *wxBLACK); | 29 | SetItemTextColour(i, *wxBLACK); |
| 29 | } else { | 30 | } else { |
| 30 | SetItemTextColour(i, *wxRED); | 31 | SetItemTextColour(i, *wxRED); |
| diff --git a/src/achievements_pane.h b/src/achievements_pane.h index ac88cac..941b5e3 100644 --- a/src/achievements_pane.h +++ b/src/achievements_pane.h | |||
| @@ -9,6 +9,10 @@ | |||
| 9 | 9 | ||
| 10 | #include <wx/listctrl.h> | 10 | #include <wx/listctrl.h> |
| 11 | 11 | ||
| 12 | #include <string> | ||
| 13 | #include <tuple> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 12 | class AchievementsPane : public wxListView { | 16 | class AchievementsPane : public wxListView { |
| 13 | public: | 17 | public: |
| 14 | explicit AchievementsPane(wxWindow* parent); | 18 | explicit AchievementsPane(wxWindow* parent); |
| @@ -16,7 +20,7 @@ class AchievementsPane : public wxListView { | |||
| 16 | void UpdateIndicators(); | 20 | void UpdateIndicators(); |
| 17 | 21 | ||
| 18 | private: | 22 | private: |
| 19 | std::vector<std::string> achievement_names_; | 23 | std::vector<std::tuple<std::string, int>> achievements_; // name, solve index |
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | #endif /* end of include guard: ACHIEVEMENTS_PANE_H_C320D0B8 */ \ No newline at end of file | 26 | #endif /* end of include guard: ACHIEVEMENTS_PANE_H_C320D0B8 */ \ No newline at end of file |
| diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 1e5621d..8438649 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> |
| @@ -28,8 +29,8 @@ | |||
| 28 | #include "tracker_state.h" | 29 | #include "tracker_state.h" |
| 29 | 30 | ||
| 30 | constexpr int AP_MAJOR = 0; | 31 | constexpr int AP_MAJOR = 0; |
| 31 | constexpr int AP_MINOR = 4; | 32 | constexpr int AP_MINOR = 6; |
| 32 | constexpr int AP_REVISION = 5; | 33 | constexpr int AP_REVISION = 1; |
| 33 | 34 | ||
| 34 | constexpr const char* CERT_STORE_PATH = "cacert.pem"; | 35 | constexpr const char* CERT_STORE_PATH = "cacert.pem"; |
| 35 | constexpr int ITEM_HANDLING = 7; // <- all | 36 | constexpr int ITEM_HANDLING = 7; // <- all |
| @@ -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,24 +223,13 @@ 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 | } |
| 230 | 229 | ||
| 231 | bool HasAchievement(const std::string& name) { | 230 | bool HasItemSafe(int item_id, int quantity) { |
| 232 | std::lock_guard state_guard(state_mutex); | 231 | std::lock_guard state_guard(state_mutex); |
| 233 | 232 | return HasItem(item_id, quantity); | |
| 234 | std::string key = | ||
| 235 | fmt::format("{}Achievement|{}", data_storage_prefix, name); | ||
| 236 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); | ||
| 237 | } | 233 | } |
| 238 | 234 | ||
| 239 | const std::set<std::string>& GetCheckedPaintings() { | 235 | const std::set<std::string>& GetCheckedPaintings() { |
| @@ -257,8 +253,6 @@ struct APState { | |||
| 257 | checked_paintings.count(painting_mapping.at(painting_id))); | 253 | checked_paintings.count(painting_mapping.at(painting_id))); |
| 258 | } | 254 | } |
| 259 | 255 | ||
| 260 | std::string GetItemName(int id) { return apclient->get_item_name(id, "Lingo"); } | ||
| 261 | |||
| 262 | void RevealPaintings() { | 256 | void RevealPaintings() { |
| 263 | std::lock_guard state_guard(state_mutex); | 257 | std::lock_guard state_guard(state_mutex); |
| 264 | 258 | ||
| @@ -283,6 +277,12 @@ struct APState { | |||
| 283 | 30; // CLIENT_GOAL | 277 | 30; // CLIENT_GOAL |
| 284 | } | 278 | } |
| 285 | 279 | ||
| 280 | bool IsPanelSolved(int solve_index) { | ||
| 281 | std::lock_guard state_guard(state_mutex); | ||
| 282 | |||
| 283 | return solved_panels.test(solve_index); | ||
| 284 | } | ||
| 285 | |||
| 286 | private: | 286 | private: |
| 287 | void Initialize() { | 287 | void Initialize() { |
| 288 | if (!initialized) { | 288 | if (!initialized) { |
| @@ -290,16 +290,8 @@ struct APState { | |||
| 290 | 290 | ||
| 291 | std::thread([this]() { Thread(); }).detach(); | 291 | std::thread([this]() { Thread(); }).detach(); |
| 292 | 292 | ||
| 293 | for (int panel_id : GD_GetAchievementPanels()) { | 293 | for (int i = 0; i < PANEL_BITFIELDS; i++) { |
| 294 | tracked_data_storage_keys.push_back(fmt::format( | 294 | tracked_data_storage_keys.push_back(fmt::format("Panels_{}", i)); |
| 295 | "Achievement|{}", GD_GetPanel(panel_id).achievement_name)); | ||
| 296 | } | ||
| 297 | |||
| 298 | for (const MapArea& map_area : GD_GetMapAreas()) { | ||
| 299 | for (const Location& location : map_area.locations) { | ||
| 300 | tracked_data_storage_keys.push_back( | ||
| 301 | fmt::format("Hunt|{}", location.ap_location_id)); | ||
| 302 | } | ||
| 303 | } | 295 | } |
| 304 | 296 | ||
| 305 | tracked_data_storage_keys.push_back("PlayerPos"); | 297 | tracked_data_storage_keys.push_back("PlayerPos"); |
| @@ -425,7 +417,7 @@ struct APState { | |||
| 425 | } | 417 | } |
| 426 | 418 | ||
| 427 | for (const auto& [item_id, item_index] : index_by_item) { | 419 | for (const auto& [item_id, item_index] : index_by_item) { |
| 428 | item_states.push_back(ItemState{.name = GetItemName(item_id), | 420 | item_states.push_back(ItemState{.name = GD_GetItemName(item_id), |
| 429 | .amount = inventory[item_id], | 421 | .amount = inventory[item_id], |
| 430 | .index = item_index}); | 422 | .index = item_index}); |
| 431 | } | 423 | } |
| @@ -511,8 +503,9 @@ struct APState { | |||
| 511 | : kSUNWARP_ACCESS_NORMAL; | 503 | : kSUNWARP_ACCESS_NORMAL; |
| 512 | sunwarp_shuffle = slot_data.contains("shuffle_sunwarps") && | 504 | sunwarp_shuffle = slot_data.contains("shuffle_sunwarps") && |
| 513 | slot_data["shuffle_sunwarps"].get<int>() == 1; | 505 | slot_data["shuffle_sunwarps"].get<int>() == 1; |
| 514 | postgame_shuffle = slot_data.contains("shuffle_postgame") && | 506 | postgame_shuffle = slot_data.contains("shuffle_postgame") |
| 515 | slot_data["shuffle_postgame"].get<int>() == 1; | 507 | ? (slot_data["shuffle_postgame"].get<int>() == 1) |
| 508 | : true; | ||
| 516 | 509 | ||
| 517 | if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { | 510 | if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { |
| 518 | painting_mapping.clear(); | 511 | painting_mapping.clear(); |
| @@ -603,11 +596,6 @@ struct APState { | |||
| 603 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, | 596 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, |
| 604 | (value.get<bool>() ? "true" : "false"))); | 597 | (value.get<bool>() ? "true" : "false"))); |
| 605 | 598 | ||
| 606 | if (key.find("Achievement|") != std::string::npos) { | ||
| 607 | state_update.achievements = true; | ||
| 608 | } else if (key.find("Hunt|") != std::string::npos) { | ||
| 609 | state_update.hunt_panels = true; | ||
| 610 | } | ||
| 611 | } else if (value.is_number()) { | 599 | } else if (value.is_number()) { |
| 612 | data_storage[key] = value.get<int>(); | 600 | data_storage[key] = value.get<int>(); |
| 613 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, | 601 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, |
| @@ -615,6 +603,21 @@ struct APState { | |||
| 615 | 603 | ||
| 616 | if (key == victory_data_storage_key) { | 604 | if (key == victory_data_storage_key) { |
| 617 | state_update.cleared_locations = true; | 605 | state_update.cleared_locations = true; |
| 606 | } else if (key.find("Panels_") != std::string::npos) { | ||
| 607 | int bitfield_num = | ||
| 608 | std::stoi(key.substr(data_storage_prefix.size() + 7)); | ||
| 609 | uint64_t bitfield_value = value.get<uint64_t>(); | ||
| 610 | for (int i = 0; i < PANEL_BITFIELD_LENGTH; i++) { | ||
| 611 | if ((bitfield_value & (1LL << i)) != 0) { | ||
| 612 | int solve_index = bitfield_num * PANEL_BITFIELD_LENGTH + i; | ||
| 613 | |||
| 614 | if (!solved_panels.test(solve_index)) { | ||
| 615 | state_update.panels.insert(solve_index); | ||
| 616 | } | ||
| 617 | |||
| 618 | solved_panels.set(solve_index); | ||
| 619 | } | ||
| 620 | } | ||
| 618 | } | 621 | } |
| 619 | } else if (value.is_object()) { | 622 | } else if (value.is_object()) { |
| 620 | if (key.ends_with("PlayerPos")) { | 623 | if (key.ends_with("PlayerPos")) { |
| @@ -715,16 +718,12 @@ bool AP_HasCheckedGameLocation(int location_id) { | |||
| 715 | return GetState().HasCheckedGameLocation(location_id); | 718 | return GetState().HasCheckedGameLocation(location_id); |
| 716 | } | 719 | } |
| 717 | 720 | ||
| 718 | bool AP_HasCheckedHuntPanel(int location_id) { | ||
| 719 | return GetState().HasCheckedHuntPanel(location_id); | ||
| 720 | } | ||
| 721 | |||
| 722 | bool AP_HasItem(int item_id, int quantity) { | 721 | bool AP_HasItem(int item_id, int quantity) { |
| 723 | return GetState().HasItem(item_id, quantity); | 722 | return GetState().HasItem(item_id, quantity); |
| 724 | } | 723 | } |
| 725 | 724 | ||
| 726 | std::string AP_GetItemName(int item_id) { | 725 | bool AP_HasItemSafe(int item_id, int quantity) { |
| 727 | return GetState().GetItemName(item_id); | 726 | return GetState().HasItemSafe(item_id, quantity); |
| 728 | } | 727 | } |
| 729 | 728 | ||
| 730 | DoorShuffleMode AP_GetDoorShuffleMode() { | 729 | DoorShuffleMode AP_GetDoorShuffleMode() { |
| @@ -830,10 +829,6 @@ VictoryCondition AP_GetVictoryCondition() { | |||
| 830 | return GetState().victory_condition; | 829 | return GetState().victory_condition; |
| 831 | } | 830 | } |
| 832 | 831 | ||
| 833 | bool AP_HasAchievement(const std::string& achievement_name) { | ||
| 834 | return GetState().HasAchievement(achievement_name); | ||
| 835 | } | ||
| 836 | |||
| 837 | bool AP_HasEarlyColorHallways() { | 832 | bool AP_HasEarlyColorHallways() { |
| 838 | std::lock_guard state_guard(GetState().state_mutex); | 833 | std::lock_guard state_guard(GetState().state_mutex); |
| 839 | 834 | ||
| @@ -883,3 +878,7 @@ std::optional<std::tuple<int, int>> AP_GetPlayerPosition() { | |||
| 883 | 878 | ||
| 884 | return GetState().player_pos; | 879 | return GetState().player_pos; |
| 885 | } | 880 | } |
| 881 | |||
| 882 | bool AP_IsPanelSolved(int solve_index) { | ||
| 883 | return GetState().IsPanelSolved(solve_index); | ||
| 884 | } | ||
| diff --git a/src/ap_state.h b/src/ap_state.h index 298df8c..a757d89 100644 --- a/src/ap_state.h +++ b/src/ap_state.h | |||
| @@ -57,16 +57,11 @@ 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); |
| 65 | 63 | ||
| 66 | // This doesn't lock the client mutex because it is ONLY to be called from | 64 | bool AP_HasItemSafe(int item_id, int quantity = 1); |
| 67 | // RecalculateReachability, which is only called from within a client callback | ||
| 68 | // anyway. | ||
| 69 | std::string AP_GetItemName(int item_id); | ||
| 70 | 65 | ||
| 71 | DoorShuffleMode AP_GetDoorShuffleMode(); | 66 | DoorShuffleMode AP_GetDoorShuffleMode(); |
| 72 | 67 | ||
| @@ -98,8 +93,6 @@ PanelShuffleMode AP_GetPanelShuffleMode(); | |||
| 98 | 93 | ||
| 99 | VictoryCondition AP_GetVictoryCondition(); | 94 | VictoryCondition AP_GetVictoryCondition(); |
| 100 | 95 | ||
| 101 | bool AP_HasAchievement(const std::string& achievement_name); | ||
| 102 | |||
| 103 | bool AP_HasEarlyColorHallways(); | 96 | bool AP_HasEarlyColorHallways(); |
| 104 | 97 | ||
| 105 | bool AP_IsPilgrimageEnabled(); | 98 | bool AP_IsPilgrimageEnabled(); |
| @@ -120,4 +113,6 @@ bool AP_HasReachedGoal(); | |||
| 120 | 113 | ||
| 121 | std::optional<std::tuple<int, int>> AP_GetPlayerPosition(); | 114 | std::optional<std::tuple<int, int>> AP_GetPlayerPosition(); |
| 122 | 115 | ||
| 116 | bool AP_IsPanelSolved(int solve_index); | ||
| 117 | |||
| 123 | #endif /* end of include guard: AP_STATE_H_664A4180 */ | 118 | #endif /* end of include guard: AP_STATE_H_664A4180 */ |
| diff --git a/src/area_popup.cpp b/src/area_popup.cpp index a8e6004..c95e492 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp | |||
| @@ -50,21 +50,15 @@ void AreaPopup::ResetIndicators() { | |||
| 50 | for (int section_id = 0; section_id < map_area.locations.size(); | 50 | for (int section_id = 0; section_id < map_area.locations.size(); |
| 51 | section_id++) { | 51 | section_id++) { |
| 52 | const Location& location = map_area.locations.at(section_id); | 52 | const Location& location = map_area.locations.at(section_id); |
| 53 | if (IsLocationPostgame(location.ap_location_id)) { | 53 | if ((!AP_IsLocationVisible(location.classification) || |
| 54 | IsLocationPostgame(location.ap_location_id)) && | ||
| 55 | !(location.hunt && | ||
| 56 | GetTrackerConfig().visible_panels == TrackerConfig::kHUNT_PANELS) && | ||
| 57 | !(location.single_panel && | ||
| 58 | GetTrackerConfig().visible_panels == TrackerConfig::kALL_PANELS)) { | ||
| 54 | continue; | 59 | continue; |
| 55 | } | 60 | } |
| 56 | 61 | ||
| 57 | if (tracker_panel->IsPanelsMode()) { | ||
| 58 | if (!location.single_panel) { | ||
| 59 | continue; | ||
| 60 | } | ||
| 61 | } else { | ||
| 62 | if (!AP_IsLocationVisible(location.classification) && | ||
| 63 | !(location.hunt && GetTrackerConfig().show_hunt_panels)) { | ||
| 64 | continue; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | indicators_.emplace_back(section_id, kLOCATION, acc_height); | 62 | indicators_.emplace_back(section_id, kLOCATION, acc_height); |
| 69 | 63 | ||
| 70 | wxSize item_extent = mem_dc.GetTextExtent(location.name); | 64 | wxSize item_extent = mem_dc.GetTextExtent(location.name); |
| @@ -77,7 +71,7 @@ void AreaPopup::ResetIndicators() { | |||
| 77 | } | 71 | } |
| 78 | } | 72 | } |
| 79 | 73 | ||
| 80 | if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { | 74 | if (AP_IsPaintingShuffle()) { |
| 81 | for (int painting_id : map_area.paintings) { | 75 | for (int painting_id : map_area.paintings) { |
| 82 | if (IsPaintingPostgame(painting_id)) { | 76 | if (IsPaintingPostgame(painting_id)) { |
| 83 | continue; | 77 | continue; |
| @@ -135,17 +129,11 @@ void AreaPopup::UpdateIndicators() { | |||
| 135 | bool checked = false; | 129 | bool checked = false; |
| 136 | if (IsLocationWinCondition(location)) { | 130 | if (IsLocationWinCondition(location)) { |
| 137 | checked = AP_HasReachedGoal(); | 131 | checked = AP_HasReachedGoal(); |
| 138 | } else if (tracker_panel->IsPanelsMode()) { | ||
| 139 | const Panel& panel = GD_GetPanel(*location.single_panel); | ||
| 140 | if (panel.non_counting) { | ||
| 141 | checked = AP_HasCheckedGameLocation(location.ap_location_id); | ||
| 142 | } else { | ||
| 143 | checked = tracker_panel->GetSolvedPanels().contains(panel.nodepath); | ||
| 144 | } | ||
| 145 | } else { | 132 | } else { |
| 146 | checked = AP_HasCheckedGameLocation(location.ap_location_id) || | 133 | checked = AP_HasCheckedGameLocation(location.ap_location_id) || |
| 147 | (location.hunt && | 134 | (location.single_panel && |
| 148 | AP_HasCheckedHuntPanel(location.ap_location_id)); | 135 | AP_IsPanelSolved( |
| 136 | GD_GetPanel(*location.single_panel).solve_index)); | ||
| 149 | } | 137 | } |
| 150 | 138 | ||
| 151 | const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_; | 139 | const wxBitmap* eye_ptr = checked ? checked_eye_ : unchecked_eye_; |
| diff --git a/src/game_data.cpp b/src/game_data.cpp index a4a441d..588ffc8 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp | |||
| @@ -12,32 +12,6 @@ | |||
| 12 | 12 | ||
| 13 | namespace { | 13 | namespace { |
| 14 | 14 | ||
| 15 | LingoColor GetColorForString(const std::string &str) { | ||
| 16 | if (str == "black") { | ||
| 17 | return LingoColor::kBlack; | ||
| 18 | } else if (str == "red") { | ||
| 19 | return LingoColor::kRed; | ||
| 20 | } else if (str == "blue") { | ||
| 21 | return LingoColor::kBlue; | ||
| 22 | } else if (str == "yellow") { | ||
| 23 | return LingoColor::kYellow; | ||
| 24 | } else if (str == "orange") { | ||
| 25 | return LingoColor::kOrange; | ||
| 26 | } else if (str == "green") { | ||
| 27 | return LingoColor::kGreen; | ||
| 28 | } else if (str == "gray") { | ||
| 29 | return LingoColor::kGray; | ||
| 30 | } else if (str == "brown") { | ||
| 31 | return LingoColor::kBrown; | ||
| 32 | } else if (str == "purple") { | ||
| 33 | return LingoColor::kPurple; | ||
| 34 | } else { | ||
| 35 | TrackerLog(fmt::format("Invalid color: {}", str)); | ||
| 36 | |||
| 37 | return LingoColor::kNone; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | struct GameData { | 15 | struct GameData { |
| 42 | std::vector<Room> rooms_; | 16 | std::vector<Room> rooms_; |
| 43 | std::vector<Door> doors_; | 17 | std::vector<Door> doors_; |
| @@ -55,10 +29,10 @@ struct GameData { | |||
| 55 | std::map<std::string, int> painting_by_id_; | 29 | std::map<std::string, int> painting_by_id_; |
| 56 | 30 | ||
| 57 | std::vector<int> door_definition_order_; | 31 | std::vector<int> door_definition_order_; |
| 58 | std::vector<int> room_definition_order_; | ||
| 59 | 32 | ||
| 60 | std::map<std::string, int> room_by_painting_; | 33 | std::map<std::string, int> room_by_painting_; |
| 61 | std::map<int, int> room_by_sunwarp_; | 34 | std::map<int, int> room_by_sunwarp_; |
| 35 | std::map<int, int> panel_by_solve_index_; | ||
| 62 | 36 | ||
| 63 | std::vector<int> achievement_panels_; | 37 | std::vector<int> achievement_panels_; |
| 64 | 38 | ||
| @@ -69,6 +43,8 @@ struct GameData { | |||
| 69 | std::map<std::string, int> subway_item_by_painting_; | 43 | std::map<std::string, int> subway_item_by_painting_; |
| 70 | std::map<SubwaySunwarp, int> subway_item_by_sunwarp_; | 44 | std::map<SubwaySunwarp, int> subway_item_by_sunwarp_; |
| 71 | 45 | ||
| 46 | std::map<int, std::string> item_by_ap_id_; | ||
| 47 | |||
| 72 | bool loaded_area_data_ = false; | 48 | bool loaded_area_data_ = false; |
| 73 | std::set<std::string> malconfigured_areas_; | 49 | std::set<std::string> malconfigured_areas_; |
| 74 | 50 | ||
| @@ -84,7 +60,7 @@ struct GameData { | |||
| 84 | ids_config["special_items"][color_name]) { | 60 | ids_config["special_items"][color_name]) { |
| 85 | std::string input_name = color_name; | 61 | std::string input_name = color_name; |
| 86 | input_name[0] = std::tolower(input_name[0]); | 62 | input_name[0] = std::tolower(input_name[0]); |
| 87 | ap_id_by_color_[GetColorForString(input_name)] = | 63 | ap_id_by_color_[GetLingoColorForString(input_name)] = |
| 88 | ids_config["special_items"][color_name].as<int>(); | 64 | ids_config["special_items"][color_name].as<int>(); |
| 89 | } else { | 65 | } else { |
| 90 | TrackerLog(fmt::format("Missing AP item ID for color {}", color_name)); | 66 | TrackerLog(fmt::format("Missing AP item ID for color {}", color_name)); |
| @@ -101,11 +77,20 @@ struct GameData { | |||
| 101 | init_color_id("Brown"); | 77 | init_color_id("Brown"); |
| 102 | init_color_id("Gray"); | 78 | init_color_id("Gray"); |
| 103 | 79 | ||
| 80 | if (ids_config["special_items"]) { | ||
| 81 | for (const auto& special_item_it : ids_config["special_items"]) | ||
| 82 | { | ||
| 83 | item_by_ap_id_[special_item_it.second.as<int>()] = | ||
| 84 | special_item_it.first.as<std::string>(); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 104 | rooms_.reserve(lingo_config.size() * 2); | 88 | rooms_.reserve(lingo_config.size() * 2); |
| 105 | 89 | ||
| 90 | std::vector<int> panel_location_ids; | ||
| 91 | |||
| 106 | for (const auto &room_it : lingo_config) { | 92 | for (const auto &room_it : lingo_config) { |
| 107 | int room_id = AddOrGetRoom(room_it.first.as<std::string>()); | 93 | int room_id = AddOrGetRoom(room_it.first.as<std::string>()); |
| 108 | room_definition_order_.push_back(room_id); | ||
| 109 | 94 | ||
| 110 | for (const auto &entrance_it : room_it.second["entrances"]) { | 95 | for (const auto &entrance_it : room_it.second["entrances"]) { |
| 111 | int from_room_id = AddOrGetRoom(entrance_it.first.as<std::string>()); | 96 | int from_room_id = AddOrGetRoom(entrance_it.first.as<std::string>()); |
| @@ -142,6 +127,10 @@ struct GameData { | |||
| 142 | exit_obj.type = EntranceType::kCrossroadsRoofAccess; | 127 | exit_obj.type = EntranceType::kCrossroadsRoofAccess; |
| 143 | } | 128 | } |
| 144 | 129 | ||
| 130 | if (option["static_painting"] && option["static_painting"].as<bool>()) { | ||
| 131 | exit_obj.type = EntranceType::kStaticPainting; | ||
| 132 | } | ||
| 133 | |||
| 145 | rooms_[from_room_id].exits.push_back(exit_obj); | 134 | rooms_[from_room_id].exits.push_back(exit_obj); |
| 146 | }; | 135 | }; |
| 147 | 136 | ||
| @@ -181,12 +170,12 @@ struct GameData { | |||
| 181 | 170 | ||
| 182 | if (panel_it.second["colors"]) { | 171 | if (panel_it.second["colors"]) { |
| 183 | if (panel_it.second["colors"].IsScalar()) { | 172 | if (panel_it.second["colors"].IsScalar()) { |
| 184 | panels_[panel_id].colors.push_back(GetColorForString( | 173 | panels_[panel_id].colors.push_back(GetLingoColorForString( |
| 185 | panel_it.second["colors"].as<std::string>())); | 174 | panel_it.second["colors"].as<std::string>())); |
| 186 | } else { | 175 | } else { |
| 187 | for (const auto &color_node : panel_it.second["colors"]) { | 176 | for (const auto &color_node : panel_it.second["colors"]) { |
| 188 | panels_[panel_id].colors.push_back( | 177 | panels_[panel_id].colors.push_back( |
| 189 | GetColorForString(color_node.as<std::string>())); | 178 | GetLingoColorForString(color_node.as<std::string>())); |
| 190 | } | 179 | } |
| 191 | } | 180 | } |
| 192 | } | 181 | } |
| @@ -292,10 +281,11 @@ struct GameData { | |||
| 292 | ids_config["panels"][rooms_[room_id].name] && | 281 | ids_config["panels"][rooms_[room_id].name] && |
| 293 | ids_config["panels"][rooms_[room_id].name] | 282 | ids_config["panels"][rooms_[room_id].name] |
| 294 | [panels_[panel_id].name]) { | 283 | [panels_[panel_id].name]) { |
| 295 | panels_[panel_id].ap_location_id = | 284 | int location_id = ids_config["panels"][rooms_[room_id].name] |
| 296 | ids_config["panels"][rooms_[room_id].name] | 285 | [panels_[panel_id].name] |
| 297 | [panels_[panel_id].name] | 286 | .as<int>(); |
| 298 | .as<int>(); | 287 | panels_[panel_id].ap_location_id = location_id; |
| 288 | panel_location_ids.push_back(location_id); | ||
| 299 | } else { | 289 | } else { |
| 300 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", | 290 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", |
| 301 | rooms_[room_id].name, | 291 | rooms_[room_id].name, |
| @@ -361,6 +351,9 @@ struct GameData { | |||
| 361 | ids_config["doors"][rooms_[room_id].name] | 351 | ids_config["doors"][rooms_[room_id].name] |
| 362 | [doors_[door_id].name]["item"] | 352 | [doors_[door_id].name]["item"] |
| 363 | .as<int>(); | 353 | .as<int>(); |
| 354 | |||
| 355 | item_by_ap_id_[doors_[door_id].ap_item_id] = | ||
| 356 | doors_[door_id].item_name; | ||
| 364 | } else { | 357 | } else { |
| 365 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", | 358 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", |
| 366 | rooms_[room_id].name, | 359 | rooms_[room_id].name, |
| @@ -377,6 +370,9 @@ struct GameData { | |||
| 377 | doors_[door_id].group_ap_item_id = | 370 | doors_[door_id].group_ap_item_id = |
| 378 | ids_config["door_groups"][doors_[door_id].group_name] | 371 | ids_config["door_groups"][doors_[door_id].group_name] |
| 379 | .as<int>(); | 372 | .as<int>(); |
| 373 | |||
| 374 | item_by_ap_id_[doors_[door_id].group_ap_item_id] = | ||
| 375 | doors_[door_id].group_name; | ||
| 380 | } else { | 376 | } else { |
| 381 | TrackerLog(fmt::format("Missing AP item ID for door group {}", | 377 | TrackerLog(fmt::format("Missing AP item ID for door group {}", |
| 382 | doors_[door_id].group_name)); | 378 | doors_[door_id].group_name)); |
| @@ -440,21 +436,50 @@ struct GameData { | |||
| 440 | int panel_door_id = | 436 | int panel_door_id = |
| 441 | AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name); | 437 | AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name); |
| 442 | 438 | ||
| 439 | std::map<std::string, std::vector<std::string>> panel_per_room; | ||
| 440 | int num_panels = 0; | ||
| 443 | for (const auto &panel_node : panel_door_it.second["panels"]) { | 441 | for (const auto &panel_node : panel_door_it.second["panels"]) { |
| 442 | num_panels++; | ||
| 443 | |||
| 444 | int panel_id = -1; | 444 | int panel_id = -1; |
| 445 | 445 | ||
| 446 | if (panel_node.IsScalar()) { | 446 | if (panel_node.IsScalar()) { |
| 447 | panel_id = AddOrGetPanel(rooms_[room_id].name, | 447 | panel_id = AddOrGetPanel(rooms_[room_id].name, |
| 448 | panel_node.as<std::string>()); | 448 | panel_node.as<std::string>()); |
| 449 | |||
| 450 | panel_per_room[rooms_[room_id].name].push_back( | ||
| 451 | panel_node.as<std::string>()); | ||
| 449 | } else { | 452 | } else { |
| 450 | panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(), | 453 | panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(), |
| 451 | panel_node["panel"].as<std::string>()); | 454 | panel_node["panel"].as<std::string>()); |
| 455 | |||
| 456 | panel_per_room[panel_node["room"].as<std::string>()].push_back( | ||
| 457 | panel_node["panel"].as<std::string>()); | ||
| 452 | } | 458 | } |
| 453 | 459 | ||
| 454 | Panel &panel = panels_[panel_id]; | 460 | Panel &panel = panels_[panel_id]; |
| 455 | panel.panel_door = panel_door_id; | 461 | panel.panel_door = panel_door_id; |
| 456 | } | 462 | } |
| 457 | 463 | ||
| 464 | if (panel_door_it.second["item_name"]) { | ||
| 465 | panel_doors_[panel_door_id].item_name = | ||
| 466 | panel_door_it.second["item_name"].as<std::string>(); | ||
| 467 | } else { | ||
| 468 | std::vector<std::string> room_strs; | ||
| 469 | for (const auto &[room_str, panels_str] : panel_per_room) { | ||
| 470 | room_strs.push_back(fmt::format( | ||
| 471 | "{} - {}", room_str, hatkirby::implode(panels_str, ", "))); | ||
| 472 | } | ||
| 473 | |||
| 474 | if (num_panels == 1) { | ||
| 475 | panel_doors_[panel_door_id].item_name = | ||
| 476 | fmt::format("{} (Panel)", room_strs[0]); | ||
| 477 | } else { | ||
| 478 | panel_doors_[panel_door_id].item_name = fmt::format( | ||
| 479 | "{} (Panels)", hatkirby::implode(room_strs, " and ")); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 458 | if (ids_config["panel_doors"] && | 483 | if (ids_config["panel_doors"] && |
| 459 | ids_config["panel_doors"][rooms_[room_id].name] && | 484 | ids_config["panel_doors"][rooms_[room_id].name] && |
| 460 | ids_config["panel_doors"][rooms_[room_id].name] | 485 | ids_config["panel_doors"][rooms_[room_id].name] |
| @@ -462,6 +487,9 @@ struct GameData { | |||
| 462 | panel_doors_[panel_door_id].ap_item_id = | 487 | panel_doors_[panel_door_id].ap_item_id = |
| 463 | ids_config["panel_doors"][rooms_[room_id].name][panel_door_name] | 488 | ids_config["panel_doors"][rooms_[room_id].name][panel_door_name] |
| 464 | .as<int>(); | 489 | .as<int>(); |
| 490 | |||
| 491 | item_by_ap_id_[panel_doors_[panel_door_id].ap_item_id] = | ||
| 492 | panel_doors_[panel_door_id].item_name; | ||
| 465 | } else { | 493 | } else { |
| 466 | TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}", | 494 | TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}", |
| 467 | rooms_[room_id].name, panel_door_name)); | 495 | rooms_[room_id].name, panel_door_name)); |
| @@ -475,6 +503,9 @@ struct GameData { | |||
| 475 | ids_config["panel_groups"][panel_group]) { | 503 | ids_config["panel_groups"][panel_group]) { |
| 476 | panel_doors_[panel_door_id].group_ap_item_id = | 504 | panel_doors_[panel_door_id].group_ap_item_id = |
| 477 | ids_config["panel_groups"][panel_group].as<int>(); | 505 | ids_config["panel_groups"][panel_group].as<int>(); |
| 506 | |||
| 507 | item_by_ap_id_[panel_doors_[panel_door_id].group_ap_item_id] = | ||
| 508 | panel_group; | ||
| 478 | } else { | 509 | } else { |
| 479 | TrackerLog(fmt::format( | 510 | TrackerLog(fmt::format( |
| 480 | "Missing AP item ID for panel door group {}", panel_group)); | 511 | "Missing AP item ID for panel door group {}", panel_group)); |
| @@ -538,6 +569,8 @@ struct GameData { | |||
| 538 | ids_config["progression"][progressive_item_name]) { | 569 | ids_config["progression"][progressive_item_name]) { |
| 539 | progressive_item_id = | 570 | progressive_item_id = |
| 540 | ids_config["progression"][progressive_item_name].as<int>(); | 571 | ids_config["progression"][progressive_item_name].as<int>(); |
| 572 | |||
| 573 | item_by_ap_id_[progressive_item_id] = progressive_item_name; | ||
| 541 | } else { | 574 | } else { |
| 542 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", | 575 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", |
| 543 | progressive_item_name)); | 576 | progressive_item_name)); |
| @@ -589,6 +622,21 @@ struct GameData { | |||
| 589 | } | 622 | } |
| 590 | } | 623 | } |
| 591 | 624 | ||
| 625 | // Determine the panel solve indices from the sorted location IDs. | ||
| 626 | std::sort(panel_location_ids.begin(), panel_location_ids.end()); | ||
| 627 | |||
| 628 | std::map<int, int> solve_index_by_location_id; | ||
| 629 | for (int i = 0; i < panel_location_ids.size(); i++) { | ||
| 630 | solve_index_by_location_id[panel_location_ids[i]] = i; | ||
| 631 | } | ||
| 632 | |||
| 633 | for (Panel &panel : panels_) { | ||
| 634 | if (panel.ap_location_id != -1) { | ||
| 635 | panel.solve_index = solve_index_by_location_id[panel.ap_location_id]; | ||
| 636 | panel_by_solve_index_[panel.solve_index] = panel.id; | ||
| 637 | } | ||
| 638 | } | ||
| 639 | |||
| 592 | map_areas_.reserve(areas_config.size()); | 640 | map_areas_.reserve(areas_config.size()); |
| 593 | 641 | ||
| 594 | std::map<std::string, int> fold_areas; | 642 | std::map<std::string, int> fold_areas; |
| @@ -734,31 +782,6 @@ struct GameData { | |||
| 734 | } | 782 | } |
| 735 | } | 783 | } |
| 736 | 784 | ||
| 737 | // As a workaround for a generator bug in 0.5.1, we are going to remove the | ||
| 738 | // panel door requirement on panels that are defined earlier in the file than | ||
| 739 | // the panel door is. This results in logic that matches the generator, even | ||
| 740 | // if it is not true to how the game should work. This will be reverted once | ||
| 741 | // the logic bug is fixed and released. | ||
| 742 | // See: https://github.com/ArchipelagoMW/Archipelago/pull/4342 | ||
| 743 | for (Panel& panel : panels_) { | ||
| 744 | if (panel.panel_door == -1) { | ||
| 745 | continue; | ||
| 746 | } | ||
| 747 | const PanelDoor &panel_door = panel_doors_[panel.panel_door]; | ||
| 748 | for (int room_id : room_definition_order_) { | ||
| 749 | if (room_id == panel_door.room) { | ||
| 750 | // The panel door was defined first (or at the same time as the panel), | ||
| 751 | // so we're good. | ||
| 752 | break; | ||
| 753 | } else if (room_id == panel.room) { | ||
| 754 | // The panel was defined first, so we have to pretend the panel door is | ||
| 755 | // not required for this panel. | ||
| 756 | panel.panel_door = -1; | ||
| 757 | break; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | } | ||
| 761 | |||
| 762 | // Report errors. | 785 | // Report errors. |
| 763 | for (const std::string &area : malconfigured_areas_) { | 786 | for (const std::string &area : malconfigured_areas_) { |
| 764 | TrackerLog(fmt::format("Area data not found for: {}", area)); | 787 | TrackerLog(fmt::format("Area data not found for: {}", area)); |
| @@ -891,7 +914,7 @@ struct GameData { | |||
| 891 | if (!panel_doors_by_id_.count(full_name)) { | 914 | if (!panel_doors_by_id_.count(full_name)) { |
| 892 | int panel_door_id = panel_doors_.size(); | 915 | int panel_door_id = panel_doors_.size(); |
| 893 | panel_doors_by_id_[full_name] = panel_door_id; | 916 | panel_doors_by_id_[full_name] = panel_door_id; |
| 894 | panel_doors_.push_back({.room = AddOrGetRoom(room)}); | 917 | panel_doors_.push_back({}); |
| 895 | } | 918 | } |
| 896 | 919 | ||
| 897 | return panel_doors_by_id_[full_name]; | 920 | return panel_doors_by_id_[full_name]; |
| @@ -964,6 +987,10 @@ const Panel &GD_GetPanel(int panel_id) { | |||
| 964 | return GetState().panels_.at(panel_id); | 987 | return GetState().panels_.at(panel_id); |
| 965 | } | 988 | } |
| 966 | 989 | ||
| 990 | int GD_GetPanelBySolveIndex(int solve_index) { | ||
| 991 | return GetState().panel_by_solve_index_.at(solve_index); | ||
| 992 | } | ||
| 993 | |||
| 967 | const std::vector<PaintingExit> &GD_GetPaintings() { | 994 | const std::vector<PaintingExit> &GD_GetPaintings() { |
| 968 | return GetState().paintings_; | 995 | return GetState().paintings_; |
| 969 | } | 996 | } |
| @@ -1010,3 +1037,38 @@ std::optional<int> GD_GetSubwayItemForPainting(const std::string &painting_id) { | |||
| 1010 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { | 1037 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { |
| 1011 | return GetState().subway_item_by_sunwarp_.at(sunwarp); | 1038 | return GetState().subway_item_by_sunwarp_.at(sunwarp); |
| 1012 | } | 1039 | } |
| 1040 | |||
| 1041 | std::string GD_GetItemName(int id) { | ||
| 1042 | auto it = GetState().item_by_ap_id_.find(id); | ||
| 1043 | if (it != GetState().item_by_ap_id_.end()) { | ||
| 1044 | return it->second; | ||
| 1045 | } else { | ||
| 1046 | return "Unknown"; | ||
| 1047 | } | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | LingoColor GetLingoColorForString(const std::string &str) { | ||
| 1051 | if (str == "black") { | ||
| 1052 | return LingoColor::kBlack; | ||
| 1053 | } else if (str == "red") { | ||
| 1054 | return LingoColor::kRed; | ||
| 1055 | } else if (str == "blue") { | ||
| 1056 | return LingoColor::kBlue; | ||
| 1057 | } else if (str == "yellow") { | ||
| 1058 | return LingoColor::kYellow; | ||
| 1059 | } else if (str == "orange") { | ||
| 1060 | return LingoColor::kOrange; | ||
| 1061 | } else if (str == "green") { | ||
| 1062 | return LingoColor::kGreen; | ||
| 1063 | } else if (str == "gray") { | ||
| 1064 | return LingoColor::kGray; | ||
| 1065 | } else if (str == "brown") { | ||
| 1066 | return LingoColor::kBrown; | ||
| 1067 | } else if (str == "purple") { | ||
| 1068 | return LingoColor::kPurple; | ||
| 1069 | } else { | ||
| 1070 | TrackerLog(fmt::format("Invalid color: {}", str)); | ||
| 1071 | |||
| 1072 | return LingoColor::kNone; | ||
| 1073 | } | ||
| 1074 | } | ||
| diff --git a/src/game_data.h b/src/game_data.h index 24760de..8d3db4b 100644 --- a/src/game_data.h +++ b/src/game_data.h | |||
| @@ -31,6 +31,7 @@ enum class EntranceType { | |||
| 31 | kWarp, | 31 | kWarp, |
| 32 | kPilgrimage, | 32 | kPilgrimage, |
| 33 | kCrossroadsRoofAccess, | 33 | kCrossroadsRoofAccess, |
| 34 | kStaticPainting, | ||
| 34 | }; | 35 | }; |
| 35 | 36 | ||
| 36 | enum class DoorType { | 37 | enum class DoorType { |
| @@ -57,6 +58,7 @@ struct Panel { | |||
| 57 | int ap_location_id = -1; | 58 | int ap_location_id = -1; |
| 58 | bool hunt = false; | 59 | bool hunt = false; |
| 59 | int panel_door = -1; | 60 | int panel_door = -1; |
| 61 | int solve_index = -1; | ||
| 60 | }; | 62 | }; |
| 61 | 63 | ||
| 62 | struct ProgressiveRequirement { | 64 | struct ProgressiveRequirement { |
| @@ -85,10 +87,10 @@ struct Door { | |||
| 85 | }; | 87 | }; |
| 86 | 88 | ||
| 87 | struct PanelDoor { | 89 | struct PanelDoor { |
| 88 | int room; | ||
| 89 | int ap_item_id = -1; | 90 | int ap_item_id = -1; |
| 90 | int group_ap_item_id = -1; | 91 | int group_ap_item_id = -1; |
| 91 | std::vector<ProgressiveRequirement> progressives; | 92 | std::vector<ProgressiveRequirement> progressives; |
| 93 | std::string item_name; | ||
| 92 | }; | 94 | }; |
| 93 | 95 | ||
| 94 | struct Exit { | 96 | struct Exit { |
| @@ -176,6 +178,7 @@ const std::vector<Door>& GD_GetDoors(); | |||
| 176 | const Door& GD_GetDoor(int door_id); | 178 | const Door& GD_GetDoor(int door_id); |
| 177 | int GD_GetDoorByName(const std::string& name); | 179 | int GD_GetDoorByName(const std::string& name); |
| 178 | const Panel& GD_GetPanel(int panel_id); | 180 | const Panel& GD_GetPanel(int panel_id); |
| 181 | int GD_GetPanelBySolveIndex(int solve_index); | ||
| 179 | const PanelDoor& GD_GetPanelDoor(int panel_door_id); | 182 | const PanelDoor& GD_GetPanelDoor(int panel_door_id); |
| 180 | const std::vector<PaintingExit>& GD_GetPaintings(); | 183 | const std::vector<PaintingExit>& GD_GetPaintings(); |
| 181 | const PaintingExit& GD_GetPaintingExit(int painting_id); | 184 | const PaintingExit& GD_GetPaintingExit(int painting_id); |
| @@ -188,5 +191,8 @@ const std::vector<SubwayItem>& GD_GetSubwayItems(); | |||
| 188 | const SubwayItem& GD_GetSubwayItem(int id); | 191 | const SubwayItem& GD_GetSubwayItem(int id); |
| 189 | std::optional<int> GD_GetSubwayItemForPainting(const std::string& painting_id); | 192 | std::optional<int> GD_GetSubwayItemForPainting(const std::string& painting_id); |
| 190 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp& sunwarp); | 193 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp& sunwarp); |
| 194 | std::string GD_GetItemName(int id); | ||
| 195 | |||
| 196 | LingoColor GetLingoColorForString(const std::string& str); | ||
| 191 | 197 | ||
| 192 | #endif /* end of include guard: GAME_DATA_H_9C42AC51 */ | 198 | #endif /* end of include guard: GAME_DATA_H_9C42AC51 */ |
| diff --git a/src/godot_variant.cpp b/src/godot_variant.cpp deleted file mode 100644 index 152b9ef..0000000 --- a/src/godot_variant.cpp +++ /dev/null | |||
| @@ -1,84 +0,0 @@ | |||
| 1 | // Godot save decoder algorithm by Chris Souvey. | ||
| 2 | |||
| 3 | #include "godot_variant.h" | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <charconv> | ||
| 7 | #include <cstddef> | ||
| 8 | #include <cstdint> | ||
| 9 | #include <fstream> | ||
| 10 | #include <string> | ||
| 11 | #include <tuple> | ||
| 12 | #include <variant> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | namespace { | ||
| 16 | |||
| 17 | uint16_t ReadUint16(std::basic_istream<char>& stream) { | ||
| 18 | uint16_t result; | ||
| 19 | stream.read(reinterpret_cast<char*>(&result), 2); | ||
| 20 | return result; | ||
| 21 | } | ||
| 22 | |||
| 23 | uint32_t ReadUint32(std::basic_istream<char>& stream) { | ||
| 24 | uint32_t result; | ||
| 25 | stream.read(reinterpret_cast<char*>(&result), 4); | ||
| 26 | return result; | ||
| 27 | } | ||
| 28 | |||
| 29 | GodotVariant ParseVariant(std::basic_istream<char>& stream) { | ||
| 30 | uint16_t type = ReadUint16(stream); | ||
| 31 | stream.ignore(2); | ||
| 32 | |||
| 33 | switch (type) { | ||
| 34 | case 1: { | ||
| 35 | // bool | ||
| 36 | bool boolval = (ReadUint32(stream) == 1); | ||
| 37 | return {boolval}; | ||
| 38 | } | ||
| 39 | case 15: { | ||
| 40 | // nodepath | ||
| 41 | uint32_t name_length = ReadUint32(stream) & 0x7fffffff; | ||
| 42 | uint32_t subname_length = ReadUint32(stream) & 0x7fffffff; | ||
| 43 | uint32_t flags = ReadUint32(stream); | ||
| 44 | |||
| 45 | std::vector<std::string> result; | ||
| 46 | for (size_t i = 0; i < name_length + subname_length; i++) { | ||
| 47 | uint32_t char_length = ReadUint32(stream); | ||
| 48 | uint32_t padded_length = (char_length % 4 == 0) | ||
| 49 | ? char_length | ||
| 50 | : (char_length + 4 - (char_length % 4)); | ||
| 51 | std::vector<char> next_bytes(padded_length); | ||
| 52 | stream.read(next_bytes.data(), padded_length); | ||
| 53 | std::string next_piece; | ||
| 54 | std::copy(next_bytes.begin(), | ||
| 55 | std::next(next_bytes.begin(), char_length), | ||
| 56 | std::back_inserter(next_piece)); | ||
| 57 | result.push_back(next_piece); | ||
| 58 | } | ||
| 59 | |||
| 60 | return {result}; | ||
| 61 | } | ||
| 62 | case 19: { | ||
| 63 | // array | ||
| 64 | uint32_t length = ReadUint32(stream) & 0x7fffffff; | ||
| 65 | std::vector<GodotVariant> result; | ||
| 66 | for (size_t i = 0; i < length; i++) { | ||
| 67 | result.push_back(ParseVariant(stream)); | ||
| 68 | } | ||
| 69 | return {result}; | ||
| 70 | } | ||
| 71 | default: { | ||
| 72 | // eh | ||
| 73 | return {std::monostate{}}; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | } // namespace | ||
| 79 | |||
| 80 | GodotVariant ParseGodotFile(std::string filename) { | ||
| 81 | std::ifstream file_stream(filename, std::ios_base::binary); | ||
| 82 | file_stream.ignore(4); | ||
| 83 | return ParseVariant(file_stream); | ||
| 84 | } | ||
| diff --git a/src/godot_variant.h b/src/godot_variant.h deleted file mode 100644 index 620e569..0000000 --- a/src/godot_variant.h +++ /dev/null | |||
| @@ -1,28 +0,0 @@ | |||
| 1 | #ifndef GODOT_VARIANT_H_ED7F2EB6 | ||
| 2 | #define GODOT_VARIANT_H_ED7F2EB6 | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | #include <variant> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 8 | struct GodotVariant { | ||
| 9 | using value_type = std::variant<std::monostate, bool, std::vector<std::string>, std::vector<GodotVariant>>; | ||
| 10 | |||
| 11 | value_type value; | ||
| 12 | |||
| 13 | GodotVariant(value_type v) : value(v) {} | ||
| 14 | |||
| 15 | bool AsBool() const { return std::get<bool>(value); } | ||
| 16 | |||
| 17 | const std::vector<std::string>& AsNodePath() const { | ||
| 18 | return std::get<std::vector<std::string>>(value); | ||
| 19 | } | ||
| 20 | |||
| 21 | const std::vector<GodotVariant>& AsArray() const { | ||
| 22 | return std::get<std::vector<GodotVariant>>(value); | ||
| 23 | } | ||
| 24 | }; | ||
| 25 | |||
| 26 | GodotVariant ParseGodotFile(std::string filename); | ||
| 27 | |||
| 28 | #endif /* end of include guard: GODOT_VARIANT_H_ED7F2EB6 */ | ||
| diff --git a/src/ipc_state.cpp b/src/ipc_state.cpp index a99fa89..6e2a440 100644 --- a/src/ipc_state.cpp +++ b/src/ipc_state.cpp | |||
| @@ -39,7 +39,6 @@ struct IPCState { | |||
| 39 | std::string game_ap_user; | 39 | std::string game_ap_user; |
| 40 | 40 | ||
| 41 | std::optional<std::tuple<int, int>> player_position; | 41 | std::optional<std::tuple<int, int>> player_position; |
| 42 | std::set<std::string> solved_panels; | ||
| 43 | 42 | ||
| 44 | // Thread state | 43 | // Thread state |
| 45 | std::unique_ptr<wswrap::WS> ws; | 44 | std::unique_ptr<wswrap::WS> ws; |
| @@ -103,12 +102,6 @@ struct IPCState { | |||
| 103 | return player_position; | 102 | return player_position; |
| 104 | } | 103 | } |
| 105 | 104 | ||
| 106 | std::set<std::string> GetSolvedPanels() { | ||
| 107 | std::lock_guard state_guard(state_mutex); | ||
| 108 | |||
| 109 | return solved_panels; | ||
| 110 | } | ||
| 111 | |||
| 112 | private: | 105 | private: |
| 113 | void Thread() { | 106 | void Thread() { |
| 114 | for (;;) { | 107 | for (;;) { |
| @@ -134,7 +127,6 @@ struct IPCState { | |||
| 134 | game_ap_user.clear(); | 127 | game_ap_user.clear(); |
| 135 | 128 | ||
| 136 | player_position = std::nullopt; | 129 | player_position = std::nullopt; |
| 137 | solved_panels.clear(); | ||
| 138 | 130 | ||
| 139 | if (address.empty()) { | 131 | if (address.empty()) { |
| 140 | initialized = false; | 132 | initialized = false; |
| @@ -273,7 +265,6 @@ struct IPCState { | |||
| 273 | 265 | ||
| 274 | slot_matches = false; | 266 | slot_matches = false; |
| 275 | player_position = std::nullopt; | 267 | player_position = std::nullopt; |
| 276 | solved_panels.clear(); | ||
| 277 | } | 268 | } |
| 278 | } | 269 | } |
| 279 | 270 | ||
| @@ -314,14 +305,6 @@ struct IPCState { | |||
| 314 | std::make_tuple<int, int>(msg["position"]["x"], msg["position"]["z"]); | 305 | std::make_tuple<int, int>(msg["position"]["x"], msg["position"]["z"]); |
| 315 | 306 | ||
| 316 | tracker_frame->UpdateIndicators(StateUpdate{.player_position = true}); | 307 | tracker_frame->UpdateIndicators(StateUpdate{.player_position = true}); |
| 317 | } else if (msg["cmd"] == "SolvePanels") { | ||
| 318 | std::lock_guard state_guard(state_mutex); | ||
| 319 | |||
| 320 | for (std::string panel : msg["panels"]) { | ||
| 321 | solved_panels.insert(std::move(panel)); | ||
| 322 | } | ||
| 323 | |||
| 324 | tracker_frame->UpdateIndicators(StateUpdate{.open_panels_tab = true}); | ||
| 325 | } | 308 | } |
| 326 | } | 309 | } |
| 327 | 310 | ||
| @@ -382,7 +365,3 @@ bool IPC_IsConnected() { return GetState().IsConnected(); } | |||
| 382 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition() { | 365 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition() { |
| 383 | return GetState().GetPlayerPosition(); | 366 | return GetState().GetPlayerPosition(); |
| 384 | } | 367 | } |
| 385 | |||
| 386 | std::set<std::string> IPC_GetSolvedPanels() { | ||
| 387 | return GetState().GetSolvedPanels(); | ||
| 388 | } | ||
| diff --git a/src/ipc_state.h b/src/ipc_state.h index 7c9d68d..0e6fa51 100644 --- a/src/ipc_state.h +++ b/src/ipc_state.h | |||
| @@ -20,6 +20,4 @@ bool IPC_IsConnected(); | |||
| 20 | 20 | ||
| 21 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition(); | 21 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition(); |
| 22 | 22 | ||
| 23 | std::set<std::string> IPC_GetSolvedPanels(); | ||
| 24 | |||
| 25 | #endif /* end of include guard: IPC_STATE_H_6B3B0958 */ | 23 | #endif /* end of include guard: IPC_STATE_H_6B3B0958 */ |
| diff --git a/src/main.cpp b/src/main.cpp index 1d7cc9e..574b6df 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | class TrackerApp : public wxApp { | 11 | class TrackerApp : public wxApp { |
| 12 | public: | 12 | public: |
| 13 | virtual bool OnInit() { | 13 | virtual bool OnInit() override { |
| 14 | GetTrackerConfig().Load(); | 14 | GetTrackerConfig().Load(); |
| 15 | 15 | ||
| 16 | TrackerFrame *frame = new TrackerFrame(); | 16 | TrackerFrame *frame = new TrackerFrame(); |
| diff --git a/src/settings_dialog.cpp b/src/settings_dialog.cpp index fa7a82f..95df577 100644 --- a/src/settings_dialog.cpp +++ b/src/settings_dialog.cpp | |||
| @@ -3,35 +3,43 @@ | |||
| 3 | #include "tracker_config.h" | 3 | #include "tracker_config.h" |
| 4 | 4 | ||
| 5 | SettingsDialog::SettingsDialog() : wxDialog(nullptr, wxID_ANY, "Settings") { | 5 | SettingsDialog::SettingsDialog() : wxDialog(nullptr, wxID_ANY, "Settings") { |
| 6 | should_check_for_updates_box_ = new wxCheckBox( | 6 | wxStaticBoxSizer* main_box = |
| 7 | this, wxID_ANY, "Check for updates when the tracker opens"); | 7 | new wxStaticBoxSizer(wxVERTICAL, this, "General settings"); |
| 8 | |||
| 9 | should_check_for_updates_box_ = | ||
| 10 | new wxCheckBox(main_box->GetStaticBox(), wxID_ANY, | ||
| 11 | "Check for updates when the tracker opens"); | ||
| 8 | hybrid_areas_box_ = new wxCheckBox( | 12 | hybrid_areas_box_ = new wxCheckBox( |
| 9 | this, wxID_ANY, | 13 | main_box->GetStaticBox(), wxID_ANY, |
| 10 | "Use two colors to show that an area has partial availability"); | 14 | "Use two colors to show that an area has partial availability"); |
| 11 | show_hunt_panels_box_ = new wxCheckBox(this, wxID_ANY, "Show hunt panels"); | 15 | track_position_box_ = new wxCheckBox(main_box->GetStaticBox(), wxID_ANY, |
| 12 | track_position_box_ = new wxCheckBox(this, wxID_ANY, "Track player position"); | 16 | "Track player position"); |
| 13 | 17 | ||
| 14 | should_check_for_updates_box_->SetValue( | 18 | should_check_for_updates_box_->SetValue( |
| 15 | GetTrackerConfig().should_check_for_updates); | 19 | GetTrackerConfig().should_check_for_updates); |
| 16 | hybrid_areas_box_->SetValue(GetTrackerConfig().hybrid_areas); | 20 | hybrid_areas_box_->SetValue(GetTrackerConfig().hybrid_areas); |
| 17 | show_hunt_panels_box_->SetValue(GetTrackerConfig().show_hunt_panels); | ||
| 18 | track_position_box_->SetValue(GetTrackerConfig().track_position); | 21 | track_position_box_->SetValue(GetTrackerConfig().track_position); |
| 19 | 22 | ||
| 20 | wxBoxSizer* form_sizer = new wxBoxSizer(wxVERTICAL); | 23 | main_box->Add(should_check_for_updates_box_, wxSizerFlags().Border()); |
| 21 | 24 | main_box->AddSpacer(2); | |
| 22 | form_sizer->Add(should_check_for_updates_box_, wxSizerFlags().HorzBorder()); | 25 | main_box->Add(hybrid_areas_box_, wxSizerFlags().Border()); |
| 23 | form_sizer->AddSpacer(2); | 26 | main_box->AddSpacer(2); |
| 24 | 27 | main_box->Add(track_position_box_, wxSizerFlags().Border()); | |
| 25 | form_sizer->Add(hybrid_areas_box_, wxSizerFlags().HorzBorder()); | 28 | |
| 26 | form_sizer->AddSpacer(2); | 29 | const wxString visible_panels_choices[] = {"Only show locations", |
| 30 | "Show locations and hunt panels", | ||
| 31 | "Show all panels"}; | ||
| 32 | visible_panels_box_ = | ||
| 33 | new wxRadioBox(this, wxID_ANY, "Visible panels", wxDefaultPosition, | ||
| 34 | wxDefaultSize, 3, visible_panels_choices, 1); | ||
| 35 | visible_panels_box_->SetSelection( | ||
| 36 | static_cast<int>(GetTrackerConfig().visible_panels)); | ||
| 27 | 37 | ||
| 28 | form_sizer->Add(show_hunt_panels_box_, wxSizerFlags().HorzBorder()); | 38 | wxBoxSizer* form_sizer = new wxBoxSizer(wxVERTICAL); |
| 29 | form_sizer->AddSpacer(2); | 39 | form_sizer->Add(main_box, wxSizerFlags().Border().Expand()); |
| 30 | 40 | form_sizer->Add(visible_panels_box_, wxSizerFlags().Border().Expand()); | |
| 31 | form_sizer->Add(track_position_box_, wxSizerFlags().HorzBorder()); | 41 | form_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), |
| 32 | form_sizer->AddSpacer(2); | 42 | wxSizerFlags().Center().Border()); |
| 33 | |||
| 34 | form_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Center()); | ||
| 35 | 43 | ||
| 36 | SetSizerAndFit(form_sizer); | 44 | SetSizerAndFit(form_sizer); |
| 37 | 45 | ||
| diff --git a/src/settings_dialog.h b/src/settings_dialog.h index 12f2439..c4dacfa 100644 --- a/src/settings_dialog.h +++ b/src/settings_dialog.h | |||
| @@ -7,6 +7,10 @@ | |||
| 7 | #include <wx/wx.h> | 7 | #include <wx/wx.h> |
| 8 | #endif | 8 | #endif |
| 9 | 9 | ||
| 10 | #include <wx/radiobox.h> | ||
| 11 | |||
| 12 | #include "tracker_config.h" | ||
| 13 | |||
| 10 | class SettingsDialog : public wxDialog { | 14 | class SettingsDialog : public wxDialog { |
| 11 | public: | 15 | public: |
| 12 | SettingsDialog(); | 16 | SettingsDialog(); |
| @@ -15,13 +19,16 @@ class SettingsDialog : public wxDialog { | |||
| 15 | return should_check_for_updates_box_->GetValue(); | 19 | return should_check_for_updates_box_->GetValue(); |
| 16 | } | 20 | } |
| 17 | bool GetHybridAreas() const { return hybrid_areas_box_->GetValue(); } | 21 | bool GetHybridAreas() const { return hybrid_areas_box_->GetValue(); } |
| 18 | bool GetShowHuntPanels() const { return show_hunt_panels_box_->GetValue(); } | 22 | TrackerConfig::VisiblePanels GetVisiblePanels() const { |
| 23 | return static_cast<TrackerConfig::VisiblePanels>( | ||
| 24 | visible_panels_box_->GetSelection()); | ||
| 25 | } | ||
| 19 | bool GetTrackPosition() const { return track_position_box_->GetValue(); } | 26 | bool GetTrackPosition() const { return track_position_box_->GetValue(); } |
| 20 | 27 | ||
| 21 | private: | 28 | private: |
| 22 | wxCheckBox* should_check_for_updates_box_; | 29 | wxCheckBox* should_check_for_updates_box_; |
| 23 | wxCheckBox* hybrid_areas_box_; | 30 | wxCheckBox* hybrid_areas_box_; |
| 24 | wxCheckBox* show_hunt_panels_box_; | 31 | wxRadioBox* visible_panels_box_; |
| 25 | wxCheckBox* track_position_box_; | 32 | wxCheckBox* track_position_box_; |
| 26 | }; | 33 | }; |
| 27 | 34 | ||
| diff --git a/src/subway_map.cpp b/src/subway_map.cpp index 94292fd..55ac411 100644 --- a/src/subway_map.cpp +++ b/src/subway_map.cpp | |||
| @@ -551,6 +551,18 @@ void SubwayMap::Redraw() { | |||
| 551 | brush_color = wxGREEN_BRUSH; | 551 | brush_color = wxGREEN_BRUSH; |
| 552 | } else if (subway_item.special == "starting_room_overhead") { | 552 | } else if (subway_item.special == "starting_room_overhead") { |
| 553 | // Do not draw. | 553 | // Do not draw. |
| 554 | } else if (AP_IsColorShuffle() && subway_item.special && | ||
| 555 | subway_item.special->starts_with("color_")) { | ||
| 556 | std::string color_name = subway_item.special->substr(6); | ||
| 557 | LingoColor lingo_color = GetLingoColorForString(color_name); | ||
| 558 | int color_item_id = GD_GetItemIdForColor(lingo_color); | ||
| 559 | |||
| 560 | draw_type = ItemDrawType::kBox; | ||
| 561 | if (AP_HasItemSafe(color_item_id)) { | ||
| 562 | brush_color = wxGREEN_BRUSH; | ||
| 563 | } else { | ||
| 564 | brush_color = wxRED_BRUSH; | ||
| 565 | } | ||
| 554 | } else if (subway_item.special == "sun_painting") { | 566 | } else if (subway_item.special == "sun_painting") { |
| 555 | if (!AP_IsPilgrimageEnabled()) { | 567 | if (!AP_IsPilgrimageEnabled()) { |
| 556 | draw_type = ItemDrawType::kOwl; | 568 | draw_type = ItemDrawType::kOwl; |
| diff --git a/src/tracker_config.cpp b/src/tracker_config.cpp index aeff669..da5d60a 100644 --- a/src/tracker_config.cpp +++ b/src/tracker_config.cpp | |||
| @@ -16,7 +16,9 @@ void TrackerConfig::Load() { | |||
| 16 | asked_to_check_for_updates = file["asked_to_check_for_updates"].as<bool>(); | 16 | asked_to_check_for_updates = file["asked_to_check_for_updates"].as<bool>(); |
| 17 | should_check_for_updates = file["should_check_for_updates"].as<bool>(); | 17 | should_check_for_updates = file["should_check_for_updates"].as<bool>(); |
| 18 | hybrid_areas = file["hybrid_areas"].as<bool>(); | 18 | hybrid_areas = file["hybrid_areas"].as<bool>(); |
| 19 | show_hunt_panels = file["show_hunt_panels"].as<bool>(); | 19 | if (file["show_hunt_panels"] && file["show_hunt_panels"].as<bool>()) { |
| 20 | visible_panels = kHUNT_PANELS; | ||
| 21 | } | ||
| 20 | 22 | ||
| 21 | if (file["connection_history"]) { | 23 | if (file["connection_history"]) { |
| 22 | for (const auto& connection : file["connection_history"]) { | 24 | for (const auto& connection : file["connection_history"]) { |
| @@ -30,6 +32,8 @@ void TrackerConfig::Load() { | |||
| 30 | 32 | ||
| 31 | ipc_address = file["ipc_address"].as<std::string>(); | 33 | ipc_address = file["ipc_address"].as<std::string>(); |
| 32 | track_position = file["track_position"].as<bool>(); | 34 | track_position = file["track_position"].as<bool>(); |
| 35 | visible_panels = | ||
| 36 | static_cast<VisiblePanels>(file["visible_panels"].as<int>()); | ||
| 33 | } catch (const std::exception&) { | 37 | } catch (const std::exception&) { |
| 34 | // It's fine if the file can't be loaded. | 38 | // It's fine if the file can't be loaded. |
| 35 | } | 39 | } |
| @@ -43,7 +47,6 @@ void TrackerConfig::Save() { | |||
| 43 | output["asked_to_check_for_updates"] = asked_to_check_for_updates; | 47 | output["asked_to_check_for_updates"] = asked_to_check_for_updates; |
| 44 | output["should_check_for_updates"] = should_check_for_updates; | 48 | output["should_check_for_updates"] = should_check_for_updates; |
| 45 | output["hybrid_areas"] = hybrid_areas; | 49 | output["hybrid_areas"] = hybrid_areas; |
| 46 | output["show_hunt_panels"] = show_hunt_panels; | ||
| 47 | 50 | ||
| 48 | output.remove("connection_history"); | 51 | output.remove("connection_history"); |
| 49 | for (const ConnectionDetails& details : connection_history) { | 52 | for (const ConnectionDetails& details : connection_history) { |
| @@ -57,6 +60,7 @@ void TrackerConfig::Save() { | |||
| 57 | 60 | ||
| 58 | output["ipc_address"] = ipc_address; | 61 | output["ipc_address"] = ipc_address; |
| 59 | output["track_position"] = track_position; | 62 | output["track_position"] = track_position; |
| 63 | output["visible_panels"] = static_cast<int>(visible_panels); | ||
| 60 | 64 | ||
| 61 | std::ofstream filewriter(filename_); | 65 | std::ofstream filewriter(filename_); |
| 62 | filewriter << output; | 66 | filewriter << output; |
| diff --git a/src/tracker_config.h b/src/tracker_config.h index 4e851dc..df4105d 100644 --- a/src/tracker_config.h +++ b/src/tracker_config.h | |||
| @@ -23,14 +23,20 @@ class TrackerConfig { | |||
| 23 | 23 | ||
| 24 | void Save(); | 24 | void Save(); |
| 25 | 25 | ||
| 26 | enum VisiblePanels { | ||
| 27 | kLOCATIONS_ONLY, | ||
| 28 | kHUNT_PANELS, | ||
| 29 | kALL_PANELS, | ||
| 30 | }; | ||
| 31 | |||
| 26 | ConnectionDetails connection_details; | 32 | ConnectionDetails connection_details; |
| 27 | bool asked_to_check_for_updates = false; | 33 | bool asked_to_check_for_updates = false; |
| 28 | bool should_check_for_updates = false; | 34 | bool should_check_for_updates = false; |
| 29 | bool hybrid_areas = false; | 35 | bool hybrid_areas = false; |
| 30 | bool show_hunt_panels = false; | ||
| 31 | std::deque<ConnectionDetails> connection_history; | 36 | std::deque<ConnectionDetails> connection_history; |
| 32 | std::string ipc_address; | 37 | std::string ipc_address; |
| 33 | bool track_position = true; | 38 | bool track_position = true; |
| 39 | VisiblePanels visible_panels = kLOCATIONS_ONLY; | ||
| 34 | 40 | ||
| 35 | private: | 41 | private: |
| 36 | std::string filename_; | 42 | std::string filename_; |
| diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index fa68582..e8d7ef6 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 | ||
| @@ -50,7 +51,6 @@ enum TrackerFrameIds { | |||
| 50 | ID_SETTINGS = 3, | 51 | ID_SETTINGS = 3, |
| 51 | ID_ZOOM_IN = 4, | 52 | ID_ZOOM_IN = 4, |
| 52 | ID_ZOOM_OUT = 5, | 53 | ID_ZOOM_OUT = 5, |
| 53 | ID_OPEN_SAVE_FILE = 6, | ||
| 54 | ID_IPC_CONNECT = 7, | 54 | ID_IPC_CONNECT = 7, |
| 55 | ID_LOG_DIALOG = 8, | 55 | ID_LOG_DIALOG = 8, |
| 56 | }; | 56 | }; |
| @@ -76,7 +76,6 @@ TrackerFrame::TrackerFrame() | |||
| 76 | wxMenu *menuFile = new wxMenu(); | 76 | wxMenu *menuFile = new wxMenu(); |
| 77 | menuFile->Append(ID_AP_CONNECT, "&Connect to Archipelago"); | 77 | menuFile->Append(ID_AP_CONNECT, "&Connect to Archipelago"); |
| 78 | menuFile->Append(ID_IPC_CONNECT, "&Connect to Lingo"); | 78 | menuFile->Append(ID_IPC_CONNECT, "&Connect to Lingo"); |
| 79 | menuFile->Append(ID_OPEN_SAVE_FILE, "&Open Save Data\tCtrl-O"); | ||
| 80 | menuFile->Append(ID_SETTINGS, "&Settings"); | 79 | menuFile->Append(ID_SETTINGS, "&Settings"); |
| 81 | menuFile->Append(wxID_EXIT); | 80 | menuFile->Append(wxID_EXIT); |
| 82 | 81 | ||
| @@ -113,7 +112,6 @@ TrackerFrame::TrackerFrame() | |||
| 113 | Bind(wxEVT_MENU, &TrackerFrame::OnZoomOut, this, ID_ZOOM_OUT); | 112 | Bind(wxEVT_MENU, &TrackerFrame::OnZoomOut, this, ID_ZOOM_OUT); |
| 114 | Bind(wxEVT_MENU, &TrackerFrame::OnOpenLogWindow, this, ID_LOG_DIALOG); | 113 | Bind(wxEVT_MENU, &TrackerFrame::OnOpenLogWindow, this, ID_LOG_DIALOG); |
| 115 | Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this); | 114 | Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this); |
| 116 | Bind(wxEVT_MENU, &TrackerFrame::OnOpenFile, this, ID_OPEN_SAVE_FILE); | ||
| 117 | Bind(wxEVT_SPLITTER_SASH_POS_CHANGED, &TrackerFrame::OnSashPositionChanged, | 115 | Bind(wxEVT_SPLITTER_SASH_POS_CHANGED, &TrackerFrame::OnSashPositionChanged, |
| 118 | this); | 116 | this); |
| 119 | Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); | 117 | Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); |
| @@ -251,7 +249,7 @@ void TrackerFrame::OnSettings(wxCommandEvent &event) { | |||
| 251 | GetTrackerConfig().should_check_for_updates = | 249 | GetTrackerConfig().should_check_for_updates = |
| 252 | dlg.GetShouldCheckForUpdates(); | 250 | dlg.GetShouldCheckForUpdates(); |
| 253 | GetTrackerConfig().hybrid_areas = dlg.GetHybridAreas(); | 251 | GetTrackerConfig().hybrid_areas = dlg.GetHybridAreas(); |
| 254 | GetTrackerConfig().show_hunt_panels = dlg.GetShowHuntPanels(); | 252 | GetTrackerConfig().visible_panels = dlg.GetVisiblePanels(); |
| 255 | GetTrackerConfig().track_position = dlg.GetTrackPosition(); | 253 | GetTrackerConfig().track_position = dlg.GetTrackPosition(); |
| 256 | GetTrackerConfig().Save(); | 254 | GetTrackerConfig().Save(); |
| 257 | 255 | ||
| @@ -302,28 +300,6 @@ void TrackerFrame::OnChangePage(wxBookCtrlEvent &event) { | |||
| 302 | zoom_out_menu_item_->Enable(event.GetSelection() == 1); | 300 | zoom_out_menu_item_->Enable(event.GetSelection() == 1); |
| 303 | } | 301 | } |
| 304 | 302 | ||
| 305 | void TrackerFrame::OnOpenFile(wxCommandEvent &event) { | ||
| 306 | wxFileDialog open_file_dialog( | ||
| 307 | this, "Open Lingo Save File", | ||
| 308 | fmt::format("{}\\Godot\\app_userdata\\Lingo\\level1_stable", | ||
| 309 | wxStandardPaths::Get().GetUserConfigDir().ToStdString()), | ||
| 310 | AP_GetSaveName(), "Lingo save file (*.save)|*.save", | ||
| 311 | wxFD_OPEN | wxFD_FILE_MUST_EXIST); | ||
| 312 | if (open_file_dialog.ShowModal() == wxID_CANCEL) { | ||
| 313 | return; | ||
| 314 | } | ||
| 315 | |||
| 316 | std::string savedata_path = open_file_dialog.GetPath().ToStdString(); | ||
| 317 | |||
| 318 | if (panels_panel_ == nullptr) { | ||
| 319 | panels_panel_ = new TrackerPanel(notebook_); | ||
| 320 | notebook_->AddPage(panels_panel_, "Panels"); | ||
| 321 | } | ||
| 322 | |||
| 323 | notebook_->SetSelection(notebook_->FindPage(panels_panel_)); | ||
| 324 | panels_panel_->SetSavedataPath(savedata_path); | ||
| 325 | } | ||
| 326 | |||
| 327 | void TrackerFrame::OnSashPositionChanged(wxSplitterEvent& event) { | 303 | void TrackerFrame::OnSashPositionChanged(wxSplitterEvent& event) { |
| 328 | notebook_->Refresh(); | 304 | notebook_->Refresh(); |
| 329 | } | 305 | } |
| @@ -335,51 +311,41 @@ void TrackerFrame::OnStateReset(wxCommandEvent &event) { | |||
| 335 | options_pane_->OnConnect(); | 311 | options_pane_->OnConnect(); |
| 336 | paintings_pane_->ResetIndicators(); | 312 | paintings_pane_->ResetIndicators(); |
| 337 | subway_map_->OnConnect(); | 313 | subway_map_->OnConnect(); |
| 338 | if (panels_panel_ != nullptr) { | ||
| 339 | notebook_->DeletePage(notebook_->FindPage(panels_panel_)); | ||
| 340 | panels_panel_ = nullptr; | ||
| 341 | } | ||
| 342 | Refresh(); | 314 | Refresh(); |
| 343 | } | 315 | } |
| 344 | 316 | ||
| 345 | void TrackerFrame::OnStateChanged(StateChangedEvent &event) { | 317 | void TrackerFrame::OnStateChanged(StateChangedEvent &event) { |
| 346 | const StateUpdate &state = event.GetState(); | 318 | const StateUpdate &state = event.GetState(); |
| 347 | 319 | ||
| 348 | if (state.open_panels_tab) { | 320 | bool hunt_panels = false; |
| 349 | if (panels_panel_ == nullptr) { | 321 | if (GetTrackerConfig().visible_panels == TrackerConfig::kHUNT_PANELS) { |
| 350 | panels_panel_ = new TrackerPanel(notebook_); | 322 | hunt_panels = std::any_of( |
| 351 | panels_panel_->SetPanelsMode(); | 323 | state.panels.begin(), state.panels.end(), [](int solve_index) { |
| 352 | notebook_->AddPage(panels_panel_, "Panels"); | 324 | return GD_GetPanel(GD_GetPanelBySolveIndex(solve_index)).hunt; |
| 353 | } | 325 | }); |
| 354 | panels_panel_->UpdateIndicators(/*reset=*/false); | 326 | } else if (GetTrackerConfig().visible_panels == TrackerConfig::kALL_PANELS) { |
| 355 | if (notebook_->GetSelection() == 2) { | 327 | hunt_panels = true; |
| 356 | Refresh(); | ||
| 357 | } | ||
| 358 | |||
| 359 | return; | ||
| 360 | } | 328 | } |
| 361 | 329 | ||
| 362 | if (!state.items.empty() || !state.paintings.empty() || | 330 | if (!state.items.empty() || !state.paintings.empty() || |
| 363 | state.cleared_locations || | 331 | 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 | 332 | // 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 | 333 | // connection is if the hunt panels setting changes. If we remove hunt |
| 367 | // panels later, we can get rid of this. | 334 | // panels later, we can get rid of this. |
| 368 | tracker_panel_->UpdateIndicators(/*reset=*/state.changed_settings); | 335 | tracker_panel_->UpdateIndicators(/*reset=*/state.changed_settings); |
| 369 | subway_map_->UpdateIndicators(); | 336 | subway_map_->UpdateIndicators(); |
| 370 | if (panels_panel_ != nullptr) { | ||
| 371 | panels_panel_->UpdateIndicators(/*reset=*/false); | ||
| 372 | } | ||
| 373 | Refresh(); | 337 | Refresh(); |
| 374 | } else if (state.player_position && GetTrackerConfig().track_position) { | 338 | } else if (state.player_position && GetTrackerConfig().track_position) { |
| 375 | if (notebook_->GetSelection() == 0) { | 339 | if (notebook_->GetSelection() == 0) { |
| 376 | tracker_panel_->Refresh(); | 340 | tracker_panel_->Refresh(); |
| 377 | } else if (notebook_->GetSelection() == 2) { | ||
| 378 | panels_panel_->Refresh(); | ||
| 379 | } | 341 | } |
| 380 | } | 342 | } |
| 381 | 343 | ||
| 382 | if (state.achievements) { | 344 | if (std::any_of(state.panels.begin(), state.panels.end(), |
| 345 | [](int solve_index) { | ||
| 346 | return GD_GetPanel(GD_GetPanelBySolveIndex(solve_index)) | ||
| 347 | .achievement; | ||
| 348 | })) { | ||
| 383 | achievements_pane_->UpdateIndicators(); | 349 | achievements_pane_->UpdateIndicators(); |
| 384 | } | 350 | } |
| 385 | 351 | ||
| diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 131c7b8..00bbe70 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" |
| @@ -52,10 +53,8 @@ struct StateUpdate { | |||
| 52 | std::vector<ItemState> items; | 53 | std::vector<ItemState> items; |
| 53 | bool progression_items = false; | 54 | bool progression_items = false; |
| 54 | std::vector<std::string> paintings; | 55 | std::vector<std::string> paintings; |
| 55 | bool achievements = false; | ||
| 56 | bool open_panels_tab = false; | ||
| 57 | bool cleared_locations = false; | 56 | bool cleared_locations = false; |
| 58 | bool hunt_panels = false; | 57 | std::set<int> panels; |
| 59 | bool player_position = false; | 58 | bool player_position = false; |
| 60 | bool changed_settings = false; | 59 | bool changed_settings = false; |
| 61 | }; | 60 | }; |
| @@ -100,7 +99,6 @@ class TrackerFrame : public wxFrame { | |||
| 100 | void OnOpenLogWindow(wxCommandEvent &event); | 99 | void OnOpenLogWindow(wxCommandEvent &event); |
| 101 | void OnCloseLogWindow(wxCloseEvent &event); | 100 | void OnCloseLogWindow(wxCloseEvent &event); |
| 102 | void OnChangePage(wxBookCtrlEvent &event); | 101 | void OnChangePage(wxBookCtrlEvent &event); |
| 103 | void OnOpenFile(wxCommandEvent &event); | ||
| 104 | void OnSashPositionChanged(wxSplitterEvent &event); | 102 | void OnSashPositionChanged(wxSplitterEvent &event); |
| 105 | 103 | ||
| 106 | void OnStateReset(wxCommandEvent &event); | 104 | void OnStateReset(wxCommandEvent &event); |
| @@ -118,7 +116,6 @@ class TrackerFrame : public wxFrame { | |||
| 118 | OptionsPane *options_pane_; | 116 | OptionsPane *options_pane_; |
| 119 | PaintingsPane *paintings_pane_; | 117 | PaintingsPane *paintings_pane_; |
| 120 | SubwayMap *subway_map_; | 118 | SubwayMap *subway_map_; |
| 121 | TrackerPanel *panels_panel_ = nullptr; | ||
| 122 | LogDialog *log_dialog_ = nullptr; | 119 | LogDialog *log_dialog_ = nullptr; |
| 123 | 120 | ||
| 124 | wxMenuItem *zoom_in_menu_item_; | 121 | wxMenuItem *zoom_in_menu_item_; |
| diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 9adcb1f..ddb4df9 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include "area_popup.h" | 9 | #include "area_popup.h" |
| 10 | #include "game_data.h" | 10 | #include "game_data.h" |
| 11 | #include "global.h" | 11 | #include "global.h" |
| 12 | #include "godot_variant.h" | ||
| 13 | #include "ipc_state.h" | 12 | #include "ipc_state.h" |
| 14 | #include "tracker_config.h" | 13 | #include "tracker_config.h" |
| 15 | #include "tracker_state.h" | 14 | #include "tracker_state.h" |
| @@ -52,21 +51,17 @@ TrackerPanel::TrackerPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { | |||
| 52 | } | 51 | } |
| 53 | 52 | ||
| 54 | void TrackerPanel::UpdateIndicators(bool reset) { | 53 | void TrackerPanel::UpdateIndicators(bool reset) { |
| 55 | if (panels_mode_ && !savedata_path_) { | ||
| 56 | solved_panels_ = IPC_GetSolvedPanels(); | ||
| 57 | } | ||
| 58 | |||
| 59 | if (reset) { | 54 | if (reset) { |
| 60 | for (AreaIndicator &area : areas_) { | 55 | for (AreaIndicator &area : areas_) { |
| 61 | const MapArea &map_area = GD_GetMapArea(area.area_id); | 56 | const MapArea &map_area = GD_GetMapArea(area.area_id); |
| 62 | 57 | ||
| 63 | if (IsAreaPostgame(area.area_id)) { | 58 | if ((!AP_IsLocationVisible(map_area.classification) || |
| 64 | area.active = false; | 59 | IsAreaPostgame(area.area_id)) && |
| 65 | } else if (panels_mode_) { | 60 | !(map_area.hunt && |
| 66 | area.active = map_area.has_single_panel; | 61 | GetTrackerConfig().visible_panels == TrackerConfig::kHUNT_PANELS) && |
| 67 | } else if (!AP_IsLocationVisible(map_area.classification) && | 62 | !(map_area.has_single_panel && |
| 68 | !(map_area.hunt && GetTrackerConfig().show_hunt_panels) && | 63 | GetTrackerConfig().visible_panels == TrackerConfig::kALL_PANELS) && |
| 69 | !(AP_IsPaintingShuffle() && !map_area.paintings.empty())) { | 64 | !(AP_IsPaintingShuffle() && !map_area.paintings.empty())) { |
| 70 | area.active = false; | 65 | area.active = false; |
| 71 | } else { | 66 | } else { |
| 72 | area.active = true; | 67 | area.active = true; |
| @@ -85,47 +80,10 @@ void TrackerPanel::UpdateIndicators(bool reset) { | |||
| 85 | Redraw(); | 80 | Redraw(); |
| 86 | } | 81 | } |
| 87 | 82 | ||
| 88 | void TrackerPanel::SetPanelsMode() { panels_mode_ = true; } | ||
| 89 | |||
| 90 | void TrackerPanel::SetSavedataPath(std::string savedata_path) { | ||
| 91 | if (!savedata_path_) { | ||
| 92 | refresh_button_ = new wxButton(this, wxID_ANY, "Refresh"); | ||
| 93 | refresh_button_->Bind(wxEVT_BUTTON, &TrackerPanel::OnRefreshSavedata, this); | ||
| 94 | SetUpRefreshButton(); | ||
| 95 | } | ||
| 96 | |||
| 97 | savedata_path_ = savedata_path; | ||
| 98 | panels_mode_ = true; | ||
| 99 | |||
| 100 | UpdateIndicators(/*reset=*/true); | ||
| 101 | RefreshSavedata(); | ||
| 102 | } | ||
| 103 | |||
| 104 | void TrackerPanel::RefreshSavedata() { | ||
| 105 | solved_panels_.clear(); | ||
| 106 | |||
| 107 | GodotVariant godot_variant = ParseGodotFile(*savedata_path_); | ||
| 108 | for (const GodotVariant &panel_node : godot_variant.AsArray()) { | ||
| 109 | const std::vector<GodotVariant> &fields = panel_node.AsArray(); | ||
| 110 | if (fields[1].AsBool()) { | ||
| 111 | const std::vector<std::string> &nodepath = fields[0].AsNodePath(); | ||
| 112 | std::string key = fmt::format("{}/{}", nodepath[3], nodepath[4]); | ||
| 113 | solved_panels_.insert(key); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | UpdateIndicators(/*reset=*/false); | ||
| 118 | Refresh(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void TrackerPanel::OnPaint(wxPaintEvent &event) { | 83 | void TrackerPanel::OnPaint(wxPaintEvent &event) { |
| 122 | if (GetSize() != rendered_.GetSize()) { | 84 | if (GetSize() != rendered_.GetSize()) { |
| 123 | Resize(); | 85 | Resize(); |
| 124 | Redraw(); | 86 | Redraw(); |
| 125 | |||
| 126 | if (refresh_button_ != nullptr) { | ||
| 127 | SetUpRefreshButton(); | ||
| 128 | } | ||
| 129 | } | 87 | } |
| 130 | 88 | ||
| 131 | wxBufferedPaintDC dc(this); | 89 | wxBufferedPaintDC dc(this); |
| @@ -174,10 +132,6 @@ void TrackerPanel::OnMouseMove(wxMouseEvent &event) { | |||
| 174 | event.Skip(); | 132 | event.Skip(); |
| 175 | } | 133 | } |
| 176 | 134 | ||
| 177 | void TrackerPanel::OnRefreshSavedata(wxCommandEvent &event) { | ||
| 178 | RefreshSavedata(); | ||
| 179 | } | ||
| 180 | |||
| 181 | void TrackerPanel::Resize() { | 135 | void TrackerPanel::Resize() { |
| 182 | wxSize panel_size = GetClientSize(); | 136 | wxSize panel_size = GetClientSize(); |
| 183 | wxSize image_size = map_image_.GetSize(); | 137 | wxSize image_size = map_image_.GetSize(); |
| @@ -277,23 +231,17 @@ void TrackerPanel::Redraw() { | |||
| 277 | bool has_unreachable_unchecked = false; | 231 | bool has_unreachable_unchecked = false; |
| 278 | for (const Location §ion : map_area.locations) { | 232 | for (const Location §ion : map_area.locations) { |
| 279 | bool has_unchecked = false; | 233 | bool has_unchecked = false; |
| 280 | if (IsLocationPostgame(section.ap_location_id)) { | 234 | if (IsLocationWinCondition(section)) { |
| 281 | // Nope. | ||
| 282 | } else if (IsLocationWinCondition(section)) { | ||
| 283 | has_unchecked = !AP_HasReachedGoal(); | 235 | has_unchecked = !AP_HasReachedGoal(); |
| 284 | } else if (panels_mode_) { | 236 | } else if (AP_IsLocationVisible(section.classification) && |
| 285 | if (section.single_panel) { | 237 | !IsLocationPostgame(section.ap_location_id)) { |
| 286 | const Panel &panel = GD_GetPanel(*section.single_panel); | ||
| 287 | if (panel.non_counting) { | ||
| 288 | has_unchecked = !AP_HasCheckedGameLocation(section.ap_location_id); | ||
| 289 | } else { | ||
| 290 | has_unchecked = !GetSolvedPanels().contains(panel.nodepath); | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } else if (AP_IsLocationVisible(section.classification)) { | ||
| 294 | has_unchecked = !AP_HasCheckedGameLocation(section.ap_location_id); | 238 | has_unchecked = !AP_HasCheckedGameLocation(section.ap_location_id); |
| 295 | } else if (section.hunt && GetTrackerConfig().show_hunt_panels) { | 239 | } else if ((section.hunt && GetTrackerConfig().visible_panels == |
| 296 | has_unchecked = !AP_HasCheckedHuntPanel(section.ap_location_id); | 240 | TrackerConfig::kHUNT_PANELS) || |
| 241 | (section.single_panel && GetTrackerConfig().visible_panels == | ||
| 242 | TrackerConfig::kALL_PANELS)) { | ||
| 243 | has_unchecked = | ||
| 244 | !AP_IsPanelSolved(GD_GetPanel(*section.single_panel).solve_index); | ||
| 297 | } | 245 | } |
| 298 | 246 | ||
| 299 | if (has_unchecked) { | 247 | if (has_unchecked) { |
| @@ -305,7 +253,7 @@ void TrackerPanel::Redraw() { | |||
| 305 | } | 253 | } |
| 306 | } | 254 | } |
| 307 | 255 | ||
| 308 | if (AP_IsPaintingShuffle() && !panels_mode_) { | 256 | if (AP_IsPaintingShuffle()) { |
| 309 | for (int painting_id : map_area.paintings) { | 257 | for (int painting_id : map_area.paintings) { |
| 310 | if (IsPaintingPostgame(painting_id)) { | 258 | if (IsPaintingPostgame(painting_id)) { |
| 311 | continue; | 259 | continue; |
| @@ -357,8 +305,3 @@ void TrackerPanel::Redraw() { | |||
| 357 | } | 305 | } |
| 358 | } | 306 | } |
| 359 | } | 307 | } |
| 360 | |||
| 361 | void TrackerPanel::SetUpRefreshButton() { | ||
| 362 | refresh_button_->SetSize(FromDIP(15), FromDIP(15), wxDefaultCoord, | ||
| 363 | wxDefaultCoord, wxSIZE_AUTO); | ||
| 364 | } | ||
| diff --git a/src/tracker_panel.h b/src/tracker_panel.h index abab1bf..6825843 100644 --- a/src/tracker_panel.h +++ b/src/tracker_panel.h | |||
| @@ -19,16 +19,6 @@ class TrackerPanel : public wxPanel { | |||
| 19 | 19 | ||
| 20 | void UpdateIndicators(bool reset); | 20 | void UpdateIndicators(bool reset); |
| 21 | 21 | ||
| 22 | void SetPanelsMode(); | ||
| 23 | |||
| 24 | void SetSavedataPath(std::string savedata_path); | ||
| 25 | |||
| 26 | bool IsPanelsMode() const { return panels_mode_; } | ||
| 27 | |||
| 28 | const std::set<std::string> &GetSolvedPanels() const { | ||
| 29 | return solved_panels_; | ||
| 30 | } | ||
| 31 | |||
| 32 | private: | 22 | private: |
| 33 | struct AreaIndicator { | 23 | struct AreaIndicator { |
| 34 | int area_id = -1; | 24 | int area_id = -1; |
| @@ -42,23 +32,16 @@ class TrackerPanel : public wxPanel { | |||
| 42 | 32 | ||
| 43 | void OnPaint(wxPaintEvent &event); | 33 | void OnPaint(wxPaintEvent &event); |
| 44 | void OnMouseMove(wxMouseEvent &event); | 34 | void OnMouseMove(wxMouseEvent &event); |
| 45 | void OnRefreshSavedata(wxCommandEvent &event); | ||
| 46 | 35 | ||
| 47 | void Resize(); | 36 | void Resize(); |
| 48 | void Redraw(); | 37 | void Redraw(); |
| 49 | 38 | ||
| 50 | void RefreshSavedata(); | ||
| 51 | |||
| 52 | void SetUpRefreshButton(); | ||
| 53 | |||
| 54 | wxImage map_image_; | 39 | wxImage map_image_; |
| 55 | wxImage player_image_; | 40 | wxImage player_image_; |
| 56 | wxBitmap scaled_map_; | 41 | wxBitmap scaled_map_; |
| 57 | wxBitmap rendered_; | 42 | wxBitmap rendered_; |
| 58 | wxBitmap scaled_player_; | 43 | wxBitmap scaled_player_; |
| 59 | 44 | ||
| 60 | wxButton *refresh_button_ = nullptr; | ||
| 61 | |||
| 62 | int offset_x_ = 0; | 45 | int offset_x_ = 0; |
| 63 | int offset_y_ = 0; | 46 | int offset_y_ = 0; |
| 64 | double scale_x_ = 0; | 47 | double scale_x_ = 0; |
| @@ -66,10 +49,6 @@ class TrackerPanel : public wxPanel { | |||
| 66 | int real_area_size_ = 0; | 49 | int real_area_size_ = 0; |
| 67 | 50 | ||
| 68 | std::vector<AreaIndicator> areas_; | 51 | std::vector<AreaIndicator> areas_; |
| 69 | |||
| 70 | bool panels_mode_ = false; | ||
| 71 | std::optional<std::string> savedata_path_; | ||
| 72 | std::set<std::string> solved_panels_; | ||
| 73 | }; | 52 | }; |
| 74 | 53 | ||
| 75 | #endif /* end of include guard: TRACKER_PANEL_H_D675A54D */ | 54 | #endif /* end of include guard: TRACKER_PANEL_H_D675A54D */ |
| diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 40ba6c4..674f68a 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp | |||
| @@ -342,7 +342,7 @@ class StateCalculator { | |||
| 342 | new_boundary.push_back( | 342 | new_boundary.push_back( |
| 343 | {.source_room = room_exit.destination_room, | 343 | {.source_room = room_exit.destination_room, |
| 344 | .destination_room = GD_GetRoomByName("Color Hallways"), | 344 | .destination_room = GD_GetRoomByName("Color Hallways"), |
| 345 | .type = EntranceType::kPainting}); | 345 | .type = EntranceType::kStaticPainting}); |
| 346 | } | 346 | } |
| 347 | 347 | ||
| 348 | if (AP_IsPilgrimageEnabled()) { | 348 | if (AP_IsPilgrimageEnabled()) { |
| @@ -368,7 +368,7 @@ class StateCalculator { | |||
| 368 | .destination_room = GD_GetRoomByName("Pilgrim Antechamber"), | 368 | .destination_room = GD_GetRoomByName("Pilgrim Antechamber"), |
| 369 | .door = | 369 | .door = |
| 370 | GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"), | 370 | GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"), |
| 371 | .type = EntranceType::kPainting}); | 371 | .type = EntranceType::kStaticPainting}); |
| 372 | } | 372 | } |
| 373 | } | 373 | } |
| 374 | 374 | ||
| @@ -473,8 +473,7 @@ class StateCalculator { | |||
| 473 | Decision decision = IsNonGroupedDoorReachable(panel_door_obj); | 473 | Decision decision = IsNonGroupedDoorReachable(panel_door_obj); |
| 474 | 474 | ||
| 475 | if (report) { | 475 | if (report) { |
| 476 | (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] = | 476 | (*report)[panel_door_obj.item_name] = (decision == kYes); |
| 477 | (decision == kYes); | ||
| 478 | } | 477 | } |
| 479 | 478 | ||
| 480 | if (decision != kYes) { | 479 | if (decision != kYes) { |
| @@ -485,7 +484,7 @@ class StateCalculator { | |||
| 485 | for (int item_id : reqs.items) { | 484 | for (int item_id : reqs.items) { |
| 486 | bool has_item = AP_HasItem(item_id); | 485 | bool has_item = AP_HasItem(item_id); |
| 487 | if (report) { | 486 | if (report) { |
| 488 | (*report)[AP_GetItemName(item_id)] = has_item; | 487 | (*report)[GD_GetItemName(item_id)] = has_item; |
| 489 | } | 488 | } |
| 490 | 489 | ||
| 491 | if (!has_item) { | 490 | if (!has_item) { |
| @@ -651,7 +650,8 @@ class StateCalculator { | |||
| 651 | !AP_DoesPilgrimageAllowRoofAccess()) { | 650 | !AP_DoesPilgrimageAllowRoofAccess()) { |
| 652 | return kNo; | 651 | return kNo; |
| 653 | } | 652 | } |
| 654 | if (room_exit.type == EntranceType::kPainting && | 653 | if ((room_exit.type == EntranceType::kPainting || |
| 654 | room_exit.type == EntranceType::kStaticPainting) && | ||
| 655 | !AP_DoesPilgrimageAllowPaintings()) { | 655 | !AP_DoesPilgrimageAllowPaintings()) { |
| 656 | return kNo; | 656 | return kNo; |
| 657 | } | 657 | } |
| diff --git a/src/updater.cpp b/src/updater.cpp index 67d5f31..2b05daf 100644 --- a/src/updater.cpp +++ b/src/updater.cpp | |||
| @@ -36,7 +36,7 @@ std::string CalculateStringSha256(const wxString& data) { | |||
| 36 | 36 | ||
| 37 | char output[65] = {0}; | 37 | char output[65] = {0}; |
| 38 | for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { | 38 | for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { |
| 39 | sprintf(output + (i * 2), "%02x", hash[i]); | 39 | snprintf(output + (i * 2), 3, "%02x", hash[i]); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | return std::string(output); | 42 | return std::string(output); |
| @@ -172,26 +172,33 @@ void Updater::InstallUpdate(std::string url, std::string checksum, | |||
| 172 | return; | 172 | return; |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | package_path_.clear(); | 175 | bool download_issue = false; |
| 176 | package_path_.resize(L_tmpnam + 1); | ||
| 177 | tmpnam_s(package_path_.data(), L_tmpnam); | ||
| 178 | 176 | ||
| 179 | { | 177 | wxFileName package_path; |
| 180 | wxFileOutputStream writeOut(package_path_); | 178 | package_path.AssignTempFileName(""); |
| 179 | |||
| 180 | if (!package_path.IsOk()) { | ||
| 181 | download_issue = true; | ||
| 182 | } else { | ||
| 183 | wxFileOutputStream writeOut(package_path.GetFullPath()); | ||
| 181 | wxString fileData = packageRequest.GetResponse().AsString(); | 184 | wxString fileData = packageRequest.GetResponse().AsString(); |
| 182 | writeOut.WriteAll(fileData.c_str(), fileData.length()); | 185 | writeOut.WriteAll(fileData.c_str(), fileData.length()); |
| 183 | 186 | ||
| 184 | std::string downloadedChecksum = CalculateStringSha256(fileData); | 187 | std::string downloadedChecksum = CalculateStringSha256(fileData); |
| 185 | if (downloadedChecksum != checksum) { | 188 | if (downloadedChecksum != checksum) { |
| 186 | if (wxMessageBox("There was an issue downloading the update. Would you " | 189 | download_issue = true; |
| 187 | "like to manually download it instead?", | ||
| 188 | "Error", wxYES_NO | wxICON_ERROR) == wxID_YES) { | ||
| 189 | wxLaunchDefaultBrowser(kChangelogUrl); | ||
| 190 | } | ||
| 191 | return; | ||
| 192 | } | 190 | } |
| 193 | } | 191 | } |
| 194 | 192 | ||
| 193 | if (download_issue) { | ||
| 194 | if (wxMessageBox("There was an issue downloading the update. Would you " | ||
| 195 | "like to manually download it instead?", | ||
| 196 | "Error", wxYES_NO | wxICON_ERROR) == wxID_YES) { | ||
| 197 | wxLaunchDefaultBrowser(kChangelogUrl); | ||
| 198 | } | ||
| 199 | return; | ||
| 200 | } | ||
| 201 | |||
| 195 | std::filesystem::path newArea = GetExecutableDirectory(); | 202 | std::filesystem::path newArea = GetExecutableDirectory(); |
| 196 | std::filesystem::path oldArea = newArea / "old"; | 203 | std::filesystem::path oldArea = newArea / "old"; |
| 197 | std::set<std::filesystem::path> folders; | 204 | std::set<std::filesystem::path> folders; |
| @@ -241,7 +248,7 @@ void Updater::InstallUpdate(std::string url, std::string checksum, | |||
| 241 | } | 248 | } |
| 242 | } | 249 | } |
| 243 | 250 | ||
| 244 | wxFileInputStream fileInputStream(package_path_); | 251 | wxFileInputStream fileInputStream(package_path.GetFullPath()); |
| 245 | wxZipInputStream zipStream(fileInputStream); | 252 | wxZipInputStream zipStream(fileInputStream); |
| 246 | std::unique_ptr<wxZipEntry> zipEntry; | 253 | std::unique_ptr<wxZipEntry> zipEntry; |
| 247 | while ((zipEntry = std::unique_ptr<wxZipEntry>(zipStream.GetNextEntry())) != | 254 | while ((zipEntry = std::unique_ptr<wxZipEntry>(zipStream.GetNextEntry())) != |
| diff --git a/src/updater.h b/src/updater.h index 2d2f746..c604a49 100644 --- a/src/updater.h +++ b/src/updater.h | |||
| @@ -41,7 +41,6 @@ class Updater : public wxEvtHandler { | |||
| 41 | 41 | ||
| 42 | wxFrame* parent_; | 42 | wxFrame* parent_; |
| 43 | UpdateState update_state_ = UpdateState::GetVersionInvisible; | 43 | UpdateState update_state_ = UpdateState::GetVersionInvisible; |
| 44 | std::string package_path_; | ||
| 45 | }; | 44 | }; |
| 46 | 45 | ||
| 47 | #endif /* end of include guard: UPDATER_H_809E7381 */ | 46 | #endif /* end of include guard: UPDATER_H_809E7381 */ |
| diff --git a/src/version.h b/src/version.h index 3d678e5..3439fda 100644 --- a/src/version.h +++ b/src/version.h | |||
| @@ -36,6 +36,6 @@ struct Version { | |||
| 36 | } | 36 | } |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | constexpr const Version kTrackerVersion = Version(0, 12, 3); | 39 | constexpr const Version kTrackerVersion = Version(2, 0, 2); |
| 40 | 40 | ||
| 41 | #endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file | 41 | #endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file |
| diff --git a/vcpkg.json b/vcpkg.json index e13d228..581a507 100644 --- a/vcpkg.json +++ b/vcpkg.json | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | { | 1 | { |
| 2 | "dependencies": [ | 2 | "dependencies": [ |
| 3 | "websocketpp", | ||
| 4 | "wxwidgets", | 3 | "wxwidgets", |
| 5 | "openssl", | 4 | "openssl", |
| 6 | "yaml-cpp", | 5 | "yaml-cpp", |
| diff --git a/vendor/vcpkg b/vendor/vcpkg | |||
| Subproject 3dd44b931481d7a8e9ba412621fa810232b6628 | Subproject df8bfe519564ae001903e5cdd32af0999531ef7 | ||
| diff --git a/vendor/websocketpp b/vendor/websocketpp new file mode 160000 | |||
| Subproject 1b11fd301531e6df35a6107c1e8665b1e77a2d8 | |||
