diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-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 |
8 files changed, 349 insertions, 29 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eef9d8..fd943b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -49,6 +49,7 @@ add_executable(lingo_ap_tracker | |||
49 | "src/network_set.cpp" | 49 | "src/network_set.cpp" |
50 | "src/logger.cpp" | 50 | "src/logger.cpp" |
51 | "src/godot_variant.cpp" | 51 | "src/godot_variant.cpp" |
52 | "src/ipc_state.cpp" | ||
52 | "vendor/whereami/whereami.c" | 53 | "vendor/whereami/whereami.c" |
53 | ) | 54 | ) |
54 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) | 55 | set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) |
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; |