From f8f55976533ac3b77bb8d31697ba2f1e54a994c1 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Sat, 8 Mar 2025 10:52:51 -0500 Subject: Made indicator updates more fine-grained --- src/ap_state.cpp | 93 +++++++++++++++++++++++++++++--------------------- src/ap_state.h | 6 ++++ src/ipc_state.cpp | 4 +-- src/paintings_pane.cpp | 12 +++---- src/paintings_pane.h | 3 +- src/subway_map.cpp | 2 ++ src/tracker_frame.cpp | 73 +++++++++++++++++++-------------------- src/tracker_frame.h | 37 ++++++++++++++------ 8 files changed, 133 insertions(+), 97 deletions(-) (limited to 'src') diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 29e649f..d01290b 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp @@ -243,7 +243,7 @@ struct APState { checked_paintings.count(painting_mapping.at(painting_id))); } - std::string GetItemName(int id) { return apclient->get_item_name(id); } + std::string GetItemName(int id) { return apclient->get_item_name(id, "Lingo"); } void RevealPaintings() { std::lock_guard state_guard(state_mutex); @@ -369,7 +369,7 @@ struct APState { } } - RefreshTracker(false); + RefreshTracker(StateUpdate{.cleared_locations = true}); } void OnSlotDisconnected() { @@ -391,51 +391,53 @@ struct APState { } void OnItemsReceived(const std::list& items) { + std::vector item_states; + { std::lock_guard state_guard(state_mutex); + std::map index_by_item; + for (const APClient::NetworkItem& item : items) { inventory[item.item]++; TrackerLog(fmt::format("Item: {}", item.item)); + + index_by_item[item.item] = item.index; + } + + for (const auto& [item_id, item_index] : index_by_item) { + item_states.push_back(ItemState{.name = GetItemName(item_id), + .amount = inventory[item_id], + .index = item_index}); } } - RefreshTracker(false); + RefreshTracker(StateUpdate{.items = item_states}); } void OnRetrieved(const std::map& data) { + StateUpdate state_update; + { std::lock_guard state_guard(state_mutex); for (const auto& [key, value] : data) { - HandleDataStorage(key, value); + HandleDataStorage(key, value, state_update); } } - RefreshTracker(false); + RefreshTracker(state_update); } void OnSetReply(const std::string& key, const nlohmann::json& value) { - bool should_refresh = false; - bool should_redraw_position = false; + StateUpdate state_update; + { std::lock_guard state_guard(state_mutex); - HandleDataStorage(key, value); - - if (key.ends_with("PlayerPos")) - { - should_redraw_position = true; - } else { - should_refresh = true; - } + HandleDataStorage(key, value, state_update); } - if (should_refresh) - { - RefreshTracker(false); - } else if (should_redraw_position) { - tracker_frame->RedrawPosition(); - } + RefreshTracker(state_update); } void OnSlotConnected(std::string player, std::string server, @@ -531,7 +533,7 @@ struct APState { } ResetReachabilityRequirements(); - RefreshTracker(true); + RefreshTracker(std::nullopt); } void OnSlotRefused(const std::list& errors) { @@ -574,11 +576,15 @@ struct APState { } // Assumes state mutex is locked. - void HandleDataStorage(const std::string& key, const nlohmann::json& value) { + void HandleDataStorage(const std::string& key, const nlohmann::json& value, StateUpdate& state_update) { if (value.is_boolean()) { data_storage[key] = value.get(); TrackerLog(fmt::format("Data storage {} retrieved as {}", key, (value.get() ? "true" : "false"))); + + if (key.find("Achievement|") != std::string::npos) { + state_update.achievements = true; + } } else if (value.is_number()) { data_storage[key] = value.get(); TrackerLog(fmt::format("Data storage {} retrieved as {}", key, @@ -587,6 +593,7 @@ struct APState { if (key.ends_with("PlayerPos")) { auto map_value = value.get>(); player_pos = std::tuple(map_value["x"], map_value["z"]); + state_update.player_position = true; } else { data_storage[key] = value.get>(); } @@ -595,6 +602,7 @@ struct APState { } else if (value.is_null()) { if (key.ends_with("PlayerPos")) { player_pos = std::nullopt; + state_update.player_position = true; } else { data_storage.erase(key); } @@ -606,6 +614,8 @@ struct APState { if (key.ends_with("Paintings")) { data_storage[key] = std::set(list_value.begin(), list_value.end()); + state_update.paintings = + std::vector(list_value.begin(), list_value.end()); } else { data_storage[key] = list_value; } @@ -616,29 +626,34 @@ struct APState { } // State mutex should NOT be locked. - void RefreshTracker(bool reset) { + // nullopt state_update indicates a reset. + void RefreshTracker(std::optional state_update) { TrackerLog("Refreshing display..."); - std::string prev_msg; - { - std::lock_guard state_guard(state_mutex); + if (!state_update || !state_update->items.empty() || + !state_update->paintings.empty()) { + std::string prev_msg; + { + std::lock_guard state_guard(state_mutex); - prev_msg = status_message; - SetStatusMessage(fmt::format("{} Recalculating...", status_message)); - } + prev_msg = status_message; + SetStatusMessage(fmt::format("{} Recalculating...", status_message)); + } - RecalculateReachability(); + RecalculateReachability(); - if (reset) { - tracker_frame->ResetIndicators(); - } else { - tracker_frame->UpdateIndicators(); - } + { + std::lock_guard state_guard(state_mutex); - { - std::lock_guard state_guard(state_mutex); + SetStatusMessage(prev_msg); + } + } + - SetStatusMessage(prev_msg); + if (!state_update) { + tracker_frame->ResetIndicators(); + } else { + tracker_frame->UpdateIndicators(*state_update); } } diff --git a/src/ap_state.h b/src/ap_state.h index 4de7689..8b8db03 100644 --- a/src/ap_state.h +++ b/src/ap_state.h @@ -39,6 +39,12 @@ struct SunwarpMapping { int exit_index; }; +struct ItemState { + std::string name; + int amount = 0; + int index = 0; +}; + void AP_SetTrackerFrame(TrackerFrame* tracker_frame); void AP_Connect(std::string server, std::string player, std::string password); diff --git a/src/ipc_state.cpp b/src/ipc_state.cpp index 1f8d286..a99fa89 100644 --- a/src/ipc_state.cpp +++ b/src/ipc_state.cpp @@ -313,7 +313,7 @@ struct IPCState { player_position = std::make_tuple(msg["position"]["x"], msg["position"]["z"]); - tracker_frame->RedrawPosition(); + tracker_frame->UpdateIndicators(StateUpdate{.player_position = true}); } else if (msg["cmd"] == "SolvePanels") { std::lock_guard state_guard(state_mutex); @@ -321,7 +321,7 @@ struct IPCState { solved_panels.insert(std::move(panel)); } - tracker_frame->UpdateIndicators(kUPDATE_ONLY_PANELS); + tracker_frame->UpdateIndicators(StateUpdate{.open_panels_tab = true}); } } diff --git a/src/paintings_pane.cpp b/src/paintings_pane.cpp index dc6f050..51c4995 100644 --- a/src/paintings_pane.cpp +++ b/src/paintings_pane.cpp @@ -39,15 +39,15 @@ PaintingsPane::PaintingsPane(wxWindow* parent) : wxPanel(parent, wxID_ANY) { reveal_btn_->Bind(wxEVT_BUTTON, &PaintingsPane::OnClickRevealPaintings, this); } -void PaintingsPane::UpdateIndicators() { +void PaintingsPane::ResetIndicators() { tree_ctrl_->DeleteAllItems(); + reveal_btn_->Enable(AP_IsPaintingShuffle()); +} - if (!AP_IsPaintingShuffle()) { - reveal_btn_->Disable(); - return; - } +void PaintingsPane::UpdateIndicators(const std::vector&) { + // TODO: Optimize this by using the paintings delta. - reveal_btn_->Enable(); + tree_ctrl_->DeleteAllItems(); std::map> grouped_paintings; diff --git a/src/paintings_pane.h b/src/paintings_pane.h index 4c7856f..2b79286 100644 --- a/src/paintings_pane.h +++ b/src/paintings_pane.h @@ -13,7 +13,8 @@ class PaintingsPane : public wxPanel { public: explicit PaintingsPane(wxWindow* parent); - void UpdateIndicators(); + void ResetIndicators(); + void UpdateIndicators(const std::vector& paintings); private: void OnClickRevealPaintings(wxCommandEvent& event); diff --git a/src/subway_map.cpp b/src/subway_map.cpp index f742d12..9a35eef 100644 --- a/src/subway_map.cpp +++ b/src/subway_map.cpp @@ -169,6 +169,8 @@ void SubwayMap::OnConnect() { } checked_paintings_.clear(); + + UpdateIndicators(); } void SubwayMap::UpdateIndicators() { diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index a697f9a..dc6c283 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp @@ -51,9 +51,8 @@ enum TrackerFrameIds { }; wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); -wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); +wxDEFINE_EVENT(STATE_CHANGED, StateChangedEvent); wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); -wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent); wxDEFINE_EVENT(CONNECT_TO_AP, ApConnectEvent); TrackerFrame::TrackerFrame() @@ -112,7 +111,6 @@ TrackerFrame::TrackerFrame() Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); - Bind(REDRAW_POSITION, &TrackerFrame::OnRedrawPosition, this); Bind(CONNECT_TO_AP, &TrackerFrame::OnConnectToAp, this); wxSize logicalSize = FromDIP(wxSize(1280, 728)); @@ -173,15 +171,8 @@ void TrackerFrame::ResetIndicators() { QueueEvent(new wxCommandEvent(STATE_RESET)); } -void TrackerFrame::UpdateIndicators(UpdateIndicatorsMode mode) { - auto evt = new wxCommandEvent(STATE_CHANGED); - evt->SetInt(static_cast(mode)); - - QueueEvent(evt); -} - -void TrackerFrame::RedrawPosition() { - QueueEvent(new wxCommandEvent(REDRAW_POSITION)); +void TrackerFrame::UpdateIndicators(StateUpdate state) { + QueueEvent(new StateChangedEvent(STATE_CHANGED, GetId(), std::move(state))); } void TrackerFrame::OnAbout(wxCommandEvent &event) { @@ -250,7 +241,8 @@ void TrackerFrame::OnSettings(wxCommandEvent &event) { GetTrackerConfig().track_position = dlg.GetTrackPosition(); GetTrackerConfig().Save(); - UpdateIndicators(); + UpdateIndicators( + StateUpdate{.cleared_locations = true, .player_position = true}); } } @@ -304,7 +296,7 @@ void TrackerFrame::OnSashPositionChanged(wxSplitterEvent& event) { void TrackerFrame::OnStateReset(wxCommandEvent &event) { tracker_panel_->UpdateIndicators(); achievements_pane_->UpdateIndicators(); - paintings_pane_->UpdateIndicators(); + paintings_pane_->ResetIndicators(); subway_map_->OnConnect(); if (panels_panel_ != nullptr) { notebook_->DeletePage(notebook_->FindPage(panels_panel_)); @@ -313,19 +305,10 @@ void TrackerFrame::OnStateReset(wxCommandEvent &event) { Refresh(); } -void TrackerFrame::OnStateChanged(wxCommandEvent &event) { - UpdateIndicatorsMode mode = static_cast(event.GetInt()); +void TrackerFrame::OnStateChanged(StateChangedEvent &event) { + const StateUpdate &state = event.GetState(); - if (mode == kUPDATE_ALL_INDICATORS) { - tracker_panel_->UpdateIndicators(); - achievements_pane_->UpdateIndicators(); - paintings_pane_->UpdateIndicators(); - subway_map_->UpdateIndicators(); - if (panels_panel_ != nullptr) { - panels_panel_->UpdateIndicators(); - } - Refresh(); - } else if (mode == kUPDATE_ONLY_PANELS) { + if (state.open_panels_tab) { if (panels_panel_ == nullptr) { panels_panel_ = new TrackerPanel(notebook_); panels_panel_->SetPanelsMode(); @@ -335,25 +318,39 @@ void TrackerFrame::OnStateChanged(wxCommandEvent &event) { if (notebook_->GetSelection() == 2) { Refresh(); } - } -} - -void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { - SetStatusText(wxString::FromUTF8(GetStatusMessage())); -} -void TrackerFrame::OnRedrawPosition(wxCommandEvent &event) { - if (!GetTrackerConfig().track_position) { return; } + + if (!state.items.empty() || !state.paintings.empty() || + state.cleared_locations) { + tracker_panel_->UpdateIndicators(); + subway_map_->UpdateIndicators(); + if (panels_panel_ != nullptr) { + panels_panel_->UpdateIndicators(); + } + Refresh(); + } else if (state.player_position && GetTrackerConfig().track_position) { + if (notebook_->GetSelection() == 0) { + tracker_panel_->Refresh(); + } else if (notebook_->GetSelection() == 2) { + panels_panel_->Refresh(); + } + } - if (notebook_->GetSelection() == 0) { - tracker_panel_->Refresh(); - } else if (notebook_->GetSelection() == 2) { - panels_panel_->Refresh(); + if (state.achievements) { + achievements_pane_->UpdateIndicators(); + } + + if (!state.paintings.empty()) { + paintings_pane_->UpdateIndicators(state.paintings); } } +void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { + SetStatusText(wxString::FromUTF8(GetStatusMessage())); +} + void TrackerFrame::OnConnectToAp(ApConnectEvent &event) { AP_Connect(event.GetServer(), event.GetUser(), event.GetPass()); } diff --git a/src/tracker_frame.h b/src/tracker_frame.h index 4f1f464..ff18e49 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h @@ -9,6 +9,7 @@ #include +#include "ap_state.h" #include "icons.h" #include "updater.h" @@ -44,17 +45,33 @@ class ApConnectEvent : public wxEvent { std::string ap_pass_; }; +struct StateUpdate { + std::vector items; + std::vector paintings; + bool achievements = false; + bool open_panels_tab = false; + bool cleared_locations = false; + bool player_position = false; +}; + +class StateChangedEvent : public wxEvent { + public: + StateChangedEvent(wxEventType eventType, int winid, StateUpdate state) + : wxEvent(winid, eventType), state_(std::move(state)) {} + + const StateUpdate &GetState() const { return state_; } + + virtual wxEvent *Clone() const { return new StateChangedEvent(*this); } + + private: + StateUpdate state_; +}; + wxDECLARE_EVENT(STATE_RESET, wxCommandEvent); -wxDECLARE_EVENT(STATE_CHANGED, wxCommandEvent); +wxDECLARE_EVENT(STATE_CHANGED, StateChangedEvent); wxDECLARE_EVENT(STATUS_CHANGED, wxCommandEvent); -wxDECLARE_EVENT(REDRAW_POSITION, wxCommandEvent); wxDECLARE_EVENT(CONNECT_TO_AP, ApConnectEvent); -enum UpdateIndicatorsMode { - kUPDATE_ALL_INDICATORS = 0, - kUPDATE_ONLY_PANELS = 1, -}; - class TrackerFrame : public wxFrame { public: TrackerFrame(); @@ -63,8 +80,7 @@ class TrackerFrame : public wxFrame { void UpdateStatusMessage(); void ResetIndicators(); - void UpdateIndicators(UpdateIndicatorsMode mode = kUPDATE_ALL_INDICATORS); - void RedrawPosition(); + void UpdateIndicators(StateUpdate state); private: void OnExit(wxCommandEvent &event); @@ -80,9 +96,8 @@ class TrackerFrame : public wxFrame { void OnSashPositionChanged(wxSplitterEvent &event); void OnStateReset(wxCommandEvent &event); - void OnStateChanged(wxCommandEvent &event); + void OnStateChanged(StateChangedEvent &event); void OnStatusChanged(wxCommandEvent &event); - void OnRedrawPosition(wxCommandEvent &event); void OnConnectToAp(ApConnectEvent &event); std::unique_ptr updater_; -- cgit 1.4.1