about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorStar Rauchenberger <fefferburbia@gmail.com>2024-12-17 15:40:19 -0500
committerStar Rauchenberger <fefferburbia@gmail.com>2024-12-17 15:40:19 -0500
commitad7c3e616fdc6f13812ec52675d226a19351494a (patch)
tree4cdd8b27c6df4e5f3475fe1b3f1815689b387fd7
parent5ea44a418ec5db446dd28daf5ed95f46fea504f8 (diff)
downloadlingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.tar.gz
lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.tar.bz2
lingo-ap-tracker-ad7c3e616fdc6f13812ec52675d226a19351494a.zip
Added getting player position from IPC
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/ap_state.cpp43
-rw-r--r--src/ap_state.h2
-rw-r--r--src/ipc_state.cpp231
-rw-r--r--src/ipc_state.h20
-rw-r--r--src/tracker_frame.cpp49
-rw-r--r--src/tracker_frame.h5
-rw-r--r--src/tracker_panel.cpp27
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)
54set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) 55set_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
550std::string AP_GetStatusMessage() { return GetState().GetStatusMessage(); }
551
530std::string AP_GetSaveName() { return GetState().save_name; } 552std::string AP_GetSaveName() { return GetState().save_name; }
531 553
532bool AP_HasCheckedGameLocation(int location_id) { 554bool 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
44void AP_Connect(std::string server, std::string player, std::string password); 44void AP_Connect(std::string server, std::string player, std::string password);
45 45
46std::string AP_GetStatusMessage();
47
46std::string AP_GetSaveName(); 48std::string AP_GetSaveName();
47 49
48bool AP_HasCheckedGameLocation(int location_id); 50bool 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
20namespace {
21
22constexpr const char* kIpcAddress = "ws://127.0.0.1:41253";
23
24struct 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
210IPCState& GetState() {
211 static IPCState* instance = new IPCState();
212 return *instance;
213}
214
215} // namespace
216
217void IPC_Start(TrackerFrame* tracker_frame) { GetState().Start(tracker_frame); }
218
219std::optional<std::string> IPC_GetStatusMessage() {
220 return GetState().GetStatusMessage();
221}
222
223void IPC_SetTrackerSlot(std::string server, std::string user) {
224 GetState().SetTrackerSlot(server, user);
225}
226
227bool IPC_IsConnected() { return GetState().IsConnected(); }
228
229std::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
8class TrackerFrame;
9
10void IPC_Start(TrackerFrame* tracker_frame);
11
12std::optional<std::string> IPC_GetStatusMessage();
13
14void IPC_SetTrackerSlot(std::string server, std::string user);
15
16bool IPC_IsConnected();
17
18std::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
24namespace {
25
26std::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
23enum TrackerFrameIds { 40enum 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 {
32wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); 49wxDEFINE_EVENT(STATE_RESET, wxCommandEvent);
33wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); 50wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent);
34wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); 51wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent);
52wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent);
35 53
36TrackerFrame::TrackerFrame() 54TrackerFrame::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
120void 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
142void TrackerFrame::UpdateStatusMessage() {
143 QueueEvent(new wxCommandEvent(STATUS_CHANGED));
125} 144}
126 145
127void TrackerFrame::ResetIndicators() { 146void 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
154void TrackerFrame::RedrawPosition() {
155 QueueEvent(new wxCommandEvent(REDRAW_POSITION));
156}
157
135void TrackerFrame::OnAbout(wxCommandEvent &event) { 158void 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
203void TrackerFrame::OnZoomOut(wxCommandEvent& event) { 226void 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
214void TrackerFrame::OnOpenFile(wxCommandEvent& event) { 237void 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
236void TrackerFrame::OnStateReset(wxCommandEvent& event) { 259void 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
257void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { 280void TrackerFrame::OnStatusChanged(wxCommandEvent &event) {
258 SetStatusText(event.GetString()); 281 SetStatusText(GetStatusMessage());
282}
283
284void TrackerFrame::OnRedrawPosition(wxCommandEvent &event) {
285 if (notebook_->GetSelection() == 0) {
286 tracker_panel_->Refresh();
287 }
259} 288}
260 289
261void TrackerFrame::CheckForUpdates(bool manual) { 290void 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;
16wxDECLARE_EVENT(STATE_RESET, wxCommandEvent); 16wxDECLARE_EVENT(STATE_RESET, wxCommandEvent);
17wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent); 17wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent);
18wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent); 18wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent);
19wxDECLARE_EVENT(REDRAW_POSITION, wxCommandEvent);
19 20
20class TrackerFrame : public wxFrame { 21class 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
60void TrackerPanel::SetSavedataPath(std::string savedata_path) { 61void 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;