diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2024-12-17 15:40:19 -0500 |
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2024-12-17 15:40:19 -0500 |
| commit | ad7c3e616fdc6f13812ec52675d226a19351494a (patch) | |
| tree | 4cdd8b27c6df4e5f3475fe1b3f1815689b387fd7 /src | |
| parent | 5ea44a418ec5db446dd28daf5ed95f46fea504f8 (diff) | |
| download | lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.tar.gz lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.tar.bz2 lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.zip | |
Added getting player position from IPC
Diffstat (limited to 'src')
| -rw-r--r-- | src/ap_state.cpp | 43 | ||||
| -rw-r--r-- | src/ap_state.h | 2 | ||||
| -rw-r--r-- | src/ipc_state.cpp | 231 | ||||
| -rw-r--r-- | src/ipc_state.h | 20 | ||||
| -rw-r--r-- | src/tracker_frame.cpp | 49 | ||||
| -rw-r--r-- | src/tracker_frame.h | 5 | ||||
| -rw-r--r-- | src/tracker_panel.cpp | 27 |
7 files changed, 348 insertions, 29 deletions
| diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 32ae8f0..80d96a1 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <tuple> | 22 | #include <tuple> |
| 23 | 23 | ||
| 24 | #include "game_data.h" | 24 | #include "game_data.h" |
| 25 | #include "ipc_state.h" | ||
| 25 | #include "logger.h" | 26 | #include "logger.h" |
| 26 | #include "tracker_frame.h" | 27 | #include "tracker_frame.h" |
| 27 | #include "tracker_state.h" | 28 | #include "tracker_state.h" |
| @@ -43,6 +44,7 @@ struct APState { | |||
| 43 | TrackerFrame* tracker_frame = nullptr; | 44 | TrackerFrame* tracker_frame = nullptr; |
| 44 | 45 | ||
| 45 | bool client_active = false; | 46 | bool client_active = false; |
| 47 | std::string status_message = "Not connected to Archipelago."; | ||
| 46 | std::mutex client_mutex; | 48 | std::mutex client_mutex; |
| 47 | 49 | ||
| 48 | bool connected = false; | 50 | bool connected = false; |
| @@ -113,7 +115,10 @@ struct APState { | |||
| 113 | initialized = true; | 115 | initialized = true; |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); | 118 | { |
| 119 | std::lock_guard client_guard(client_mutex); | ||
| 120 | SetStatusMessage("Connecting to Archipelago server...."); | ||
| 121 | } | ||
| 117 | TrackerLog(fmt::format("Connecting to Archipelago server ({})...", server)); | 122 | TrackerLog(fmt::format("Connecting to Archipelago server ({})...", server)); |
| 118 | 123 | ||
| 119 | { | 124 | { |
| @@ -169,8 +174,7 @@ struct APState { | |||
| 169 | "Connected to Archipelago server. Authenticating as {} {}", player, | 174 | "Connected to Archipelago server. Authenticating as {} {}", player, |
| 170 | (password.empty() ? "without password" | 175 | (password.empty() ? "without password" |
| 171 | : "with password " + password))); | 176 | : "with password " + password))); |
| 172 | tracker_frame->SetStatusMessage( | 177 | SetStatusMessage("Connected to Archipelago server. Authenticating..."); |
| 173 | "Connected to Archipelago server. Authenticating..."); | ||
| 174 | 178 | ||
| 175 | apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"}, | 179 | apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"}, |
| 176 | {AP_MAJOR, AP_MINOR, AP_REVISION}); | 180 | {AP_MAJOR, AP_MINOR, AP_REVISION}); |
| @@ -187,14 +191,14 @@ struct APState { | |||
| 187 | }); | 191 | }); |
| 188 | 192 | ||
| 189 | apclient->set_slot_disconnected_handler([this]() { | 193 | apclient->set_slot_disconnected_handler([this]() { |
| 190 | tracker_frame->SetStatusMessage( | 194 | SetStatusMessage( |
| 191 | "Disconnected from Archipelago. Attempting to reconnect..."); | 195 | "Disconnected from Archipelago. Attempting to reconnect..."); |
| 192 | TrackerLog( | 196 | TrackerLog( |
| 193 | "Slot disconnected from Archipelago. Attempting to reconnect..."); | 197 | "Slot disconnected from Archipelago. Attempting to reconnect..."); |
| 194 | }); | 198 | }); |
| 195 | 199 | ||
| 196 | apclient->set_socket_disconnected_handler([this]() { | 200 | apclient->set_socket_disconnected_handler([this]() { |
| 197 | tracker_frame->SetStatusMessage( | 201 | SetStatusMessage( |
| 198 | "Disconnected from Archipelago. Attempting to reconnect..."); | 202 | "Disconnected from Archipelago. Attempting to reconnect..."); |
| 199 | TrackerLog( | 203 | TrackerLog( |
| 200 | "Socket disconnected from Archipelago. Attempting to reconnect..."); | 204 | "Socket disconnected from Archipelago. Attempting to reconnect..."); |
| @@ -229,10 +233,12 @@ struct APState { | |||
| 229 | apclient->set_slot_connected_handler([this, player, server, | 233 | apclient->set_slot_connected_handler([this, player, server, |
| 230 | &connection_mutex]( | 234 | &connection_mutex]( |
| 231 | const nlohmann::json& slot_data) { | 235 | const nlohmann::json& slot_data) { |
| 232 | tracker_frame->SetStatusMessage( | 236 | SetStatusMessage( |
| 233 | fmt::format("Connected to Archipelago! ({}@{})", player, server)); | 237 | fmt::format("Connected to Archipelago! ({}@{}).", player, server)); |
| 234 | TrackerLog("Connected to Archipelago!"); | 238 | TrackerLog("Connected to Archipelago!"); |
| 235 | 239 | ||
| 240 | IPC_SetTrackerSlot(server, player); | ||
| 241 | |||
| 236 | save_name = fmt::format("zzAP_{}_{}.save", apclient->get_seed(), | 242 | save_name = fmt::format("zzAP_{}_{}.save", apclient->get_seed(), |
| 237 | apclient->get_player_number()); | 243 | apclient->get_player_number()); |
| 238 | data_storage_prefix = | 244 | data_storage_prefix = |
| @@ -328,7 +334,7 @@ struct APState { | |||
| 328 | has_connection_result = true; | 334 | has_connection_result = true; |
| 329 | } | 335 | } |
| 330 | 336 | ||
| 331 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); | 337 | SetStatusMessage("Disconnected from Archipelago."); |
| 332 | 338 | ||
| 333 | std::vector<std::string> error_messages; | 339 | std::vector<std::string> error_messages; |
| 334 | error_messages.push_back("Could not connect to Archipelago."); | 340 | error_messages.push_back("Could not connect to Archipelago."); |
| @@ -376,7 +382,10 @@ struct APState { | |||
| 376 | if (interval == 0) { | 382 | if (interval == 0) { |
| 377 | DestroyClient(); | 383 | DestroyClient(); |
| 378 | 384 | ||
| 379 | tracker_frame->SetStatusMessage("Disconnected from Archipelago."); | 385 | { |
| 386 | std::lock_guard client_guard(client_mutex); | ||
| 387 | SetStatusMessage("Disconnected from Archipelago."); | ||
| 388 | } | ||
| 380 | TrackerLog("Timeout while connecting to Archipelago server."); | 389 | TrackerLog("Timeout while connecting to Archipelago server."); |
| 381 | wxMessageBox("Timeout while connecting to Archipelago server.", | 390 | wxMessageBox("Timeout while connecting to Archipelago server.", |
| 382 | "Connection failed", wxOK | wxICON_ERROR); | 391 | "Connection failed", wxOK | wxICON_ERROR); |
| @@ -400,6 +409,17 @@ struct APState { | |||
| 400 | } | 409 | } |
| 401 | } | 410 | } |
| 402 | 411 | ||
| 412 | std::string GetStatusMessage() { | ||
| 413 | std::lock_guard client_guard(client_mutex); | ||
| 414 | return status_message; | ||
| 415 | } | ||
| 416 | |||
| 417 | void SetStatusMessage(std::string msg) { | ||
| 418 | status_message = std::move(msg); | ||
| 419 | |||
| 420 | tracker_frame->UpdateStatusMessage(); | ||
| 421 | } | ||
| 422 | |||
| 403 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { | 423 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { |
| 404 | if (value.is_boolean()) { | 424 | if (value.is_boolean()) { |
| 405 | data_storage[key] = value.get<bool>(); | 425 | data_storage[key] = value.get<bool>(); |
| @@ -527,6 +547,8 @@ void AP_Connect(std::string server, std::string player, std::string password) { | |||
| 527 | GetState().Connect(server, player, password); | 547 | GetState().Connect(server, player, password); |
| 528 | } | 548 | } |
| 529 | 549 | ||
| 550 | std::string AP_GetStatusMessage() { return GetState().GetStatusMessage(); } | ||
| 551 | |||
| 530 | std::string AP_GetSaveName() { return GetState().save_name; } | 552 | std::string AP_GetSaveName() { return GetState().save_name; } |
| 531 | 553 | ||
| 532 | bool AP_HasCheckedGameLocation(int location_id) { | 554 | bool AP_HasCheckedGameLocation(int location_id) { |
| @@ -590,7 +612,8 @@ bool AP_IsLocationVisible(int classification) { | |||
| 590 | return false; | 612 | return false; |
| 591 | } | 613 | } |
| 592 | 614 | ||
| 593 | if (GetState().door_shuffle_mode == kDOORS_MODE && !GetState().early_color_hallways) { | 615 | if (GetState().door_shuffle_mode == kDOORS_MODE && |
| 616 | !GetState().early_color_hallways) { | ||
| 594 | world_state |= kLOCATION_SMALL_SPHERE_ONE; | 617 | world_state |= kLOCATION_SMALL_SPHERE_ONE; |
| 595 | } | 618 | } |
| 596 | 619 | ||
| diff --git a/src/ap_state.h b/src/ap_state.h index e06d4ff..f310ee8 100644 --- a/src/ap_state.h +++ b/src/ap_state.h | |||
| @@ -43,6 +43,8 @@ void AP_SetTrackerFrame(TrackerFrame* tracker_frame); | |||
| 43 | 43 | ||
| 44 | void AP_Connect(std::string server, std::string player, std::string password); | 44 | void AP_Connect(std::string server, std::string player, std::string password); |
| 45 | 45 | ||
| 46 | std::string AP_GetStatusMessage(); | ||
| 47 | |||
| 46 | std::string AP_GetSaveName(); | 48 | std::string AP_GetSaveName(); |
| 47 | 49 | ||
| 48 | bool AP_HasCheckedGameLocation(int location_id); | 50 | bool AP_HasCheckedGameLocation(int location_id); |
| diff --git a/src/ipc_state.cpp b/src/ipc_state.cpp new file mode 100644 index 0000000..18f318f --- /dev/null +++ b/src/ipc_state.cpp | |||
| @@ -0,0 +1,231 @@ | |||
| 1 | #include "ipc_state.h" | ||
| 2 | |||
| 3 | #define _WEBSOCKETPP_CPP11_STRICT_ | ||
| 4 | |||
| 5 | #include <fmt/core.h> | ||
| 6 | |||
| 7 | #include <chrono> | ||
| 8 | #include <memory> | ||
| 9 | #include <mutex> | ||
| 10 | #include <nlohmann/json.hpp> | ||
| 11 | #include <optional> | ||
| 12 | #include <string> | ||
| 13 | #include <thread> | ||
| 14 | #include <tuple> | ||
| 15 | #include <wswrap.hpp> | ||
| 16 | |||
| 17 | #include "logger.h" | ||
| 18 | #include "tracker_frame.h" | ||
| 19 | |||
| 20 | namespace { | ||
| 21 | |||
| 22 | constexpr const char* kIpcAddress = "ws://127.0.0.1:41253"; | ||
| 23 | |||
| 24 | struct IPCState { | ||
| 25 | std::mutex state_mutex; | ||
| 26 | TrackerFrame* tracker_frame = nullptr; | ||
| 27 | |||
| 28 | // Protected state | ||
| 29 | std::optional<std::string> status_message; | ||
| 30 | |||
| 31 | bool slot_matches = false; | ||
| 32 | std::string tracker_ap_server; | ||
| 33 | std::string tracker_ap_user; | ||
| 34 | std::string game_ap_server; | ||
| 35 | std::string game_ap_user; | ||
| 36 | |||
| 37 | std::optional<std::tuple<int, int>> player_position; | ||
| 38 | |||
| 39 | int backoff_amount = 0; | ||
| 40 | |||
| 41 | // Thread state | ||
| 42 | std::unique_ptr<wswrap::WS> ws; | ||
| 43 | bool connected = false; | ||
| 44 | |||
| 45 | void Start(TrackerFrame* frame) { | ||
| 46 | tracker_frame = frame; | ||
| 47 | |||
| 48 | std::thread([this]() { Thread(); }).detach(); | ||
| 49 | } | ||
| 50 | |||
| 51 | std::optional<std::string> GetStatusMessage() { | ||
| 52 | std::lock_guard state_guard(state_mutex); | ||
| 53 | |||
| 54 | return status_message; | ||
| 55 | } | ||
| 56 | |||
| 57 | void SetTrackerSlot(std::string server, std::string user) { | ||
| 58 | std::lock_guard state_guard(state_mutex); | ||
| 59 | |||
| 60 | tracker_ap_server = std::move(server); | ||
| 61 | tracker_ap_user = std::move(user); | ||
| 62 | |||
| 63 | CheckIfSlotMatches(); | ||
| 64 | |||
| 65 | backoff_amount = 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | bool IsConnected() { | ||
| 69 | std::lock_guard state_guard(state_mutex); | ||
| 70 | |||
| 71 | return slot_matches; | ||
| 72 | } | ||
| 73 | |||
| 74 | std::optional<std::tuple<int, int>> GetPlayerPosition() { | ||
| 75 | std::lock_guard state_guard(state_mutex); | ||
| 76 | |||
| 77 | return player_position; | ||
| 78 | } | ||
| 79 | |||
| 80 | private: | ||
| 81 | void Thread() { | ||
| 82 | for (;;) { | ||
| 83 | player_position = std::nullopt; | ||
| 84 | |||
| 85 | TrackerLog("Looking for game over IPC..."); | ||
| 86 | |||
| 87 | { | ||
| 88 | std::lock_guard state_guard(state_mutex); | ||
| 89 | backoff_amount = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | while (!TryConnect() || !connected) { | ||
| 93 | int backoff_limit; | ||
| 94 | { | ||
| 95 | std::lock_guard state_guard(state_mutex); | ||
| 96 | backoff_limit = (backoff_amount + 1) * 10; | ||
| 97 | } | ||
| 98 | |||
| 99 | for (int i = 0; i < backoff_limit && !connected; i++) { | ||
| 100 | ws->poll(); | ||
| 101 | |||
| 102 | // Back off | ||
| 103 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
| 104 | } | ||
| 105 | |||
| 106 | { | ||
| 107 | std::lock_guard state_guard(state_mutex); | ||
| 108 | backoff_amount = std::min(9, backoff_amount + 1); | ||
| 109 | |||
| 110 | if (!connected) { | ||
| 111 | TrackerLog(fmt::format("Retrying IPC in {} second(s)...", | ||
| 112 | backoff_amount + 1)); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | while (connected) { | ||
| 118 | ws->poll(); | ||
| 119 | |||
| 120 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||
| 121 | } | ||
| 122 | |||
| 123 | SetStatusMessage("Disconnected from game."); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | bool TryConnect() { | ||
| 128 | try { | ||
| 129 | ws = std::make_unique<wswrap::WS>( | ||
| 130 | kIpcAddress, [this]() { OnConnect(); }, [this]() { OnClose(); }, | ||
| 131 | [this](const std::string& s) { OnMessage(s); }, | ||
| 132 | [this](const std::string& s) { OnError(s); }); | ||
| 133 | return true; | ||
| 134 | } catch (const std::exception& ex) { | ||
| 135 | ws.reset(); | ||
| 136 | return false; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | void OnConnect() { connected = true; } | ||
| 141 | |||
| 142 | void OnClose() { | ||
| 143 | connected = false; | ||
| 144 | |||
| 145 | { | ||
| 146 | std::lock_guard state_guard(state_mutex); | ||
| 147 | |||
| 148 | slot_matches = false; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | void OnMessage(const std::string& s) { | ||
| 153 | TrackerLog(s); | ||
| 154 | |||
| 155 | auto msg = nlohmann::json::parse(s); | ||
| 156 | |||
| 157 | if (msg["cmd"] == "Connect") { | ||
| 158 | std::lock_guard state_guard(state_mutex); | ||
| 159 | |||
| 160 | game_ap_server = msg["slot"]["server"]; | ||
| 161 | game_ap_user = msg["slot"]["player"]; | ||
| 162 | |||
| 163 | CheckIfSlotMatches(); | ||
| 164 | } else if (msg["cmd"] == "UpdatePosition") { | ||
| 165 | std::lock_guard state_guard(state_mutex); | ||
| 166 | |||
| 167 | player_position = | ||
| 168 | std::make_tuple<int, int>(msg["position"]["x"], msg["position"]["z"]); | ||
| 169 | |||
| 170 | tracker_frame->RedrawPosition(); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | void OnError(const std::string& s) {} | ||
| 175 | |||
| 176 | void CheckIfSlotMatches() { | ||
| 177 | slot_matches = (tracker_ap_server == game_ap_server && | ||
| 178 | tracker_ap_user == game_ap_user); | ||
| 179 | |||
| 180 | if (slot_matches) { | ||
| 181 | status_message = "Connected to game."; | ||
| 182 | |||
| 183 | Sync(); | ||
| 184 | } else if (tracker_ap_server.empty()) { | ||
| 185 | status_message = std::nullopt; | ||
| 186 | } else if (connected) { | ||
| 187 | status_message = "Local game doesn't match AP slot."; | ||
| 188 | } | ||
| 189 | |||
| 190 | tracker_frame->UpdateStatusMessage(); | ||
| 191 | } | ||
| 192 | |||
| 193 | void SetStatusMessage(std::optional<std::string> msg) { | ||
| 194 | { | ||
| 195 | std::lock_guard state_guard(state_mutex); | ||
| 196 | status_message = msg; | ||
| 197 | } | ||
| 198 | |||
| 199 | tracker_frame->UpdateStatusMessage(); | ||
| 200 | } | ||
| 201 | |||
| 202 | void Sync() { | ||
| 203 | nlohmann::json msg; | ||
| 204 | msg["cmd"] = "Sync"; | ||
| 205 | |||
| 206 | ws->send_text(msg.dump()); | ||
| 207 | } | ||
| 208 | }; | ||
| 209 | |||
| 210 | IPCState& GetState() { | ||
| 211 | static IPCState* instance = new IPCState(); | ||
| 212 | return *instance; | ||
| 213 | } | ||
| 214 | |||
| 215 | } // namespace | ||
| 216 | |||
| 217 | void IPC_Start(TrackerFrame* tracker_frame) { GetState().Start(tracker_frame); } | ||
| 218 | |||
| 219 | std::optional<std::string> IPC_GetStatusMessage() { | ||
| 220 | return GetState().GetStatusMessage(); | ||
| 221 | } | ||
| 222 | |||
| 223 | void IPC_SetTrackerSlot(std::string server, std::string user) { | ||
| 224 | GetState().SetTrackerSlot(server, user); | ||
| 225 | } | ||
| 226 | |||
| 227 | bool IPC_IsConnected() { return GetState().IsConnected(); } | ||
| 228 | |||
| 229 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition() { | ||
| 230 | return GetState().GetPlayerPosition(); | ||
| 231 | } | ||
| diff --git a/src/ipc_state.h b/src/ipc_state.h new file mode 100644 index 0000000..be71673 --- /dev/null +++ b/src/ipc_state.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef IPC_STATE_H_6B3B0958 | ||
| 2 | #define IPC_STATE_H_6B3B0958 | ||
| 3 | |||
| 4 | #include <optional> | ||
| 5 | #include <string> | ||
| 6 | #include <tuple> | ||
| 7 | |||
| 8 | class TrackerFrame; | ||
| 9 | |||
| 10 | void IPC_Start(TrackerFrame* tracker_frame); | ||
| 11 | |||
| 12 | std::optional<std::string> IPC_GetStatusMessage(); | ||
| 13 | |||
| 14 | void IPC_SetTrackerSlot(std::string server, std::string user); | ||
| 15 | |||
| 16 | bool IPC_IsConnected(); | ||
| 17 | |||
| 18 | std::optional<std::tuple<int, int>> IPC_GetPlayerPosition(); | ||
| 19 | |||
| 20 | #endif /* end of include guard: IPC_STATE_H_6B3B0958 */ | ||
| diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index b9282f5..a06e46b 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp | |||
| @@ -14,12 +14,29 @@ | |||
| 14 | #include "achievements_pane.h" | 14 | #include "achievements_pane.h" |
| 15 | #include "ap_state.h" | 15 | #include "ap_state.h" |
| 16 | #include "connection_dialog.h" | 16 | #include "connection_dialog.h" |
| 17 | #include "ipc_state.h" | ||
| 17 | #include "settings_dialog.h" | 18 | #include "settings_dialog.h" |
| 18 | #include "subway_map.h" | 19 | #include "subway_map.h" |
| 19 | #include "tracker_config.h" | 20 | #include "tracker_config.h" |
| 20 | #include "tracker_panel.h" | 21 | #include "tracker_panel.h" |
| 21 | #include "version.h" | 22 | #include "version.h" |
| 22 | 23 | ||
| 24 | namespace { | ||
| 25 | |||
| 26 | std::string GetStatusMessage() { | ||
| 27 | std::string msg = AP_GetStatusMessage(); | ||
| 28 | |||
| 29 | std::optional<std::string> ipc_msg = IPC_GetStatusMessage(); | ||
| 30 | if (ipc_msg) { | ||
| 31 | msg += " "; | ||
| 32 | msg += *ipc_msg; | ||
| 33 | } | ||
| 34 | |||
| 35 | return msg; | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace | ||
| 39 | |||
| 23 | enum TrackerFrameIds { | 40 | enum TrackerFrameIds { |
| 24 | ID_CONNECT = 1, | 41 | ID_CONNECT = 1, |
| 25 | ID_CHECK_FOR_UPDATES = 2, | 42 | ID_CHECK_FOR_UPDATES = 2, |
| @@ -32,6 +49,7 @@ enum TrackerFrameIds { | |||
| 32 | wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); | 49 | wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); |
| 33 | wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); | 50 | wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); |
| 34 | wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); | 51 | wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); |
| 52 | wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent); | ||
| 35 | 53 | ||
| 36 | TrackerFrame::TrackerFrame() | 54 | TrackerFrame::TrackerFrame() |
| 37 | : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition, | 55 | : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition, |
| @@ -65,7 +83,6 @@ TrackerFrame::TrackerFrame() | |||
| 65 | SetMenuBar(menuBar); | 83 | SetMenuBar(menuBar); |
| 66 | 84 | ||
| 67 | CreateStatusBar(); | 85 | CreateStatusBar(); |
| 68 | SetStatusText("Not connected to Archipelago."); | ||
| 69 | 86 | ||
| 70 | Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT); | 87 | Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT); |
| 71 | Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); | 88 | Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); |
| @@ -80,6 +97,7 @@ TrackerFrame::TrackerFrame() | |||
| 80 | Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); | 97 | Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); |
| 81 | Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); | 98 | Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); |
| 82 | Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); | 99 | Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); |
| 100 | Bind(REDRAW_POSITION, &TrackerFrame::OnRedrawPosition, this); | ||
| 83 | 101 | ||
| 84 | wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY); | 102 | wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY); |
| 85 | achievements_pane_ = new AchievementsPane(choicebook); | 103 | achievements_pane_ = new AchievementsPane(choicebook); |
| @@ -115,13 +133,14 @@ TrackerFrame::TrackerFrame() | |||
| 115 | if (GetTrackerConfig().should_check_for_updates) { | 133 | if (GetTrackerConfig().should_check_for_updates) { |
| 116 | CheckForUpdates(/*manual=*/false); | 134 | CheckForUpdates(/*manual=*/false); |
| 117 | } | 135 | } |
| 118 | } | ||
| 119 | 136 | ||
| 120 | void TrackerFrame::SetStatusMessage(std::string message) { | 137 | IPC_Start(this); |
| 121 | wxCommandEvent *event = new wxCommandEvent(STATUS_CHANGED); | ||
| 122 | event->SetString(message.c_str()); | ||
| 123 | 138 | ||
| 124 | QueueEvent(event); | 139 | SetStatusText(GetStatusMessage()); |
| 140 | } | ||
| 141 | |||
| 142 | void TrackerFrame::UpdateStatusMessage() { | ||
| 143 | QueueEvent(new wxCommandEvent(STATUS_CHANGED)); | ||
| 125 | } | 144 | } |
| 126 | 145 | ||
| 127 | void TrackerFrame::ResetIndicators() { | 146 | void TrackerFrame::ResetIndicators() { |
| @@ -132,6 +151,10 @@ void TrackerFrame::UpdateIndicators() { | |||
| 132 | QueueEvent(new wxCommandEvent(STATE_CHANGED)); | 151 | QueueEvent(new wxCommandEvent(STATE_CHANGED)); |
| 133 | } | 152 | } |
| 134 | 153 | ||
| 154 | void TrackerFrame::RedrawPosition() { | ||
| 155 | QueueEvent(new wxCommandEvent(REDRAW_POSITION)); | ||
| 156 | } | ||
| 157 | |||
| 135 | void TrackerFrame::OnAbout(wxCommandEvent &event) { | 158 | void TrackerFrame::OnAbout(wxCommandEvent &event) { |
| 136 | wxAboutDialogInfo about_info; | 159 | wxAboutDialogInfo about_info; |
| 137 | about_info.SetName("Lingo Archipelago Tracker"); | 160 | about_info.SetName("Lingo Archipelago Tracker"); |
| @@ -200,7 +223,7 @@ void TrackerFrame::OnZoomIn(wxCommandEvent &event) { | |||
| 200 | } | 223 | } |
| 201 | } | 224 | } |
| 202 | 225 | ||
| 203 | void TrackerFrame::OnZoomOut(wxCommandEvent& event) { | 226 | void TrackerFrame::OnZoomOut(wxCommandEvent &event) { |
| 204 | if (notebook_->GetSelection() == 1) { | 227 | if (notebook_->GetSelection() == 1) { |
| 205 | subway_map_->Zoom(false); | 228 | subway_map_->Zoom(false); |
| 206 | } | 229 | } |
| @@ -211,7 +234,7 @@ void TrackerFrame::OnChangePage(wxBookCtrlEvent &event) { | |||
| 211 | zoom_out_menu_item_->Enable(event.GetSelection() == 1); | 234 | zoom_out_menu_item_->Enable(event.GetSelection() == 1); |
| 212 | } | 235 | } |
| 213 | 236 | ||
| 214 | void TrackerFrame::OnOpenFile(wxCommandEvent& event) { | 237 | void TrackerFrame::OnOpenFile(wxCommandEvent &event) { |
| 215 | wxFileDialog open_file_dialog( | 238 | wxFileDialog open_file_dialog( |
| 216 | this, "Open Lingo Save File", | 239 | this, "Open Lingo Save File", |
| 217 | fmt::format("{}\\Godot\\app_userdata\\Lingo\\level1_stable", | 240 | fmt::format("{}\\Godot\\app_userdata\\Lingo\\level1_stable", |
| @@ -233,7 +256,7 @@ void TrackerFrame::OnOpenFile(wxCommandEvent& event) { | |||
| 233 | panels_panel_->SetSavedataPath(savedata_path); | 256 | panels_panel_->SetSavedataPath(savedata_path); |
| 234 | } | 257 | } |
| 235 | 258 | ||
| 236 | void TrackerFrame::OnStateReset(wxCommandEvent& event) { | 259 | void TrackerFrame::OnStateReset(wxCommandEvent &event) { |
| 237 | tracker_panel_->UpdateIndicators(); | 260 | tracker_panel_->UpdateIndicators(); |
| 238 | achievements_pane_->UpdateIndicators(); | 261 | achievements_pane_->UpdateIndicators(); |
| 239 | subway_map_->OnConnect(); | 262 | subway_map_->OnConnect(); |
| @@ -255,7 +278,13 @@ void TrackerFrame::OnStateChanged(wxCommandEvent &event) { | |||
| 255 | } | 278 | } |
| 256 | 279 | ||
| 257 | void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { | 280 | void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { |
| 258 | SetStatusText(event.GetString()); | 281 | SetStatusText(GetStatusMessage()); |
| 282 | } | ||
| 283 | |||
| 284 | void TrackerFrame::OnRedrawPosition(wxCommandEvent &event) { | ||
| 285 | if (notebook_->GetSelection() == 0) { | ||
| 286 | tracker_panel_->Refresh(); | ||
| 287 | } | ||
| 259 | } | 288 | } |
| 260 | 289 | ||
| 261 | void TrackerFrame::CheckForUpdates(bool manual) { | 290 | void TrackerFrame::CheckForUpdates(bool manual) { |
| diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 19bd0b3..ab59ffb 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h | |||
| @@ -16,15 +16,17 @@ class wxNotebook; | |||
| 16 | wxDECLARE_EVENT(STATE_RESET, wxCommandEvent); | 16 | wxDECLARE_EVENT(STATE_RESET, wxCommandEvent); |
| 17 | wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent); | 17 | wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent); |
| 18 | wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent); | 18 | wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent); |
| 19 | wxDECLARE_EVENT(REDRAW_POSITION, wxCommandEvent); | ||
| 19 | 20 | ||
| 20 | class TrackerFrame : public wxFrame { | 21 | class TrackerFrame : public wxFrame { |
| 21 | public: | 22 | public: |
| 22 | TrackerFrame(); | 23 | TrackerFrame(); |
| 23 | 24 | ||
| 24 | void SetStatusMessage(std::string message); | 25 | void UpdateStatusMessage(); |
| 25 | 26 | ||
| 26 | void ResetIndicators(); | 27 | void ResetIndicators(); |
| 27 | void UpdateIndicators(); | 28 | void UpdateIndicators(); |
| 29 | void RedrawPosition(); | ||
| 28 | 30 | ||
| 29 | private: | 31 | private: |
| 30 | void OnExit(wxCommandEvent &event); | 32 | void OnExit(wxCommandEvent &event); |
| @@ -40,6 +42,7 @@ class TrackerFrame : public wxFrame { | |||
| 40 | void OnStateReset(wxCommandEvent &event); | 42 | void OnStateReset(wxCommandEvent &event); |
| 41 | void OnStateChanged(wxCommandEvent &event); | 43 | void OnStateChanged(wxCommandEvent &event); |
| 42 | void OnStatusChanged(wxCommandEvent &event); | 44 | void OnStatusChanged(wxCommandEvent &event); |
| 45 | void OnRedrawPosition(wxCommandEvent &event); | ||
| 43 | 46 | ||
| 44 | void CheckForUpdates(bool manual); | 47 | void CheckForUpdates(bool manual); |
| 45 | 48 | ||
| diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 27e825a..2f2d596 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "game_data.h" | 10 | #include "game_data.h" |
| 11 | #include "global.h" | 11 | #include "global.h" |
| 12 | #include "godot_variant.h" | 12 | #include "godot_variant.h" |
| 13 | #include "ipc_state.h" | ||
| 13 | #include "tracker_config.h" | 14 | #include "tracker_config.h" |
| 14 | #include "tracker_state.h" | 15 | #include "tracker_state.h" |
| 15 | 16 | ||
| @@ -59,7 +60,8 @@ void TrackerPanel::UpdateIndicators() { | |||
| 59 | 60 | ||
| 60 | void TrackerPanel::SetSavedataPath(std::string savedata_path) { | 61 | void TrackerPanel::SetSavedataPath(std::string savedata_path) { |
| 61 | if (!panels_mode_) { | 62 | if (!panels_mode_) { |
| 62 | wxButton *refresh_button = new wxButton(this, wxID_ANY, "Refresh", {15, 15}); | 63 | wxButton *refresh_button = |
| 64 | new wxButton(this, wxID_ANY, "Refresh", {15, 15}); | ||
| 63 | refresh_button->Bind(wxEVT_BUTTON, &TrackerPanel::OnRefreshSavedata, this); | 65 | refresh_button->Bind(wxEVT_BUTTON, &TrackerPanel::OnRefreshSavedata, this); |
| 64 | } | 66 | } |
| 65 | 67 | ||
| @@ -94,16 +96,25 @@ void TrackerPanel::OnPaint(wxPaintEvent &event) { | |||
| 94 | wxBufferedPaintDC dc(this); | 96 | wxBufferedPaintDC dc(this); |
| 95 | dc.DrawBitmap(rendered_, 0, 0); | 97 | dc.DrawBitmap(rendered_, 0, 0); |
| 96 | 98 | ||
| 97 | if (AP_GetPlayerPosition().has_value()) { | 99 | std::optional<std::tuple<int, int>> player_position; |
| 100 | if (IPC_IsConnected()) { | ||
| 101 | player_position = IPC_GetPlayerPosition(); | ||
| 102 | } else { | ||
| 103 | player_position = AP_GetPlayerPosition(); | ||
| 104 | } | ||
| 105 | |||
| 106 | if (player_position.has_value()) { | ||
| 98 | // 1588, 1194 | 107 | // 1588, 1194 |
| 99 | // 14x14 -> 154x154 | 108 | // 14x14 -> 154x154 |
| 100 | double intended_x = | 109 | double intended_x = |
| 101 | 1588.0 + (std::get<0>(*AP_GetPlayerPosition()) * (154.0 / 14.0)); | 110 | 1588.0 + (std::get<0>(*player_position) * (154.0 / 14.0)); |
| 102 | double intended_y = | 111 | double intended_y = |
| 103 | 1194.0 + (std::get<1>(*AP_GetPlayerPosition()) * (154.0 / 14.0)); | 112 | 1194.0 + (std::get<1>(*player_position) * (154.0 / 14.0)); |
| 104 | 113 | ||
| 105 | int real_x = offset_x_ + scale_x_ * intended_x - scaled_player_.GetWidth() / 2; | 114 | int real_x = |
| 106 | int real_y = offset_y_ + scale_y_ * intended_y - scaled_player_.GetHeight() / 2; | 115 | offset_x_ + scale_x_ * intended_x - scaled_player_.GetWidth() / 2; |
| 116 | int real_y = | ||
| 117 | offset_y_ + scale_y_ * intended_y - scaled_player_.GetHeight() / 2; | ||
| 107 | 118 | ||
| 108 | dc.DrawBitmap(scaled_player_, real_x, real_y); | 119 | dc.DrawBitmap(scaled_player_, real_x, real_y); |
| 109 | } | 120 | } |
| @@ -182,8 +193,8 @@ void TrackerPanel::Redraw() { | |||
| 182 | if (panels_mode_) { | 193 | if (panels_mode_) { |
| 183 | area.active = map_area.has_single_panel; | 194 | area.active = map_area.has_single_panel; |
| 184 | } else if (!AP_IsLocationVisible(map_area.classification) && | 195 | } else if (!AP_IsLocationVisible(map_area.classification) && |
| 185 | !(map_area.hunt && GetTrackerConfig().show_hunt_panels) && | 196 | !(map_area.hunt && GetTrackerConfig().show_hunt_panels) && |
| 186 | !(AP_IsPaintingShuffle() && !map_area.paintings.empty())) { | 197 | !(AP_IsPaintingShuffle() && !map_area.paintings.empty())) { |
| 187 | area.active = false; | 198 | area.active = false; |
| 188 | } else { | 199 | } else { |
| 189 | area.active = true; | 200 | area.active = true; |
