From 09d67fbad9df92caf2251d36b4abd7979fd27126 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 2 May 2023 17:26:46 -0400 Subject: Map + popups reflect checked locations --- .gitignore | 1 + CMakeLists.txt | 1 + ap_state.cpp | 37 ++++++++++++++++++++++++++++++++++--- ap_state.h | 17 +++++++++++------ area_popup.cpp | 48 +++++++++++++++++++++++++++++++----------------- area_popup.h | 7 +++++++ area_window.cpp | 20 +++++++++++++++++++- area_window.h | 2 ++ assets/checked.png | Bin 0 -> 888 bytes assets/unchecked.png | Bin 0 -> 5001 bytes eye_indicator.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ eye_indicator.h | 30 ++++++++++++++++++++++++++++++ game_data.cpp | 11 ++++++++--- game_data.h | 2 +- tracker_frame.cpp | 7 ++++++- tracker_frame.h | 6 ++++++ tracker_panel.cpp | 13 +++++++++++++ tracker_panel.h | 3 +++ 18 files changed, 220 insertions(+), 32 deletions(-) create mode 100644 assets/checked.png create mode 100644 assets/unchecked.png create mode 100644 eye_indicator.cpp create mode 100644 eye_indicator.h diff --git a/.gitignore b/.gitignore index 4a87eed..3fac228 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/ assets/LL1.yaml +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 89f3996..9c346a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(lingo_ap_tracker area_popup.cpp ap_state.cpp connection_dialog.cpp + eye_indicator.cpp ) set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 17) set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD_REQUIRED ON) diff --git a/ap_state.cpp b/ap_state.cpp index d85fde5..4d7ddb7 100644 --- a/ap_state.cpp +++ b/ap_state.cpp @@ -8,6 +8,8 @@ #include #include +#include "game_data.h" + constexpr int AP_MAJOR = 0; constexpr int AP_MINOR = 4; constexpr int AP_REVISION = 0; @@ -64,7 +66,7 @@ void APState::Connect(std::string server, std::string player, std::cout << "Location: " << location_id << std::endl; } - tracker_frame_->Refresh(); + RefreshTracker(); }); apclient_->set_slot_disconnected_handler([&]() { @@ -84,7 +86,7 @@ void APState::Connect(std::string server, std::string player, std::cout << "Item: " << item.item << std::endl; } - tracker_frame_->Refresh(); + RefreshTracker(); }); apclient_->set_slot_connected_handler([&](const nlohmann::json& slot_data) { @@ -151,11 +153,40 @@ void APState::Connect(std::string server, std::string player, interval--; } - if (!connected) { + if (connected) { + for (const MapArea& map_area : GetGameData().GetMapAreas()) { + for (int section_id = 0; section_id < map_area.locations.size(); + section_id++) { + const Location& location = map_area.locations.at(section_id); + + int64_t ap_id = apclient_->get_location_id(location.ap_location_name); + if (ap_id == APClient::INVALID_NAME_ID) { + std::cout << "Could not find AP location ID for " + << location.ap_location_name << std::endl; + } else { + ap_id_by_location_id_[{map_area.id, section_id}] = ap_id; + } + } + } + + RefreshTracker(); + } else { client_active_ = false; } } +bool APState::HasCheckedGameLocation(int area_id, int section_id) const { + std::tuple location_key = {area_id, section_id}; + + if (ap_id_by_location_id_.count(location_key)) { + return checked_locations_.count(ap_id_by_location_id_.at(location_key)); + } else { + return false; + } +} + +void APState::RefreshTracker() { tracker_frame_->UpdateIndicators(); } + APState& GetAPState() { static APState* instance = new APState(); return *instance; diff --git a/ap_state.h b/ap_state.h index 5f9e952..b5a94e3 100644 --- a/ap_state.h +++ b/ap_state.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "game_data.h" #include "tracker_frame.h" @@ -20,20 +21,24 @@ class APState { void Connect(std::string server, std::string player, std::string password); + bool HasCheckedGameLocation(int area_id, int section_id) const; + private: + void RefreshTracker(); + TrackerFrame* tracker_frame_; std::unique_ptr apclient_; bool client_active_ = false; std::mutex client_mutex_; - std::set inventory_; - std::set checked_locations_; + std::set inventory_; + std::set checked_locations_; - std::map ap_id_by_location_id_; - std::map ap_id_by_door_id_; - std::map ap_id_by_door_group_id_; - std::map ap_id_by_color_; + std::map, int64_t> ap_id_by_location_id_; + std::map ap_id_by_door_id_; + std::map ap_id_by_door_group_id_; + std::map ap_id_by_color_; }; APState& GetAPState(); diff --git a/area_popup.cpp b/area_popup.cpp index 62cbe4d..e46e4ec 100644 --- a/area_popup.cpp +++ b/area_popup.cpp @@ -1,37 +1,51 @@ #include "area_popup.h" +#include "ap_state.h" #include "game_data.h" AreaPopup::AreaPopup(wxWindow* parent, int area_id) : wxPanel(parent, wxID_ANY), area_id_(area_id) { const MapArea& map_area = GetGameData().GetMapArea(area_id); - wxBoxSizer* list_sizer = new wxBoxSizer(wxVERTICAL); + wxFlexGridSizer* section_sizer = new wxFlexGridSizer(2, 10, 10); - wxStaticText* top_label = new wxStaticText(this, -1, map_area.name); - top_label->SetForegroundColour(*wxBLACK); - top_label->SetFont(top_label->GetFont().Bold()); - list_sizer->Add(top_label, wxSizerFlags().Center().DoubleBorder(wxDOWN)); - - bool is_first = true; for (const Location& location : map_area.locations) { - wxSizerFlags sizer_flags = wxSizerFlags().Left(); - if (!is_first) { - sizer_flags = sizer_flags.Border(wxUP); - } + EyeIndicator* eye_indicator = new EyeIndicator(this); + section_sizer->Add(eye_indicator, wxSizerFlags().Expand()); + eye_indicators_.push_back(eye_indicator); wxStaticText* section_label = new wxStaticText(this, -1, location.name); - section_label->SetForegroundColour(*wxBLACK); - list_sizer->Add(section_label, sizer_flags); - - is_first = false; + section_label->SetForegroundColour(*wxWHITE); + section_sizer->Add( + section_label, + wxSizerFlags().Align(wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL)); + section_labels_.push_back(section_label); } wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL); - top_sizer->Add(list_sizer, wxSizerFlags().DoubleBorder(wxALL)); + + wxStaticText* top_label = new wxStaticText(this, -1, map_area.name); + top_label->SetForegroundColour(*wxWHITE); + top_label->SetFont(top_label->GetFont().Bold()); + top_sizer->Add(top_label, + wxSizerFlags().Center().DoubleBorder(wxUP | wxLEFT | wxRIGHT)); + + top_sizer->Add(section_sizer, wxSizerFlags().DoubleBorder(wxALL).Expand()); SetSizerAndFit(top_sizer); - SetBackgroundColour(*wxLIGHT_GREY); + SetBackgroundColour(*wxBLACK); Hide(); } + +void AreaPopup::UpdateIndicators() { + const MapArea& map_area = GetGameData().GetMapArea(area_id_); + for (int section_id = 0; section_id < map_area.locations.size(); + section_id++) { + bool checked = GetAPState().HasCheckedGameLocation(area_id_, section_id); + const wxColour* text_color = checked ? wxWHITE : wxGREEN; + + section_labels_[section_id]->SetForegroundColour(*text_color); + eye_indicators_[section_id]->SetChecked(checked); + } +} diff --git a/area_popup.h b/area_popup.h index c3de4bb..b602b63 100644 --- a/area_popup.h +++ b/area_popup.h @@ -7,12 +7,19 @@ #include #endif +#include "eye_indicator.h" + class AreaPopup : public wxPanel { public: AreaPopup(wxWindow* parent, int area_id); + void UpdateIndicators(); + private: int area_id_; + + std::vector section_labels_; + std::vector eye_indicators_; }; #endif /* end of include guard: AREA_POPUP_H_03FAC988 */ diff --git a/area_window.cpp b/area_window.cpp index 2f334e4..ca327b8 100644 --- a/area_window.cpp +++ b/area_window.cpp @@ -2,6 +2,7 @@ #include +#include "ap_state.h" #include "game_data.h" AreaWindow::AreaWindow(wxWindow* parent, int area_id, AreaPopup* popup) @@ -15,6 +16,8 @@ AreaWindow::AreaWindow(wxWindow* parent, int area_id, AreaPopup* popup) Bind(wxEVT_LEAVE_WINDOW, &AreaWindow::OnLeaveWindow, this); } +void AreaWindow::UpdateIndicators() { Redraw(); } + void AreaWindow::OnPaint(wxPaintEvent& event) { if (GetSize() != rendered_.GetSize()) { Redraw(); @@ -29,12 +32,27 @@ void AreaWindow::OnEnterWindow(wxMouseEvent& event) { popup_->Show(); } void AreaWindow::OnLeaveWindow(wxMouseEvent& event) { popup_->Hide(); } void AreaWindow::Redraw() { + const wxBrush* brush_color = wxGREY_BRUSH; + + const MapArea& map_area = GetGameData().GetMapArea(area_id_); + int unchecked_sections = 0; + for (int section_id = 0; section_id < map_area.locations.size(); + section_id++) { + if (!GetAPState().HasCheckedGameLocation(area_id_, section_id)) { + unchecked_sections++; + } + } + + if (unchecked_sections > 0) { + brush_color = wxGREEN_BRUSH; + } + int actual_border_size = GetSize().GetWidth() * BORDER_SIZE / EFFECTIVE_SIZE; rendered_ = wxBitmap(GetSize()); wxMemoryDC dc; dc.SelectObject(rendered_); dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, actual_border_size)); - dc.SetBrush(*wxGREEN_BRUSH); + dc.SetBrush(*brush_color); dc.DrawRectangle({0, 0}, GetSize()); } diff --git a/area_window.h b/area_window.h index c9abc4c..3e7c001 100644 --- a/area_window.h +++ b/area_window.h @@ -21,6 +21,8 @@ class AreaWindow : public wxWindow { AreaPopup* GetPopup() { return popup_; } + void UpdateIndicators(); + private: void OnPaint(wxPaintEvent& event); void OnEnterWindow(wxMouseEvent& event); diff --git a/assets/checked.png b/assets/checked.png new file mode 100644 index 0000000..ad3028c Binary files /dev/null and b/assets/checked.png differ diff --git a/assets/unchecked.png b/assets/unchecked.png new file mode 100644 index 0000000..4ff77a3 Binary files /dev/null and b/assets/unchecked.png differ diff --git a/eye_indicator.cpp b/eye_indicator.cpp new file mode 100644 index 0000000..c490589 --- /dev/null +++ b/eye_indicator.cpp @@ -0,0 +1,47 @@ +#include "eye_indicator.h" + +EyeIndicator::EyeIndicator(wxWindow* parent) : wxWindow(parent, wxID_ANY) { + SetMinSize({32, 32}); + + Redraw(); + + Bind(wxEVT_PAINT, &EyeIndicator::OnPaint, this); +} + +void EyeIndicator::SetChecked(bool checked) { + if (intended_checked_ != checked) { + intended_checked_ = checked; + + Redraw(); + } +} + +const wxImage& EyeIndicator::GetUncheckedImage() { + static wxImage* unchecked_image = + new wxImage("assets/unchecked.png", wxBITMAP_TYPE_PNG); + return *unchecked_image; +} + +const wxImage& EyeIndicator::GetCheckedImage() { + static wxImage* checked_image = + new wxImage("assets/checked.png", wxBITMAP_TYPE_PNG); + return *checked_image; +} + +void EyeIndicator::OnPaint(wxPaintEvent& event) { + if (GetSize() != rendered_.GetSize() || + intended_checked_ != rendered_checked_) { + Redraw(); + } + + wxPaintDC dc(this); + dc.DrawBitmap(rendered_, 0, 0); +} + +void EyeIndicator::Redraw() { + rendered_ = + wxBitmap((intended_checked_ ? GetCheckedImage() : GetUncheckedImage()) + .Scale(GetSize().GetWidth(), GetSize().GetHeight(), + wxIMAGE_QUALITY_NORMAL)); + rendered_checked_ = intended_checked_; +} diff --git a/eye_indicator.h b/eye_indicator.h new file mode 100644 index 0000000..e8fd890 --- /dev/null +++ b/eye_indicator.h @@ -0,0 +1,30 @@ +#ifndef EYE_INDICATOR_H_778150F2 +#define EYE_INDICATOR_H_778150F2 + +#include + +#ifndef WX_PRECOMP +#include +#endif + +class EyeIndicator : public wxWindow { + public: + EyeIndicator(wxWindow* parent); + + void SetChecked(bool checked); + + private: + static const wxImage& GetUncheckedImage(); + static const wxImage& GetCheckedImage(); + + void OnPaint(wxPaintEvent& event); + + void Redraw(); + + bool intended_checked_ = false; + + wxBitmap rendered_; + bool rendered_checked_ = false; +}; + +#endif /* end of include guard: EYE_INDICATOR_H_778150F2 */ diff --git a/game_data.cpp b/game_data.cpp index 54a01ed..af5f665 100644 --- a/game_data.cpp +++ b/game_data.cpp @@ -252,7 +252,10 @@ GameData::GameData() { MapArea &map_area = map_areas_[area_id]; // room field should be the original room ID map_area.locations.push_back( - {.name = panel.name, .room = panel.room, .panels = {panel.id}}); + {.name = panel.name, + .ap_location_name = room_name + " - " + panel.name, + .room = panel.room, + .panels = {panel.id}}); } } @@ -278,8 +281,10 @@ GameData::GameData() { int area_id = AddOrGetArea(area_name); MapArea &map_area = map_areas_[area_id]; // room field should be the original room ID - map_area.locations.push_back( - {.name = section_name, .room = door.room, .panels = door.panels}); + map_area.locations.push_back({.name = section_name, + .ap_location_name = door.location_name, + .room = door.room, + .panels = door.panels}); } } } diff --git a/game_data.h b/game_data.h index 00d6328..4f93d92 100644 --- a/game_data.h +++ b/game_data.h @@ -52,7 +52,7 @@ struct Room { struct Location { std::string name; - int location_id = -1; + std::string ap_location_name; int room; std::vector panels; }; diff --git a/tracker_frame.cpp b/tracker_frame.cpp index d58dfe8..cd2060c 100644 --- a/tracker_frame.cpp +++ b/tracker_frame.cpp @@ -34,13 +34,18 @@ TrackerFrame::TrackerFrame() Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); Bind(wxEVT_MENU, &TrackerFrame::OnConnect, this, ID_CONNECT); - new TrackerPanel(this); + tracker_panel_ = new TrackerPanel(this); } void TrackerFrame::SetStatusMessage(std::string message) { SetStatusText(message); } +void TrackerFrame::UpdateIndicators() { + tracker_panel_->UpdateIndicators(); + Refresh(); +} + void TrackerFrame::OnAbout(wxCommandEvent &event) { wxMessageBox("Lingo Archipelago Tracker by hatkirby", "About lingo-ap-tracker", wxOK | wxICON_INFORMATION); diff --git a/tracker_frame.h b/tracker_frame.h index 10dfc9d..082c12c 100644 --- a/tracker_frame.h +++ b/tracker_frame.h @@ -7,16 +7,22 @@ #include #endif +class TrackerPanel; + class TrackerFrame : public wxFrame { public: TrackerFrame(); void SetStatusMessage(std::string message); + void UpdateIndicators(); + private: void OnExit(wxCommandEvent &event); void OnAbout(wxCommandEvent &event); void OnConnect(wxCommandEvent &event); + + TrackerPanel *tracker_panel_; }; #endif /* end of include guard: TRACKER_FRAME_H_86BD8DFB */ diff --git a/tracker_panel.cpp b/tracker_panel.cpp index 0e78cc5..1aa7dcf 100644 --- a/tracker_panel.cpp +++ b/tracker_panel.cpp @@ -13,6 +13,7 @@ TrackerPanel::TrackerPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { AreaPopup *area_popup = new AreaPopup(this, map_area.id); area_popup->SetPosition({0, 0}); area_popup->Raise(); + area_popups_.push_back(area_popup); AreaWindow *area_window = new AreaWindow(this, map_area.id, area_popup); area_window->Lower(); @@ -24,6 +25,18 @@ TrackerPanel::TrackerPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { Bind(wxEVT_PAINT, &TrackerPanel::OnPaint, this); } +void TrackerPanel::UpdateIndicators() { + Redraw(); + + for (AreaWindow *area_window : area_windows_) { + area_window->UpdateIndicators(); + } + + for (AreaPopup *area_popup : area_popups_) { + area_popup->UpdateIndicators(); + } +} + void TrackerPanel::OnPaint(wxPaintEvent &event) { if (GetSize() != rendered_.GetSize()) { Redraw(); diff --git a/tracker_panel.h b/tracker_panel.h index ae15271..20d4f92 100644 --- a/tracker_panel.h +++ b/tracker_panel.h @@ -13,6 +13,8 @@ class TrackerPanel : public wxPanel { public: TrackerPanel(wxWindow *parent); + void UpdateIndicators(); + private: void OnPaint(wxPaintEvent &event); @@ -22,6 +24,7 @@ class TrackerPanel : public wxPanel { wxBitmap rendered_; std::vector area_windows_; + std::vector area_popups_; }; #endif /* end of include guard: TRACKER_PANEL_H_D675A54D */ -- cgit 1.4.1