diff options
| -rw-r--r-- | CHANGELOG.md | 42 | ||||
| -rw-r--r-- | CMakeLists.txt | 5 | ||||
| -rw-r--r-- | VERSION | 2 | ||||
| -rw-r--r-- | assets/subway.yaml | 7 | ||||
| -rw-r--r-- | src/ap_state.cpp | 102 | ||||
| -rw-r--r-- | src/game_data.cpp | 80 | ||||
| -rw-r--r-- | src/game_data.h | 4 | ||||
| -rw-r--r-- | src/logger.cpp | 32 | ||||
| -rw-r--r-- | src/logger.h | 8 | ||||
| -rw-r--r-- | src/main.cpp | 12 | ||||
| -rw-r--r-- | src/subway_map.cpp | 77 | ||||
| -rw-r--r-- | src/tracker_frame.cpp | 2 | ||||
| -rw-r--r-- | src/tracker_state.cpp | 48 | ||||
| -rw-r--r-- | src/version.h | 9 | ||||
| -rw-r--r-- | vcpkg.json | 3 |
15 files changed, 293 insertions, 140 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index 37167d3..67e32ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
| @@ -1,5 +1,47 @@ | |||
| 1 | # lingo-ap-tracker Releases | 1 | # lingo-ap-tracker Releases |
| 2 | 2 | ||
| 3 | ## v0.10.6 - 2024-07-16 | ||
| 4 | |||
| 5 | - The status bar now shows the name and server for the connected slot. | ||
| 6 | - Fixed an issue with pilgrimage detection when paintings are shuffled and | ||
| 7 | paintings are not allowed for pilgrimage. | ||
| 8 | - Fixed an issue with pilgrimage starting from the wrong place when sunwarps are | ||
| 9 | shuffled. | ||
| 10 | - Fixed incorrect doors showing for sunwarps on subway map when sunwarps are | ||
| 11 | shuffled. | ||
| 12 | |||
| 13 | Download: | ||
| 14 | [lingo-ap-tracker-v0.10.6-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.10.6-win64.zip)<br/> | ||
| 15 | Source: [v0.10.6](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.10.6) | ||
| 16 | |||
| 17 | ## v0.10.5 - 2024-07-12 | ||
| 18 | |||
| 19 | - Increased length of connection history to 10. | ||
| 20 | - Fixed position of The Steady's paintings on the subway map. | ||
| 21 | |||
| 22 | Download: | ||
| 23 | [lingo-ap-tracker-v0.10.5-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.10.5-win64.zip)<br/> | ||
| 24 | Source: [v0.10.5](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.10.5) | ||
| 25 | |||
| 26 | ## v0.10.4 - 2024-07-01 | ||
| 27 | |||
| 28 | - Compatibility update for Archipelago 0.5.0 | ||
| 29 | |||
| 30 | Download: | ||
| 31 | [lingo-ap-tracker-v0.10.4-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.10.4-win64.zip)<br/> | ||
| 32 | Source: [v0.10.4](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.10.4) | ||
| 33 | |||
| 34 | ## v0.10.3 - 2024-06-10 | ||
| 35 | |||
| 36 | - Fixed crash that occurred when the Eye Wall painting was in your painting | ||
| 37 | mapping. | ||
| 38 | - Fixed getting bombarded with message boxes when the tracker crashes. | ||
| 39 | - Moved early color hallways painting to match the new in-game location. | ||
| 40 | |||
| 41 | Download: | ||
| 42 | [lingo-ap-tracker-v0.10.3-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.10.3-win64.zip)<br/> | ||
| 43 | Source: [v0.10.3](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.10.3) | ||
| 44 | |||
| 3 | ## v0.10.2 - 2024-06-09 | 45 | ## v0.10.2 - 2024-06-09 |
| 4 | 46 | ||
| 5 | - Fixed intermittent reachability detection issue. | 47 | - Fixed intermittent reachability detection issue. |
| diff --git a/CMakeLists.txt b/CMakeLists.txt index cd62c55..f9f1117 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -10,6 +10,7 @@ find_package(wxWidgets CONFIG REQUIRED) | |||
| 10 | find_package(OpenSSL REQUIRED) | 10 | find_package(OpenSSL REQUIRED) |
| 11 | find_package(yaml-cpp REQUIRED) | 11 | find_package(yaml-cpp REQUIRED) |
| 12 | find_package(websocketpp REQUIRED) | 12 | find_package(websocketpp REQUIRED) |
| 13 | find_package(fmt REQUIRED) | ||
| 13 | 14 | ||
| 14 | include_directories( | 15 | include_directories( |
| 15 | vendor/hkutil | 16 | vendor/hkutil |
| @@ -22,6 +23,7 @@ include_directories( | |||
| 22 | ${yaml-cpp_INCLUDE_DIRS} | 23 | ${yaml-cpp_INCLUDE_DIRS} |
| 23 | ${OpenSSL_INCLUDE_DIRS} | 24 | ${OpenSSL_INCLUDE_DIRS} |
| 24 | vendor/whereami | 25 | vendor/whereami |
| 26 | ${fmt_INCLUDE_DIRS} | ||
| 25 | vendor | 27 | vendor |
| 26 | ) | 28 | ) |
| 27 | 29 | ||
| @@ -45,8 +47,9 @@ add_executable(lingo_ap_tracker | |||
| 45 | "src/global.cpp" | 47 | "src/global.cpp" |
| 46 | "src/subway_map.cpp" | 48 | "src/subway_map.cpp" |
| 47 | "src/network_set.cpp" | 49 | "src/network_set.cpp" |
| 50 | "src/logger.cpp" | ||
| 48 | "vendor/whereami/whereami.c" | 51 | "vendor/whereami/whereami.c" |
| 49 | ) | 52 | ) |
| 50 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) | 53 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) |
| 51 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON) | 54 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON) |
| 52 | target_link_libraries(lingo_ap_tracker PRIVATE OpenSSL::SSL OpenSSL::Crypto websocketpp::websocketpp wx::core wx::base wx::net yaml-cpp::yaml-cpp) | 55 | 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) |
| diff --git a/VERSION b/VERSION index a30852f..7b63f4b 100644 --- a/VERSION +++ b/VERSION | |||
| @@ -1 +1 @@ | |||
| v0.10.2 \ No newline at end of file | v0.10.6 \ No newline at end of file | ||
| diff --git a/assets/subway.yaml b/assets/subway.yaml index 266b722..ede704c 100644 --- a/assets/subway.yaml +++ b/assets/subway.yaml | |||
| @@ -103,7 +103,7 @@ | |||
| 103 | room: Outside The Undeterred | 103 | room: Outside The Undeterred |
| 104 | door: Fours | 104 | door: Fours |
| 105 | - pos: [1276, 819] | 105 | - pos: [1276, 819] |
| 106 | room: Outside The Undeterred | 106 | room: Number Hunt |
| 107 | door: Eights | 107 | door: Eights |
| 108 | - pos: [1263, 867] | 108 | - pos: [1263, 867] |
| 109 | paintings: | 109 | paintings: |
| @@ -630,6 +630,7 @@ | |||
| 630 | - blue_ch | 630 | - blue_ch |
| 631 | - yellow_ch | 631 | - yellow_ch |
| 632 | - green_ch | 632 | - green_ch |
| 633 | - early_ch | ||
| 633 | - pos: [1567, 1264] | 634 | - pos: [1567, 1264] |
| 634 | tags: | 635 | tags: |
| 635 | - red_ch | 636 | - red_ch |
| @@ -686,8 +687,6 @@ | |||
| 686 | tags: | 687 | tags: |
| 687 | - blueman_courtyard | 688 | - blueman_courtyard |
| 688 | - blueman_starting | 689 | - blueman_starting |
| 689 | - pos: [60, 970] | ||
| 690 | special: early_color_hallways | ||
| 691 | - pos: [402, 1012] | 690 | - pos: [402, 1012] |
| 692 | room: Outside The Undeterred | 691 | room: Outside The Undeterred |
| 693 | door: Green Painting | 692 | door: Green Painting |
| @@ -822,7 +821,7 @@ | |||
| 822 | - pos: [1388, 1152] | 821 | - pos: [1388, 1152] |
| 823 | room: Bearer Side Area | 822 | room: Bearer Side Area |
| 824 | door: Shortcut to Tower | 823 | door: Shortcut to Tower |
| 825 | - pos: [1273, 1142] | 824 | - pos: [1273, 1442] |
| 826 | paintings: | 825 | paintings: |
| 827 | - pencil_painting5 | 826 | - pencil_painting5 |
| 828 | - pencil_painting4 | 827 | - pencil_painting4 |
| diff --git a/src/ap_state.cpp b/src/ap_state.cpp index a7565cf..d501e81 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #define _WEBSOCKETPP_CPP11_STRICT_ | 4 | #define _WEBSOCKETPP_CPP11_STRICT_ |
| 5 | #pragma comment(lib, "crypt32") | 5 | #pragma comment(lib, "crypt32") |
| 6 | 6 | ||
| 7 | #include <fmt/core.h> | ||
| 7 | #include <hkutil/string.h> | 8 | #include <hkutil/string.h> |
| 8 | 9 | ||
| 9 | #include <any> | 10 | #include <any> |
| @@ -21,6 +22,7 @@ | |||
| 21 | #include <tuple> | 22 | #include <tuple> |
| 22 | 23 | ||
| 23 | #include "game_data.h" | 24 | #include "game_data.h" |
| 25 | #include "logger.h" | ||
| 24 | #include "tracker_frame.h" | 26 | #include "tracker_frame.h" |
| 25 | #include "tracker_state.h" | 27 | #include "tracker_state.h" |
| 26 | 28 | ||
| @@ -76,7 +78,7 @@ struct APState { | |||
| 76 | 78 | ||
| 77 | void Connect(std::string server, std::string player, std::string password) { | 79 | void Connect(std::string server, std::string player, std::string password) { |
| 78 | if (!initialized) { | 80 | if (!initialized) { |
| 79 | wxLogVerbose("Initializing APState..."); | 81 | TrackerLog("Initializing APState..."); |
| 80 | 82 | ||
| 81 | std::thread([this]() { | 83 | std::thread([this]() { |
| 82 | for (;;) { | 84 | for (;;) { |
| @@ -92,14 +94,14 @@ struct APState { | |||
| 92 | }).detach(); | 94 | }).detach(); |
| 93 | 95 | ||
| 94 | for (int panel_id : GD_GetAchievementPanels()) { | 96 | for (int panel_id : GD_GetAchievementPanels()) { |
| 95 | tracked_data_storage_keys.push_back( | 97 | tracked_data_storage_keys.push_back(fmt::format( |
| 96 | "Achievement|" + GD_GetPanel(panel_id).achievement_name); | 98 | "Achievement|{}", GD_GetPanel(panel_id).achievement_name)); |
| 97 | } | 99 | } |
| 98 | 100 | ||
| 99 | for (const MapArea& map_area : GD_GetMapAreas()) { | 101 | for (const MapArea& map_area : GD_GetMapAreas()) { |
| 100 | for (const Location& location : map_area.locations) { | 102 | for (const Location& location : map_area.locations) { |
| 101 | tracked_data_storage_keys.push_back( | 103 | tracked_data_storage_keys.push_back( |
| 102 | "Hunt|" + std::to_string(location.ap_location_id)); | 104 | fmt::format("Hunt|{}", location.ap_location_id)); |
| 103 | } | 105 | } |
| 104 | } | 106 | } |
| 105 | 107 | ||
| @@ -110,10 +112,10 @@ struct APState { | |||
| 110 | } | 112 | } |
| 111 | 113 | ||
| 112 | tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); | 114 | tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); |
| 113 | wxLogStatus("Connecting to Archipelago server (%s)...", server); | 115 | TrackerLog(fmt::format("Connecting to Archipelago server ({})...", server)); |
| 114 | 116 | ||
| 115 | { | 117 | { |
| 116 | wxLogVerbose("Destroying old AP client..."); | 118 | TrackerLog("Destroying old AP client..."); |
| 117 | 119 | ||
| 118 | std::lock_guard client_guard(client_mutex); | 120 | std::lock_guard client_guard(client_mutex); |
| 119 | 121 | ||
| @@ -160,10 +162,10 @@ struct APState { | |||
| 160 | apclient->set_room_info_handler([this, player, password]() { | 162 | apclient->set_room_info_handler([this, player, password]() { |
| 161 | inventory.clear(); | 163 | inventory.clear(); |
| 162 | 164 | ||
| 163 | wxLogStatus("Connected to Archipelago server. Authenticating as %s %s", | 165 | TrackerLog(fmt::format( |
| 164 | player, | 166 | "Connected to Archipelago server. Authenticating as {} {}", player, |
| 165 | (password.empty() ? "without password" | 167 | (password.empty() ? "without password" |
| 166 | : "with password " + password)); | 168 | : "with password " + password))); |
| 167 | tracker_frame->SetStatusMessage( | 169 | tracker_frame->SetStatusMessage( |
| 168 | "Connected to Archipelago server. Authenticating..."); | 170 | "Connected to Archipelago server. Authenticating..."); |
| 169 | 171 | ||
| @@ -175,7 +177,7 @@ struct APState { | |||
| 175 | [this](const std::list<int64_t>& locations) { | 177 | [this](const std::list<int64_t>& locations) { |
| 176 | for (const int64_t location_id : locations) { | 178 | for (const int64_t location_id : locations) { |
| 177 | checked_locations.insert(location_id); | 179 | checked_locations.insert(location_id); |
| 178 | wxLogVerbose("Location: %lld", location_id); | 180 | TrackerLog(fmt::format("Location: {}", location_id)); |
| 179 | } | 181 | } |
| 180 | 182 | ||
| 181 | RefreshTracker(false); | 183 | RefreshTracker(false); |
| @@ -184,14 +186,14 @@ struct APState { | |||
| 184 | apclient->set_slot_disconnected_handler([this]() { | 186 | apclient->set_slot_disconnected_handler([this]() { |
| 185 | tracker_frame->SetStatusMessage( | 187 | tracker_frame->SetStatusMessage( |
| 186 | "Disconnected from Archipelago. Attempting to reconnect..."); | 188 | "Disconnected from Archipelago. Attempting to reconnect..."); |
| 187 | wxLogStatus( | 189 | TrackerLog( |
| 188 | "Slot disconnected from Archipelago. Attempting to reconnect..."); | 190 | "Slot disconnected from Archipelago. Attempting to reconnect..."); |
| 189 | }); | 191 | }); |
| 190 | 192 | ||
| 191 | apclient->set_socket_disconnected_handler([this]() { | 193 | apclient->set_socket_disconnected_handler([this]() { |
| 192 | tracker_frame->SetStatusMessage( | 194 | tracker_frame->SetStatusMessage( |
| 193 | "Disconnected from Archipelago. Attempting to reconnect..."); | 195 | "Disconnected from Archipelago. Attempting to reconnect..."); |
| 194 | wxLogStatus( | 196 | TrackerLog( |
| 195 | "Socket disconnected from Archipelago. Attempting to reconnect..."); | 197 | "Socket disconnected from Archipelago. Attempting to reconnect..."); |
| 196 | }); | 198 | }); |
| 197 | 199 | ||
| @@ -199,7 +201,7 @@ struct APState { | |||
| 199 | [this](const std::list<APClient::NetworkItem>& items) { | 201 | [this](const std::list<APClient::NetworkItem>& items) { |
| 200 | for (const APClient::NetworkItem& item : items) { | 202 | for (const APClient::NetworkItem& item : items) { |
| 201 | inventory[item.item]++; | 203 | inventory[item.item]++; |
| 202 | wxLogVerbose("Item: %lld", item.item); | 204 | TrackerLog(fmt::format("Item: {}", item.item)); |
| 203 | } | 205 | } |
| 204 | 206 | ||
| 205 | RefreshTracker(false); | 207 | RefreshTracker(false); |
| @@ -221,13 +223,15 @@ struct APState { | |||
| 221 | RefreshTracker(false); | 223 | RefreshTracker(false); |
| 222 | }); | 224 | }); |
| 223 | 225 | ||
| 224 | apclient->set_slot_connected_handler([this, &connection_mutex]( | 226 | apclient->set_slot_connected_handler([this, player, server, |
| 227 | &connection_mutex]( | ||
| 225 | const nlohmann::json& slot_data) { | 228 | const nlohmann::json& slot_data) { |
| 226 | tracker_frame->SetStatusMessage("Connected to Archipelago!"); | 229 | tracker_frame->SetStatusMessage( |
| 227 | wxLogStatus("Connected to Archipelago!"); | 230 | fmt::format("Connected to Archipelago! ({}@{})", player, server)); |
| 231 | TrackerLog("Connected to Archipelago!"); | ||
| 228 | 232 | ||
| 229 | data_storage_prefix = | 233 | data_storage_prefix = |
| 230 | "Lingo_" + std::to_string(apclient->get_player_number()) + "_"; | 234 | fmt::format("Lingo_{}_", apclient->get_player_number()); |
| 231 | door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>(); | 235 | door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>(); |
| 232 | if (slot_data.contains("group_doors")) { | 236 | if (slot_data.contains("group_doors")) { |
| 233 | group_doors = slot_data.contains("group_doors") && | 237 | group_doors = slot_data.contains("group_doors") && |
| @@ -290,18 +294,18 @@ struct APState { | |||
| 290 | corrected_keys.push_back(data_storage_prefix + key); | 294 | corrected_keys.push_back(data_storage_prefix + key); |
| 291 | } | 295 | } |
| 292 | 296 | ||
| 293 | { | 297 | victory_data_storage_key = |
| 294 | std::ostringstream vdsks; | 298 | fmt::format("_read_client_status_{}_{}", apclient->get_team_number(), |
| 295 | vdsks << "_read_client_status_" << apclient->get_team_number() << "_" | 299 | apclient->get_player_number()); |
| 296 | << apclient->get_player_number(); | ||
| 297 | victory_data_storage_key = vdsks.str(); | ||
| 298 | } | ||
| 299 | 300 | ||
| 300 | corrected_keys.push_back(victory_data_storage_key); | 301 | corrected_keys.push_back(victory_data_storage_key); |
| 301 | 302 | ||
| 302 | apclient->Get(corrected_keys); | 303 | apclient->Get(corrected_keys); |
| 303 | apclient->SetNotify(corrected_keys); | 304 | apclient->SetNotify(corrected_keys); |
| 304 | 305 | ||
| 306 | ResetReachabilityRequirements(); | ||
| 307 | RefreshTracker(true); | ||
| 308 | |||
| 305 | { | 309 | { |
| 306 | std::lock_guard connection_lock(connection_mutex); | 310 | std::lock_guard connection_lock(connection_mutex); |
| 307 | if (!has_connection_result) { | 311 | if (!has_connection_result) { |
| @@ -346,7 +350,7 @@ struct APState { | |||
| 346 | } | 350 | } |
| 347 | 351 | ||
| 348 | std::string full_message = hatkirby::implode(error_messages, " "); | 352 | std::string full_message = hatkirby::implode(error_messages, " "); |
| 349 | wxLogError(wxString(full_message)); | 353 | TrackerLog(full_message); |
| 350 | 354 | ||
| 351 | wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); | 355 | wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); |
| 352 | }); | 356 | }); |
| @@ -368,7 +372,7 @@ struct APState { | |||
| 368 | DestroyClient(); | 372 | DestroyClient(); |
| 369 | 373 | ||
| 370 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); | 374 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); |
| 371 | wxLogStatus("Timeout while connecting to Archipelago server."); | 375 | TrackerLog("Timeout while connecting to Archipelago server."); |
| 372 | wxMessageBox("Timeout while connecting to Archipelago server.", | 376 | wxMessageBox("Timeout while connecting to Archipelago server.", |
| 373 | "Connection failed", wxOK | wxICON_ERROR); | 377 | "Connection failed", wxOK | wxICON_ERROR); |
| 374 | 378 | ||
| @@ -387,9 +391,6 @@ struct APState { | |||
| 387 | } | 391 | } |
| 388 | 392 | ||
| 389 | if (connected) { | 393 | if (connected) { |
| 390 | ResetReachabilityRequirements(); | ||
| 391 | RefreshTracker(true); | ||
| 392 | } else { | ||
| 393 | client_active = false; | 394 | client_active = false; |
| 394 | } | 395 | } |
| 395 | } | 396 | } |
| @@ -397,11 +398,12 @@ struct APState { | |||
| 397 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { | 398 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { |
| 398 | if (value.is_boolean()) { | 399 | if (value.is_boolean()) { |
| 399 | data_storage[key] = value.get<bool>(); | 400 | data_storage[key] = value.get<bool>(); |
| 400 | wxLogVerbose("Data storage %s retrieved as %s", key, | 401 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, |
| 401 | (value.get<bool>() ? "true" : "false")); | 402 | (value.get<bool>() ? "true" : "false"))); |
| 402 | } else if (value.is_number()) { | 403 | } else if (value.is_number()) { |
| 403 | data_storage[key] = value.get<int>(); | 404 | data_storage[key] = value.get<int>(); |
| 404 | wxLogVerbose("Data storage %s retrieved as %d", key, value.get<int>()); | 405 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, |
| 406 | value.get<int>())); | ||
| 405 | } else if (value.is_object()) { | 407 | } else if (value.is_object()) { |
| 406 | if (key.ends_with("PlayerPos")) { | 408 | if (key.ends_with("PlayerPos")) { |
| 407 | auto map_value = value.get<std::map<std::string, int>>(); | 409 | auto map_value = value.get<std::map<std::string, int>>(); |
| @@ -410,7 +412,7 @@ struct APState { | |||
| 410 | data_storage[key] = value.get<std::map<std::string, int>>(); | 412 | data_storage[key] = value.get<std::map<std::string, int>>(); |
| 411 | } | 413 | } |
| 412 | 414 | ||
| 413 | wxLogVerbose("Data storage %s retrieved as dictionary", key); | 415 | TrackerLog(fmt::format("Data storage {} retrieved as dictionary", key)); |
| 414 | } else if (value.is_null()) { | 416 | } else if (value.is_null()) { |
| 415 | if (key.ends_with("PlayerPos")) { | 417 | if (key.ends_with("PlayerPos")) { |
| 416 | player_pos = std::nullopt; | 418 | player_pos = std::nullopt; |
| @@ -418,7 +420,7 @@ struct APState { | |||
| 418 | data_storage.erase(key); | 420 | data_storage.erase(key); |
| 419 | } | 421 | } |
| 420 | 422 | ||
| 421 | wxLogVerbose("Data storage %s retrieved as null", key); | 423 | TrackerLog(fmt::format("Data storage {} retrieved as null", key)); |
| 422 | } else if (value.is_array()) { | 424 | } else if (value.is_array()) { |
| 423 | auto list_value = value.get<std::vector<std::string>>(); | 425 | auto list_value = value.get<std::vector<std::string>>(); |
| 424 | 426 | ||
| @@ -429,8 +431,8 @@ struct APState { | |||
| 429 | data_storage[key] = list_value; | 431 | data_storage[key] = list_value; |
| 430 | } | 432 | } |
| 431 | 433 | ||
| 432 | wxLogVerbose("Data storage %s retrieved as list: [%s]", key, | 434 | TrackerLog(fmt::format("Data storage {} retrieved as list: [{}]", key, |
| 433 | hatkirby::implode(list_value, ", ")); | 435 | hatkirby::implode(list_value, ", "))); |
| 434 | } | 436 | } |
| 435 | } | 437 | } |
| 436 | 438 | ||
| @@ -440,7 +442,7 @@ struct APState { | |||
| 440 | 442 | ||
| 441 | bool HasCheckedHuntPanel(int location_id) { | 443 | bool HasCheckedHuntPanel(int location_id) { |
| 442 | std::string key = | 444 | std::string key = |
| 443 | data_storage_prefix + "Hunt|" + std::to_string(location_id); | 445 | fmt::format("{}Hunt|{}", data_storage_prefix, location_id); |
| 444 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); | 446 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); |
| 445 | } | 447 | } |
| 446 | 448 | ||
| @@ -449,12 +451,13 @@ struct APState { | |||
| 449 | } | 451 | } |
| 450 | 452 | ||
| 451 | bool HasAchievement(const std::string& name) { | 453 | bool HasAchievement(const std::string& name) { |
| 452 | std::string key = data_storage_prefix + "Achievement|" + name; | 454 | std::string key = |
| 455 | fmt::format("{}Achievement|{}", data_storage_prefix, name); | ||
| 453 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); | 456 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); |
| 454 | } | 457 | } |
| 455 | 458 | ||
| 456 | const std::set<std::string>& GetCheckedPaintings() { | 459 | const std::set<std::string>& GetCheckedPaintings() { |
| 457 | std::string key = data_storage_prefix + "Paintings"; | 460 | std::string key = fmt::format("{}Paintings", data_storage_prefix); |
| 458 | if (!data_storage.count(key)) { | 461 | if (!data_storage.count(key)) { |
| 459 | data_storage[key] = std::set<std::string>(); | 462 | data_storage[key] = std::set<std::string>(); |
| 460 | } | 463 | } |
| @@ -471,7 +474,7 @@ struct APState { | |||
| 471 | } | 474 | } |
| 472 | 475 | ||
| 473 | void RefreshTracker(bool reset) { | 476 | void RefreshTracker(bool reset) { |
| 474 | wxLogVerbose("Refreshing display..."); | 477 | TrackerLog("Refreshing display..."); |
| 475 | 478 | ||
| 476 | RecalculateReachability(); | 479 | RecalculateReachability(); |
| 477 | 480 | ||
| @@ -485,7 +488,7 @@ struct APState { | |||
| 485 | int64_t GetItemId(const std::string& item_name) { | 488 | int64_t GetItemId(const std::string& item_name) { |
| 486 | int64_t ap_id = apclient->get_item_id(item_name); | 489 | int64_t ap_id = apclient->get_item_id(item_name); |
| 487 | if (ap_id == APClient::INVALID_NAME_ID) { | 490 | if (ap_id == APClient::INVALID_NAME_ID) { |
| 488 | wxLogError("Could not find AP item ID for %s", item_name); | 491 | TrackerLog(fmt::format("Could not find AP item ID for {}", item_name)); |
| 489 | } | 492 | } |
| 490 | 493 | ||
| 491 | return ap_id; | 494 | return ap_id; |
| @@ -564,16 +567,27 @@ int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } | |||
| 564 | int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } | 567 | int AP_GetLevel2Requirement() { return GetState().level_2_requirement; } |
| 565 | 568 | ||
| 566 | bool AP_IsLocationVisible(int classification) { | 569 | bool AP_IsLocationVisible(int classification) { |
| 570 | int world_state = 0; | ||
| 571 | |||
| 567 | switch (GetState().location_checks) { | 572 | switch (GetState().location_checks) { |
| 568 | case kNORMAL_LOCATIONS: | 573 | case kNORMAL_LOCATIONS: |
| 569 | return classification & kLOCATION_NORMAL; | 574 | world_state = kLOCATION_NORMAL; |
| 575 | break; | ||
| 570 | case kREDUCED_LOCATIONS: | 576 | case kREDUCED_LOCATIONS: |
| 571 | return classification & kLOCATION_REDUCED; | 577 | world_state = kLOCATION_REDUCED; |
| 578 | break; | ||
| 572 | case kPANELSANITY: | 579 | case kPANELSANITY: |
| 573 | return classification & kLOCATION_INSANITY; | 580 | world_state = kLOCATION_INSANITY; |
| 581 | break; | ||
| 574 | default: | 582 | default: |
| 575 | return false; | 583 | return false; |
| 576 | } | 584 | } |
| 585 | |||
| 586 | if (GetState().door_shuffle_mode && !GetState().early_color_hallways) { | ||
| 587 | world_state |= kLOCATION_SMALL_SPHERE_ONE; | ||
| 588 | } | ||
| 589 | |||
| 590 | return (world_state & classification); | ||
| 577 | } | 591 | } |
| 578 | 592 | ||
| 579 | VictoryCondition AP_GetVictoryCondition() { | 593 | VictoryCondition AP_GetVictoryCondition() { |
| diff --git a/src/game_data.cpp b/src/game_data.cpp index 759eb0c..39ea360 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp | |||
| @@ -1,11 +1,6 @@ | |||
| 1 | #include "game_data.h" | 1 | #include "game_data.h" |
| 2 | 2 | ||
| 3 | #include <wx/wxprec.h> | 3 | #include <fmt/core.h> |
| 4 | |||
| 5 | #ifndef WX_PRECOMP | ||
| 6 | #include <wx/wx.h> | ||
| 7 | #endif | ||
| 8 | |||
| 9 | #include <hkutil/string.h> | 4 | #include <hkutil/string.h> |
| 10 | #include <yaml-cpp/yaml.h> | 5 | #include <yaml-cpp/yaml.h> |
| 11 | 6 | ||
| @@ -13,6 +8,7 @@ | |||
| 13 | #include <sstream> | 8 | #include <sstream> |
| 14 | 9 | ||
| 15 | #include "global.h" | 10 | #include "global.h" |
| 11 | #include "logger.h" | ||
| 16 | 12 | ||
| 17 | namespace { | 13 | namespace { |
| 18 | 14 | ||
| @@ -36,7 +32,7 @@ LingoColor GetColorForString(const std::string &str) { | |||
| 36 | } else if (str == "purple") { | 32 | } else if (str == "purple") { |
| 37 | return LingoColor::kPurple; | 33 | return LingoColor::kPurple; |
| 38 | } else { | 34 | } else { |
| 39 | wxLogError("Invalid color: %s", str); | 35 | TrackerLog(fmt::format("Invalid color: {}", str)); |
| 40 | 36 | ||
| 41 | return LingoColor::kNone; | 37 | return LingoColor::kNone; |
| 42 | } | 38 | } |
| @@ -90,7 +86,7 @@ struct GameData { | |||
| 90 | ap_id_by_color_[GetColorForString(input_name)] = | 86 | ap_id_by_color_[GetColorForString(input_name)] = |
| 91 | ids_config["special_items"][color_name].as<int>(); | 87 | ids_config["special_items"][color_name].as<int>(); |
| 92 | } else { | 88 | } else { |
| 93 | wxLogError("Missing AP item ID for color %s", color_name); | 89 | TrackerLog(fmt::format("Missing AP item ID for color {}", color_name)); |
| 94 | } | 90 | } |
| 95 | }; | 91 | }; |
| 96 | 92 | ||
| @@ -115,6 +111,7 @@ struct GameData { | |||
| 115 | auto process_single_entrance = | 111 | auto process_single_entrance = |
| 116 | [this, room_id, from_room_id](const YAML::Node &option) { | 112 | [this, room_id, from_room_id](const YAML::Node &option) { |
| 117 | Exit exit_obj; | 113 | Exit exit_obj; |
| 114 | exit_obj.source_room = from_room_id; | ||
| 118 | exit_obj.destination_room = room_id; | 115 | exit_obj.destination_room = room_id; |
| 119 | 116 | ||
| 120 | if (option["door"]) { | 117 | if (option["door"]) { |
| @@ -149,7 +146,7 @@ struct GameData { | |||
| 149 | switch (entrance_it.second.Type()) { | 146 | switch (entrance_it.second.Type()) { |
| 150 | case YAML::NodeType::Scalar: { | 147 | case YAML::NodeType::Scalar: { |
| 151 | // This is just "true". | 148 | // This is just "true". |
| 152 | rooms_[from_room_id].exits.push_back({.destination_room = room_id}); | 149 | rooms_[from_room_id].exits.push_back({.source_room = from_room_id, .destination_room = room_id}); |
| 153 | break; | 150 | break; |
| 154 | } | 151 | } |
| 155 | case YAML::NodeType::Map: { | 152 | case YAML::NodeType::Map: { |
| @@ -167,7 +164,8 @@ struct GameData { | |||
| 167 | // This shouldn't happen. | 164 | // This shouldn't happen. |
| 168 | std::ostringstream formatted; | 165 | std::ostringstream formatted; |
| 169 | formatted << entrance_it; | 166 | formatted << entrance_it; |
| 170 | wxLogError("Error reading game data: %s", formatted.str()); | 167 | TrackerLog( |
| 168 | fmt::format("Error reading game data: {}", formatted.str())); | ||
| 171 | break; | 169 | break; |
| 172 | } | 170 | } |
| 173 | } | 171 | } |
| @@ -292,8 +290,9 @@ struct GameData { | |||
| 292 | [panels_[panel_id].name] | 290 | [panels_[panel_id].name] |
| 293 | .as<int>(); | 291 | .as<int>(); |
| 294 | } else { | 292 | } else { |
| 295 | wxLogError("Missing AP location ID for panel %s - %s", | 293 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", |
| 296 | rooms_[room_id].name, panels_[panel_id].name); | 294 | rooms_[room_id].name, |
| 295 | panels_[panel_id].name)); | ||
| 297 | } | 296 | } |
| 298 | } | 297 | } |
| 299 | } | 298 | } |
| @@ -356,8 +355,9 @@ struct GameData { | |||
| 356 | [doors_[door_id].name]["item"] | 355 | [doors_[door_id].name]["item"] |
| 357 | .as<int>(); | 356 | .as<int>(); |
| 358 | } else { | 357 | } else { |
| 359 | wxLogError("Missing AP item ID for door %s - %s", | 358 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", |
| 360 | rooms_[room_id].name, doors_[door_id].name); | 359 | rooms_[room_id].name, |
| 360 | doors_[door_id].name)); | ||
| 361 | } | 361 | } |
| 362 | } | 362 | } |
| 363 | 363 | ||
| @@ -371,8 +371,8 @@ struct GameData { | |||
| 371 | ids_config["door_groups"][doors_[door_id].group_name] | 371 | ids_config["door_groups"][doors_[door_id].group_name] |
| 372 | .as<int>(); | 372 | .as<int>(); |
| 373 | } else { | 373 | } else { |
| 374 | wxLogError("Missing AP item ID for door group %s", | 374 | TrackerLog(fmt::format("Missing AP item ID for door group {}", |
| 375 | doors_[door_id].group_name); | 375 | doors_[door_id].group_name)); |
| 376 | } | 376 | } |
| 377 | } | 377 | } |
| 378 | 378 | ||
| @@ -382,11 +382,11 @@ struct GameData { | |||
| 382 | } else if (!door_it.second["skip_location"] && | 382 | } else if (!door_it.second["skip_location"] && |
| 383 | !door_it.second["event"]) { | 383 | !door_it.second["event"]) { |
| 384 | if (has_external_panels) { | 384 | if (has_external_panels) { |
| 385 | wxLogError( | 385 | TrackerLog(fmt::format( |
| 386 | "%s - %s has panels from other rooms but does not have an " | 386 | "{} - {} has panels from other rooms but does not have an " |
| 387 | "explicit location name and is not marked skip_location or " | 387 | "explicit location name and is not marked skip_location or " |
| 388 | "event", | 388 | "event", |
| 389 | rooms_[room_id].name, doors_[door_id].name); | 389 | rooms_[room_id].name, doors_[door_id].name)); |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | doors_[door_id].location_name = | 392 | doors_[door_id].location_name = |
| @@ -406,8 +406,9 @@ struct GameData { | |||
| 406 | [doors_[door_id].name]["location"] | 406 | [doors_[door_id].name]["location"] |
| 407 | .as<int>(); | 407 | .as<int>(); |
| 408 | } else { | 408 | } else { |
| 409 | wxLogError("Missing AP location ID for door %s - %s", | 409 | TrackerLog(fmt::format("Missing AP location ID for door {} - {}", |
| 410 | rooms_[room_id].name, doors_[door_id].name); | 410 | rooms_[room_id].name, |
| 411 | doors_[door_id].name)); | ||
| 411 | } | 412 | } |
| 412 | } | 413 | } |
| 413 | 414 | ||
| @@ -524,8 +525,8 @@ struct GameData { | |||
| 524 | progressive_item_id = | 525 | progressive_item_id = |
| 525 | ids_config["progression"][progressive_item_name].as<int>(); | 526 | ids_config["progression"][progressive_item_name].as<int>(); |
| 526 | } else { | 527 | } else { |
| 527 | wxLogError("Missing AP item ID for progressive item %s", | 528 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", |
| 528 | progressive_item_name); | 529 | progressive_item_name)); |
| 529 | } | 530 | } |
| 530 | 531 | ||
| 531 | if (progression_it.second["doors"]) { | 532 | if (progression_it.second["doors"]) { |
| @@ -625,17 +626,20 @@ struct GameData { | |||
| 625 | } | 626 | } |
| 626 | } | 627 | } |
| 627 | 628 | ||
| 629 | if (room_name == "Starting Room") { | ||
| 630 | classification |= kLOCATION_SMALL_SPHERE_ONE; | ||
| 631 | } | ||
| 632 | |||
| 628 | int area_id = AddOrGetArea(area_name); | 633 | int area_id = AddOrGetArea(area_name); |
| 629 | MapArea &map_area = map_areas_[area_id]; | 634 | MapArea &map_area = map_areas_[area_id]; |
| 630 | // room field should be the original room ID | 635 | // room field should be the original room ID |
| 631 | map_area.locations.push_back( | 636 | map_area.locations.push_back({.name = section_name, |
| 632 | {.name = section_name, | 637 | .ap_location_name = location_name, |
| 633 | .ap_location_name = location_name, | 638 | .ap_location_id = panel.ap_location_id, |
| 634 | .ap_location_id = panel.ap_location_id, | 639 | .room = panel.room, |
| 635 | .room = panel.room, | 640 | .panels = {panel.id}, |
| 636 | .panels = {panel.id}, | 641 | .classification = classification, |
| 637 | .classification = classification, | 642 | .hunt = panel.hunt}); |
| 638 | .hunt = panel.hunt}); | ||
| 639 | locations_by_name[location_name] = {area_id, | 643 | locations_by_name[location_name] = {area_id, |
| 640 | map_area.locations.size() - 1}; | 644 | map_area.locations.size() - 1}; |
| 641 | } | 645 | } |
| @@ -713,7 +717,7 @@ struct GameData { | |||
| 713 | 717 | ||
| 714 | // Report errors. | 718 | // Report errors. |
| 715 | for (const std::string &area : malconfigured_areas_) { | 719 | for (const std::string &area : malconfigured_areas_) { |
| 716 | wxLogError("Area data not found for: %s", area); | 720 | TrackerLog(fmt::format("Area data not found for: {}", area)); |
| 717 | } | 721 | } |
| 718 | 722 | ||
| 719 | // Read in subway items. | 723 | // Read in subway items. |
| @@ -784,7 +788,7 @@ struct GameData { | |||
| 784 | 788 | ||
| 785 | for (const auto &[tag, items] : subway_tags) { | 789 | for (const auto &[tag, items] : subway_tags) { |
| 786 | if (items.size() == 1) { | 790 | if (items.size() == 1) { |
| 787 | wxLogWarning("Singleton subway item tag: %s", tag); | 791 | TrackerLog(fmt::format("Singleton subway item tag: {}", tag)); |
| 788 | } | 792 | } |
| 789 | } | 793 | } |
| 790 | } | 794 | } |
| @@ -930,13 +934,11 @@ const SubwayItem &GD_GetSubwayItem(int id) { | |||
| 930 | return GetState().subway_items_.at(id); | 934 | return GetState().subway_items_.at(id); |
| 931 | } | 935 | } |
| 932 | 936 | ||
| 933 | int GD_GetSubwayItemForPainting(const std::string &painting_id) { | 937 | std::optional<int> GD_GetSubwayItemForPainting(const std::string &painting_id) { |
| 934 | #ifndef NDEBUG | 938 | if (GetState().subway_item_by_painting_.count(painting_id)) { |
| 935 | if (!GetState().subway_item_by_painting_.count(painting_id)) { | 939 | return GetState().subway_item_by_painting_.at(painting_id); |
| 936 | wxLogError("No subway item for painting %s", painting_id); | ||
| 937 | } | 940 | } |
| 938 | #endif | 941 | return std::nullopt; |
| 939 | return GetState().subway_item_by_painting_.at(painting_id); | ||
| 940 | } | 942 | } |
| 941 | 943 | ||
| 942 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { | 944 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { |
| diff --git a/src/game_data.h b/src/game_data.h index aca4c3d..197585c 100644 --- a/src/game_data.h +++ b/src/game_data.h | |||
| @@ -22,6 +22,7 @@ enum class LingoColor { | |||
| 22 | constexpr int kLOCATION_NORMAL = 1; | 22 | constexpr int kLOCATION_NORMAL = 1; |
| 23 | constexpr int kLOCATION_REDUCED = 2; | 23 | constexpr int kLOCATION_REDUCED = 2; |
| 24 | constexpr int kLOCATION_INSANITY = 4; | 24 | constexpr int kLOCATION_INSANITY = 4; |
| 25 | constexpr int kLOCATION_SMALL_SPHERE_ONE = 8; | ||
| 25 | 26 | ||
| 26 | enum class EntranceType { | 27 | enum class EntranceType { |
| 27 | kNormal, | 28 | kNormal, |
| @@ -89,6 +90,7 @@ struct PanelDoor { | |||
| 89 | }; | 90 | }; |
| 90 | 91 | ||
| 91 | struct Exit { | 92 | struct Exit { |
| 93 | int source_room; | ||
| 92 | int destination_room; | 94 | int destination_room; |
| 93 | std::optional<int> door; | 95 | std::optional<int> door; |
| 94 | EntranceType type = EntranceType::kNormal; | 96 | EntranceType type = EntranceType::kNormal; |
| @@ -172,7 +174,7 @@ const std::vector<int>& GD_GetSunwarpDoors(); | |||
| 172 | int GD_GetRoomForSunwarp(int index); | 174 | int GD_GetRoomForSunwarp(int index); |
| 173 | const std::vector<SubwayItem>& GD_GetSubwayItems(); | 175 | const std::vector<SubwayItem>& GD_GetSubwayItems(); |
| 174 | const SubwayItem& GD_GetSubwayItem(int id); | 176 | const SubwayItem& GD_GetSubwayItem(int id); |
| 175 | int GD_GetSubwayItemForPainting(const std::string& painting_id); | 177 | std::optional<int> GD_GetSubwayItemForPainting(const std::string& painting_id); |
| 176 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp& sunwarp); | 178 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp& sunwarp); |
| 177 | 179 | ||
| 178 | #endif /* end of include guard: GAME_DATA_H_9C42AC51 */ | 180 | #endif /* end of include guard: GAME_DATA_H_9C42AC51 */ |
| diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 0000000..09fc331 --- /dev/null +++ b/src/logger.cpp | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | #include "logger.h" | ||
| 2 | |||
| 3 | #include <chrono> | ||
| 4 | #include <fstream> | ||
| 5 | #include <mutex> | ||
| 6 | |||
| 7 | #include "global.h" | ||
| 8 | |||
| 9 | namespace { | ||
| 10 | |||
| 11 | class Logger { | ||
| 12 | public: | ||
| 13 | Logger() : logfile_(GetAbsolutePath("debug.log")) {} | ||
| 14 | |||
| 15 | void LogLine(const std::string& text) { | ||
| 16 | std::lock_guard guard(file_mutex_); | ||
| 17 | logfile_ << "[" << std::chrono::system_clock::now() << "] " << text | ||
| 18 | << std::endl; | ||
| 19 | logfile_.flush(); | ||
| 20 | } | ||
| 21 | |||
| 22 | private: | ||
| 23 | std::ofstream logfile_; | ||
| 24 | std::mutex file_mutex_; | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace | ||
| 28 | |||
| 29 | void TrackerLog(std::string text) { | ||
| 30 | static Logger* instance = new Logger(); | ||
| 31 | instance->LogLine(text); | ||
| 32 | } | ||
| diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 0000000..a27839f --- /dev/null +++ b/src/logger.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef LOGGER_H_9BDD07EA | ||
| 2 | #define LOGGER_H_9BDD07EA | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | |||
| 6 | void TrackerLog(std::string message); | ||
| 7 | |||
| 8 | #endif /* end of include guard: LOGGER_H_9BDD07EA */ | ||
| diff --git a/src/main.cpp b/src/main.cpp index b327b25..1d7cc9e 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -4,25 +4,13 @@ | |||
| 4 | #include <wx/wx.h> | 4 | #include <wx/wx.h> |
| 5 | #endif | 5 | #endif |
| 6 | 6 | ||
| 7 | #include <fstream> | ||
| 8 | |||
| 9 | #include "global.h" | 7 | #include "global.h" |
| 10 | #include "tracker_config.h" | 8 | #include "tracker_config.h" |
| 11 | #include "tracker_frame.h" | 9 | #include "tracker_frame.h" |
| 12 | 10 | ||
| 13 | static std::ofstream* logfile; | ||
| 14 | |||
| 15 | class TrackerApp : public wxApp { | 11 | class TrackerApp : public wxApp { |
| 16 | public: | 12 | public: |
| 17 | virtual bool OnInit() { | 13 | virtual bool OnInit() { |
| 18 | logfile = new std::ofstream(GetAbsolutePath("debug.log")); | ||
| 19 | wxLog::SetActiveTarget(new wxLogStream(logfile)); | ||
| 20 | wxLog::SetVerbose(true); | ||
| 21 | |||
| 22 | #ifndef NDEBUG | ||
| 23 | wxLog::SetActiveTarget(new wxLogWindow(nullptr, "Debug Log")); | ||
| 24 | #endif | ||
| 25 | |||
| 26 | GetTrackerConfig().Load(); | 14 | GetTrackerConfig().Load(); |
| 27 | 15 | ||
| 28 | TrackerFrame *frame = new TrackerFrame(); | 16 | TrackerFrame *frame = new TrackerFrame(); |
| diff --git a/src/subway_map.cpp b/src/subway_map.cpp index 408c4f0..044e6fa 100644 --- a/src/subway_map.cpp +++ b/src/subway_map.cpp | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #include "subway_map.h" | 1 | #include "subway_map.h" |
| 2 | 2 | ||
| 3 | #include <fmt/core.h> | ||
| 3 | #include <wx/dcbuffer.h> | 4 | #include <wx/dcbuffer.h> |
| 4 | #include <wx/dcgraph.h> | 5 | #include <wx/dcgraph.h> |
| 5 | 6 | ||
| @@ -15,6 +16,29 @@ constexpr int OWL_ACTUAL_SIZE = 32; | |||
| 15 | 16 | ||
| 16 | enum class ItemDrawType { kNone, kBox, kOwl }; | 17 | enum class ItemDrawType { kNone, kBox, kOwl }; |
| 17 | 18 | ||
| 19 | namespace { | ||
| 20 | |||
| 21 | std::optional<int> GetRealSubwayDoor(const SubwayItem subway_item) { | ||
| 22 | std::optional<int> subway_door = subway_item.door; | ||
| 23 | if (AP_IsSunwarpShuffle() && subway_item.sunwarp && | ||
| 24 | subway_item.sunwarp->type != SubwaySunwarpType::kFinal) { | ||
| 25 | int sunwarp_index = subway_item.sunwarp->dots - 1; | ||
| 26 | if (subway_item.sunwarp->type == SubwaySunwarpType::kExit) { | ||
| 27 | sunwarp_index += 6; | ||
| 28 | } | ||
| 29 | |||
| 30 | for (const auto &[start_index, mapping] : AP_GetSunwarpMapping()) { | ||
| 31 | if (start_index == sunwarp_index || mapping.exit_index == sunwarp_index) { | ||
| 32 | subway_door = GD_GetSunwarpDoors().at(mapping.dots - 1); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return subway_door; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | } // namespace | ||
| 41 | |||
| 18 | SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { | 42 | SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { |
| 19 | SetBackgroundStyle(wxBG_STYLE_PAINT); | 43 | SetBackgroundStyle(wxBG_STYLE_PAINT); |
| 20 | 44 | ||
| @@ -70,9 +94,8 @@ void SubwayMap::OnConnect() { | |||
| 70 | std::map<std::string, std::vector<int>> tagged; | 94 | std::map<std::string, std::vector<int>> tagged; |
| 71 | for (const SubwayItem &subway_item : GD_GetSubwayItems()) { | 95 | for (const SubwayItem &subway_item : GD_GetSubwayItems()) { |
| 72 | if (AP_HasEarlyColorHallways() && | 96 | if (AP_HasEarlyColorHallways() && |
| 73 | (subway_item.special == "starting_room_paintings" || | 97 | subway_item.special == "starting_room_paintings") { |
| 74 | subway_item.special == "early_color_hallways")) { | 98 | tagged["early_ch"].push_back(subway_item.id); |
| 75 | tagged["early_color_hallways"].push_back(subway_item.id); | ||
| 76 | } | 99 | } |
| 77 | 100 | ||
| 78 | if (AP_IsPaintingShuffle() && !subway_item.paintings.empty()) { | 101 | if (AP_IsPaintingShuffle() && !subway_item.paintings.empty()) { |
| @@ -85,10 +108,8 @@ void SubwayMap::OnConnect() { | |||
| 85 | 108 | ||
| 86 | if (!AP_IsSunwarpShuffle() && subway_item.sunwarp && | 109 | if (!AP_IsSunwarpShuffle() && subway_item.sunwarp && |
| 87 | subway_item.sunwarp->type != SubwaySunwarpType::kFinal) { | 110 | subway_item.sunwarp->type != SubwaySunwarpType::kFinal) { |
| 88 | std::ostringstream tag; | 111 | std::string tag = fmt::format("subway{}", subway_item.sunwarp->dots); |
| 89 | tag << "sunwarp" << subway_item.sunwarp->dots; | 112 | tagged[tag].push_back(subway_item.id); |
| 90 | |||
| 91 | tagged[tag.str()].push_back(subway_item.id); | ||
| 92 | } | 113 | } |
| 93 | 114 | ||
| 94 | if (!AP_IsPilgrimageEnabled() && | 115 | if (!AP_IsPilgrimageEnabled() && |
| @@ -100,8 +121,7 @@ void SubwayMap::OnConnect() { | |||
| 100 | 121 | ||
| 101 | if (AP_IsSunwarpShuffle()) { | 122 | if (AP_IsSunwarpShuffle()) { |
| 102 | for (const auto &[index, mapping] : AP_GetSunwarpMapping()) { | 123 | for (const auto &[index, mapping] : AP_GetSunwarpMapping()) { |
| 103 | std::ostringstream tag; | 124 | std::string tag = fmt::format("sunwarp{}", mapping.dots); |
| 104 | tag << "sunwarp" << mapping.dots; | ||
| 105 | 125 | ||
| 106 | SubwaySunwarp fromWarp; | 126 | SubwaySunwarp fromWarp; |
| 107 | if (index < 6) { | 127 | if (index < 6) { |
| @@ -121,8 +141,8 @@ void SubwayMap::OnConnect() { | |||
| 121 | toWarp.type = SubwaySunwarpType::kExit; | 141 | toWarp.type = SubwaySunwarpType::kExit; |
| 122 | } | 142 | } |
| 123 | 143 | ||
| 124 | tagged[tag.str()].push_back(GD_GetSubwayItemForSunwarp(fromWarp)); | 144 | tagged[tag].push_back(GD_GetSubwayItemForSunwarp(fromWarp)); |
| 125 | tagged[tag.str()].push_back(GD_GetSubwayItemForSunwarp(toWarp)); | 145 | tagged[tag].push_back(GD_GetSubwayItemForSunwarp(toWarp)); |
| 126 | } | 146 | } |
| 127 | } | 147 | } |
| 128 | 148 | ||
| @@ -147,9 +167,13 @@ void SubwayMap::UpdateIndicators() { | |||
| 147 | checked_paintings_.insert(painting_id); | 167 | checked_paintings_.insert(painting_id); |
| 148 | 168 | ||
| 149 | if (AP_GetPaintingMapping().count(painting_id)) { | 169 | if (AP_GetPaintingMapping().count(painting_id)) { |
| 150 | networks_.AddLink(GD_GetSubwayItemForPainting(painting_id), | 170 | std::optional<int> from_id = GD_GetSubwayItemForPainting(painting_id); |
| 151 | GD_GetSubwayItemForPainting( | 171 | std::optional<int> to_id = GD_GetSubwayItemForPainting( |
| 152 | AP_GetPaintingMapping().at(painting_id))); | 172 | AP_GetPaintingMapping().at(painting_id)); |
| 173 | |||
| 174 | if (from_id && to_id) { | ||
| 175 | networks_.AddLink(*from_id, *to_id); | ||
| 176 | } | ||
| 153 | } | 177 | } |
| 154 | } | 178 | } |
| 155 | } | 179 | } |
| @@ -266,9 +290,11 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
| 266 | // Note that these requirements are duplicated on OnMouseClick so that it | 290 | // Note that these requirements are duplicated on OnMouseClick so that it |
| 267 | // knows when an item has a hover effect. | 291 | // knows when an item has a hover effect. |
| 268 | const SubwayItem &subway_item = GD_GetSubwayItem(*hovered_item_); | 292 | const SubwayItem &subway_item = GD_GetSubwayItem(*hovered_item_); |
| 269 | if (subway_item.door && !GetDoorRequirements(*subway_item.door).empty()) { | 293 | std::optional<int> subway_door = GetRealSubwayDoor(subway_item); |
| 294 | |||
| 295 | if (subway_door && !GetDoorRequirements(*subway_door).empty()) { | ||
| 270 | const std::map<std::string, bool> &report = | 296 | const std::map<std::string, bool> &report = |
| 271 | GetDoorRequirements(*subway_item.door); | 297 | GetDoorRequirements(*subway_door); |
| 272 | 298 | ||
| 273 | int acc_height = 10; | 299 | int acc_height = 10; |
| 274 | int col_width = 0; | 300 | int col_width = 0; |
| @@ -404,7 +430,7 @@ void SubwayMap::OnMouseMove(wxMouseEvent &event) { | |||
| 404 | std::vector<int> hovered = tree_->query( | 430 | std::vector<int> hovered = tree_->query( |
| 405 | {static_cast<float>(mouse_pos.x), static_cast<float>(mouse_pos.y), 2, 2}); | 431 | {static_cast<float>(mouse_pos.x), static_cast<float>(mouse_pos.y), 2, 2}); |
| 406 | if (!hovered.empty()) { | 432 | if (!hovered.empty()) { |
| 407 | actual_hover_= hovered[0]; | 433 | actual_hover_ = hovered[0]; |
| 408 | } else { | 434 | } else { |
| 409 | actual_hover_ = std::nullopt; | 435 | actual_hover_ = std::nullopt; |
| 410 | } | 436 | } |
| @@ -449,7 +475,9 @@ void SubwayMap::OnMouseClick(wxMouseEvent &event) { | |||
| 449 | 475 | ||
| 450 | if (actual_hover_) { | 476 | if (actual_hover_) { |
| 451 | const SubwayItem &subway_item = GD_GetSubwayItem(*actual_hover_); | 477 | const SubwayItem &subway_item = GD_GetSubwayItem(*actual_hover_); |
| 452 | if ((subway_item.door && !GetDoorRequirements(*subway_item.door).empty()) || | 478 | std::optional<int> subway_door = GetRealSubwayDoor(subway_item); |
| 479 | |||
| 480 | if ((subway_door && !GetDoorRequirements(*subway_door).empty()) || | ||
| 453 | networks_.IsItemInNetwork(*hovered_item_)) { | 481 | networks_.IsItemInNetwork(*hovered_item_)) { |
| 454 | if (actual_hover_ != hovered_item_) { | 482 | if (actual_hover_ != hovered_item_) { |
| 455 | hovered_item_ = actual_hover_; | 483 | hovered_item_ = actual_hover_; |
| @@ -530,15 +558,9 @@ void SubwayMap::Redraw() { | |||
| 530 | std::optional<wxColour> shade_color; | 558 | std::optional<wxColour> shade_color; |
| 531 | 559 | ||
| 532 | if (AP_HasEarlyColorHallways() && | 560 | if (AP_HasEarlyColorHallways() && |
| 533 | (subway_item.special == "starting_room_paintings" || | 561 | subway_item.special == "starting_room_paintings") { |
| 534 | subway_item.special == "early_color_hallways")) { | ||
| 535 | draw_type = ItemDrawType::kOwl; | 562 | draw_type = ItemDrawType::kOwl; |
| 536 | 563 | shade_color = wxColour(0, 255, 0, 128); | |
| 537 | if (subway_item.special == "starting_room_paintings") { | ||
| 538 | shade_color = wxColour(0, 255, 0, 128); | ||
| 539 | } else { | ||
| 540 | shade_color = wxColour(255, 0, 0, 128); | ||
| 541 | } | ||
| 542 | } else if (subway_item.special == "sun_painting") { | 564 | } else if (subway_item.special == "sun_painting") { |
| 543 | if (!AP_IsPilgrimageEnabled()) { | 565 | if (!AP_IsPilgrimageEnabled()) { |
| 544 | if (IsDoorOpen(*subway_item.door)) { | 566 | if (IsDoorOpen(*subway_item.door)) { |
| @@ -570,7 +592,8 @@ void SubwayMap::Redraw() { | |||
| 570 | } | 592 | } |
| 571 | } | 593 | } |
| 572 | 594 | ||
| 573 | if (has_unchecked_painting || has_mapped_painting || has_codomain_painting) { | 595 | if (has_unchecked_painting || has_mapped_painting || |
| 596 | has_codomain_painting) { | ||
| 574 | draw_type = ItemDrawType::kOwl; | 597 | draw_type = ItemDrawType::kOwl; |
| 575 | 598 | ||
| 576 | if (has_checked_painting) { | 599 | if (has_checked_painting) { |
| diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index 107ae49..80fd137 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp | |||
| @@ -157,7 +157,7 @@ void TrackerFrame::OnConnect(wxCommandEvent &event) { | |||
| 157 | } | 157 | } |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | while (new_history.size() > 5) { | 160 | while (new_history.size() > 10) { |
| 161 | new_history.pop_back(); | 161 | new_history.pop_back(); |
| 162 | } | 162 | } |
| 163 | 163 | ||
| diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index eaf3e6b..14d302b 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp | |||
| @@ -1,5 +1,8 @@ | |||
| 1 | #include "tracker_state.h" | 1 | #include "tracker_state.h" |
| 2 | 2 | ||
| 3 | #include <fmt/core.h> | ||
| 4 | #include <hkutil/string.h> | ||
| 5 | |||
| 3 | #include <list> | 6 | #include <list> |
| 4 | #include <map> | 7 | #include <map> |
| 5 | #include <mutex> | 8 | #include <mutex> |
| @@ -9,6 +12,7 @@ | |||
| 9 | 12 | ||
| 10 | #include "ap_state.h" | 13 | #include "ap_state.h" |
| 11 | #include "game_data.h" | 14 | #include "game_data.h" |
| 15 | #include "logger.h" | ||
| 12 | 16 | ||
| 13 | namespace { | 17 | namespace { |
| 14 | 18 | ||
| @@ -231,7 +235,9 @@ class StateCalculator { | |||
| 231 | PaintingExit target_painting = | 235 | PaintingExit target_painting = |
| 232 | GD_GetPaintingExit(GD_GetPaintingByName( | 236 | GD_GetPaintingExit(GD_GetPaintingByName( |
| 233 | AP_GetPaintingMapping().at(cur_painting.internal_id))); | 237 | AP_GetPaintingMapping().at(cur_painting.internal_id))); |
| 238 | painting_exit.source_room = cur_painting.room; | ||
| 234 | painting_exit.destination_room = target_painting.room; | 239 | painting_exit.destination_room = target_painting.room; |
| 240 | painting_exit.type = EntranceType::kPainting; | ||
| 235 | 241 | ||
| 236 | new_boundary.push_back(painting_exit); | 242 | new_boundary.push_back(painting_exit); |
| 237 | } | 243 | } |
| @@ -258,6 +264,12 @@ class StateCalculator { | |||
| 258 | reachable_rooms_.insert(room_exit.destination_room); | 264 | reachable_rooms_.insert(room_exit.destination_room); |
| 259 | reachable_changed = true; | 265 | reachable_changed = true; |
| 260 | 266 | ||
| 267 | #ifndef NDEBUG | ||
| 268 | std::list<int> room_path = paths_[room_exit.source_room]; | ||
| 269 | room_path.push_back(room_exit.destination_room); | ||
| 270 | paths_[room_exit.destination_room] = room_path; | ||
| 271 | #endif | ||
| 272 | |||
| 261 | const Room& room_obj = GD_GetRoom(room_exit.destination_room); | 273 | const Room& room_obj = GD_GetRoom(room_exit.destination_room); |
| 262 | for (const Exit& out_edge : room_obj.exits) { | 274 | for (const Exit& out_edge : room_obj.exits) { |
| 263 | if (out_edge.type == EntranceType::kPainting && | 275 | if (out_edge.type == EntranceType::kPainting && |
| @@ -296,20 +308,33 @@ class StateCalculator { | |||
| 296 | 308 | ||
| 297 | if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") { | 309 | if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") { |
| 298 | new_boundary.push_back( | 310 | new_boundary.push_back( |
| 299 | {.destination_room = GD_GetRoomByName("Outside The Undeterred"), | 311 | {.source_room = room_exit.destination_room, |
| 312 | .destination_room = GD_GetRoomByName("Outside The Undeterred"), | ||
| 300 | .type = EntranceType::kPainting}); | 313 | .type = EntranceType::kPainting}); |
| 301 | } | 314 | } |
| 302 | 315 | ||
| 303 | if (AP_IsPilgrimageEnabled()) { | 316 | if (AP_IsPilgrimageEnabled()) { |
| 304 | if (room_obj.name == "Hub Room") { | 317 | int pilgrimage_start_id = GD_GetRoomByName("Hub Room"); |
| 318 | if (AP_IsSunwarpShuffle()) { | ||
| 319 | for (const auto& [start_index, mapping] : | ||
| 320 | AP_GetSunwarpMapping()) { | ||
| 321 | if (mapping.dots == 1) { | ||
| 322 | pilgrimage_start_id = GD_GetRoomForSunwarp(start_index); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | if (room_exit.destination_room == pilgrimage_start_id) { | ||
| 305 | new_boundary.push_back( | 328 | new_boundary.push_back( |
| 306 | {.destination_room = GD_GetRoomByName("Pilgrim Antechamber"), | 329 | {.source_room = room_exit.destination_room, |
| 330 | .destination_room = GD_GetRoomByName("Pilgrim Antechamber"), | ||
| 307 | .type = EntranceType::kPilgrimage}); | 331 | .type = EntranceType::kPilgrimage}); |
| 308 | } | 332 | } |
| 309 | } else { | 333 | } else { |
| 310 | if (room_obj.name == "Starting Room") { | 334 | if (room_obj.name == "Starting Room") { |
| 311 | new_boundary.push_back( | 335 | new_boundary.push_back( |
| 312 | {.destination_room = GD_GetRoomByName("Pilgrim Antechamber"), | 336 | {.source_room = room_exit.destination_room, |
| 337 | .destination_room = GD_GetRoomByName("Pilgrim Antechamber"), | ||
| 313 | .door = | 338 | .door = |
| 314 | GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"), | 339 | GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"), |
| 315 | .type = EntranceType::kPainting}); | 340 | .type = EntranceType::kPainting}); |
| @@ -350,6 +375,19 @@ class StateCalculator { | |||
| 350 | return door_report_; | 375 | return door_report_; |
| 351 | } | 376 | } |
| 352 | 377 | ||
| 378 | std::string GetPathToRoom(int room_id) const { | ||
| 379 | if (!paths_.count(room_id)) { | ||
| 380 | return ""; | ||
| 381 | } | ||
| 382 | |||
| 383 | const std::list<int>& path = paths_.at(room_id); | ||
| 384 | std::vector<std::string> room_names; | ||
| 385 | for (int room_id : path) { | ||
| 386 | room_names.push_back(GD_GetRoom(room_id).name); | ||
| 387 | } | ||
| 388 | return hatkirby::implode(room_names, " -> "); | ||
| 389 | } | ||
| 390 | |||
| 353 | private: | 391 | private: |
| 354 | template <typename T> | 392 | template <typename T> |
| 355 | Decision IsNonGroupedDoorReachable(const T& door_obj) { | 393 | Decision IsNonGroupedDoorReachable(const T& door_obj) { |
| @@ -603,6 +641,8 @@ class StateCalculator { | |||
| 603 | std::set<int> solveable_panels_; | 641 | std::set<int> solveable_panels_; |
| 604 | std::set<int> reachable_paintings_; | 642 | std::set<int> reachable_paintings_; |
| 605 | std::map<int, std::map<std::string, bool>> door_report_; | 643 | std::map<int, std::map<std::string, bool>> door_report_; |
| 644 | |||
| 645 | std::map<int, std::list<int>> paths_; | ||
| 606 | }; | 646 | }; |
| 607 | 647 | ||
| 608 | } // namespace | 648 | } // namespace |
| diff --git a/src/version.h b/src/version.h index 7aab91b..8f93e18 100644 --- a/src/version.h +++ b/src/version.h | |||
| @@ -1,9 +1,10 @@ | |||
| 1 | #ifndef VERSION_H_C757E53C | 1 | #ifndef VERSION_H_C757E53C |
| 2 | #define VERSION_H_C757E53C | 2 | #define VERSION_H_C757E53C |
| 3 | 3 | ||
| 4 | #include <sstream> | ||
| 5 | #include <regex> | 4 | #include <regex> |
| 6 | 5 | ||
| 6 | #include <fmt/core.h> | ||
| 7 | |||
| 7 | struct Version { | 8 | struct Version { |
| 8 | int major = 0; | 9 | int major = 0; |
| 9 | int minor = 0; | 10 | int minor = 0; |
| @@ -24,9 +25,7 @@ struct Version { | |||
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | std::string ToString() const { | 27 | std::string ToString() const { |
| 27 | std::ostringstream output; | 28 | return fmt::format("v{}.{}.{}", major, minor, revision); |
| 28 | output << "v" << major << "." << minor << "." << revision; | ||
| 29 | return output.str(); | ||
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | bool operator<(const Version& rhs) const { | 31 | bool operator<(const Version& rhs) const { |
| @@ -37,6 +36,6 @@ struct Version { | |||
| 37 | } | 36 | } |
| 38 | }; | 37 | }; |
| 39 | 38 | ||
| 40 | constexpr const Version kTrackerVersion = Version(0, 10, 2); | 39 | constexpr const Version kTrackerVersion = Version(0, 10, 6); |
| 41 | 40 | ||
| 42 | #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 925212a..e13d228 100644 --- a/vcpkg.json +++ b/vcpkg.json | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | "websocketpp", | 3 | "websocketpp", |
| 4 | "wxwidgets", | 4 | "wxwidgets", |
| 5 | "openssl", | 5 | "openssl", |
| 6 | "yaml-cpp" | 6 | "yaml-cpp", |
| 7 | "fmt" | ||
| 7 | ] | 8 | ] |
| 8 | } | 9 | } |
