#include "tracker_frame.h" #include #include #include #include #include #include #include #include #include #include "achievements_pane.h" #include "ap_state.h" #include "connection_dialog.h" #include "ipc_dialog.h" #include "ipc_state.h" #include "settings_dialog.h" #include "subway_map.h" #include "tracker_config.h" #include "tracker_panel.h" #include "version.h" namespace { std::string GetStatusMessage() { std::string msg = AP_GetStatusMessage(); std::optional ipc_msg = IPC_GetStatusMessage(); if (ipc_msg) { msg += " "; msg += *ipc_msg; } return msg; } } // namespace enum TrackerFrameIds { ID_AP_CONNECT = 1, ID_CHECK_FOR_UPDATES = 2, ID_SETTINGS = 3, ID_ZOOM_IN = 4, ID_ZOOM_OUT = 5, ID_OPEN_SAVE_FILE = 6, ID_IPC_CONNECT = 7, }; wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); wxDEFINE_EVENT(STATE_CHANGED, wxCommandEvent); wxDEFINE_EVENT(STATUS_CHANGED, wxCommandEvent); wxDEFINE_EVENT(REDRAW_POSITION, wxCommandEvent); wxDEFINE_EVENT(CONNECT_TO_AP, ApConnectEvent); TrackerFrame::TrackerFrame() : wxFrame(nullptr, wxID_ANY, "Lingo Archipelago Tracker", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxFULL_REPAINT_ON_RESIZE) { ::wxInitAllImageHandlers(); AP_SetTrackerFrame(this); IPC_SetTrackerFrame(this); wxMenu *menuFile = new wxMenu(); menuFile->Append(ID_AP_CONNECT, "&Connect to Archipelago"); menuFile->Append(ID_IPC_CONNECT, "&Connect to Lingo"); menuFile->Append(ID_OPEN_SAVE_FILE, "&Open Save Data\tCtrl-O"); menuFile->Append(ID_SETTINGS, "&Settings"); menuFile->Append(wxID_EXIT); wxMenu *menuView = new wxMenu(); zoom_in_menu_item_ = menuView->Append(ID_ZOOM_IN, "Zoom In\tCtrl-+"); zoom_out_menu_item_ = menuView->Append(ID_ZOOM_OUT, "Zoom Out\tCtrl--"); zoom_in_menu_item_->Enable(false); zoom_out_menu_item_->Enable(false); wxMenu *menuHelp = new wxMenu(); menuHelp->Append(wxID_ABOUT); menuHelp->Append(ID_CHECK_FOR_UPDATES, "Check for Updates"); wxMenuBar *menuBar = new wxMenuBar(); menuBar->Append(menuFile, "&File"); menuBar->Append(menuView, "&View"); menuBar->Append(menuHelp, "&Help"); SetMenuBar(menuBar); CreateStatusBar(); Bind(wxEVT_MENU, &TrackerFrame::OnAbout, this, wxID_ABOUT); Bind(wxEVT_MENU, &TrackerFrame::OnExit, this, wxID_EXIT); Bind(wxEVT_MENU, &TrackerFrame::OnApConnect, this, ID_AP_CONNECT); Bind(wxEVT_MENU, &TrackerFrame::OnIpcConnect, this, ID_IPC_CONNECT); Bind(wxEVT_MENU, &TrackerFrame::OnSettings, this, ID_SETTINGS); Bind(wxEVT_MENU, &TrackerFrame::OnCheckForUpdates, this, ID_CHECK_FOR_UPDATES); Bind(wxEVT_MENU, &TrackerFrame::OnZoomIn, this, ID_ZOOM_IN); Bind(wxEVT_MENU, &TrackerFrame::OnZoomOut, this, ID_ZOOM_OUT); Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this); Bind(wxEVT_MENU, &TrackerFrame::OnOpenFile, this, ID_OPEN_SAVE_FILE); 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); wxChoicebook *choicebook = new wxChoicebook(this, wxID_ANY); achievements_pane_ = new AchievementsPane(choicebook); choicebook->AddPage(achievements_pane_, "Achievements"); notebook_ = new wxNotebook(this, wxID_ANY); tracker_panel_ = new TrackerPanel(notebook_); subway_map_ = new SubwayMap(notebook_); notebook_->AddPage(tracker_panel_, "Map"); notebook_->AddPage(subway_map_, "Subway"); wxBoxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL); top_sizer->Add(choicebook, wxSizerFlags().Expand().Proportion(1)); top_sizer->Add(notebook_, wxSizerFlags().Expand().Proportion(3)); SetSizerAndFit(top_sizer); SetSize(1280, 728); if (!GetTrackerConfig().asked_to_check_for_updates) { GetTrackerConfig().asked_to_check_for_updates = true; if (wxMessageBox( "Check for updates automatically when the tracker is opened?", "Lingo AP Tracker", wxYES_NO) == wxYES) { GetTrackerConfig().should_check_for_updates = true; } else { GetTrackerConfig().should_check_for_updates = false; } GetTrackerConfig().Save(); } if (GetTrackerConfig().should_check_for_updates) { CheckForUpdates(/*manual=*/false); } SetStatusText(GetStatusMessage()); } void TrackerFrame::ConnectToAp(std::string server, std::string user, std::string pass) { QueueEvent(new ApConnectEvent(CONNECT_TO_AP, GetId(), std::move(server), std::move(user), std::move(pass))); } void TrackerFrame::UpdateStatusMessage() { QueueEvent(new wxCommandEvent(STATUS_CHANGED)); } 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::OnAbout(wxCommandEvent &event) { wxAboutDialogInfo about_info; about_info.SetName("Lingo Archipelago Tracker"); about_info.SetVersion(kTrackerVersion.ToString()); about_info.AddDeveloper("hatkirby"); about_info.AddDeveloper("art0007i"); about_info.AddArtist("Brenton Wildes"); about_info.AddArtist("kinrah"); wxAboutBox(about_info); } void TrackerFrame::OnExit(wxCommandEvent &event) { Close(true); } void TrackerFrame::OnApConnect(wxCommandEvent &event) { ConnectionDialog dlg; if (dlg.ShowModal() == wxID_OK) { GetTrackerConfig().connection_details.ap_server = dlg.GetServerValue(); GetTrackerConfig().connection_details.ap_player = dlg.GetPlayerValue(); GetTrackerConfig().connection_details.ap_password = dlg.GetPasswordValue(); std::deque new_history; new_history.push_back(GetTrackerConfig().connection_details); for (const ConnectionDetails &details : GetTrackerConfig().connection_history) { if (details != GetTrackerConfig().connection_details) { new_history.push_back(details); } } while (new_history.size() > 10) { new_history.pop_back(); } GetTrackerConfig().connection_history = std::move(new_history); GetTrackerConfig().Save(); AP_Connect(dlg.GetServerValue(), dlg.GetPlayerValue(), dlg.GetPasswordValue()); } } void TrackerFrame::OnIpcConnect(wxCommandEvent &event) { IpcDialog dlg; if (dlg.ShowModal() == wxID_OK) { GetTrackerConfig().ipc_address = dlg.GetIpcAddress(); GetTrackerConfig().Save(); IPC_Connect(dlg.GetIpcAddress()); } } void TrackerFrame::OnSettings(wxCommandEvent &event) { SettingsDialog dlg; if (dlg.ShowModal() == wxID_OK) { GetTrackerConfig().should_check_for_updates = dlg.GetShouldCheckForUpdates(); GetTrackerConfig().hybrid_areas = dlg.GetHybridAreas(); GetTrackerConfig().show_hunt_panels = dlg.GetShowHuntPanels(); GetTrackerConfig().Save(); UpdateIndicators(); } } void TrackerFrame::OnCheckForUpdates(wxCommandEvent &event) { CheckForUpdates(/*manual=*/true); } void TrackerFrame::OnZoomIn(wxCommandEvent &event) { if (notebook_->GetSelection() == 1) { subway_map_->Zoom(true); } } void TrackerFrame::OnZoomOut(wxCommandEvent &event) { if (notebook_->GetSelection() == 1) { subway_map_->Zoom(false); } } void TrackerFrame::OnChangePage(wxBookCtrlEvent &event) { zoom_in_menu_item_->Enable(event.GetSelection() == 1); zoom_out_menu_item_->Enable(event.GetSelection() == 1); } void TrackerFrame::OnOpenFile(wxCommandEvent &event) { wxFileDialog open_file_dialog( this, "Open Lingo Save File", fmt::format("{}\\Godot\\app_userdata\\Lingo\\level1_stable", wxStandardPaths::Get().GetUserConfigDir().ToStdString()), AP_GetSaveName(), "Lingo save file (*.save)|*.save", wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (open_file_dialog.ShowModal() == wxID_CANCEL) { return; } std::string savedata_path = open_file_dialog.GetPath().ToStdString(); if (panels_panel_ == nullptr) { panels_panel_ = new TrackerPanel(notebook_); notebook_->AddPage(panels_panel_, "Panels"); } notebook_->SetSelection(notebook_->FindPage(panels_panel_)); panels_panel_->SetSavedataPath(savedata_path); } void TrackerFrame::OnStateReset(wxCommandEvent &event) { tracker_panel_->UpdateIndicators(); achievements_pane_->UpdateIndicators(); subway_map_->OnConnect(); if (panels_panel_ != nullptr) { notebook_->DeletePage(notebook_->FindPage(panels_panel_)); panels_panel_ = nullptr; } Refresh(); } void TrackerFrame::OnStateChanged(wxCommandEvent &event) { UpdateIndicatorsMode mode = static_cast(event.GetInt()); if (mode == kUPDATE_ALL_INDICATORS) { tracker_panel_->UpdateIndicators(); achievements_pane_->UpdateIndicators(); subway_map_->UpdateIndicators(); if (panels_panel_ != nullptr) { panels_panel_->UpdateIndicators(); } Refresh(); } else if (mode == kUPDATE_ONLY_PANELS) { if (panels_panel_ == nullptr) { panels_panel_ = new TrackerPanel(notebook_); panels_panel_->SetPanelsMode(); notebook_->AddPage(panels_panel_, "Panels"); } panels_panel_->UpdateIndicators(); if (notebook_->GetSelection() == 2) { Refresh(); } } } void TrackerFrame::OnStatusChanged(wxCommandEvent &event) { SetStatusText(GetStatusMessage()); } void TrackerFrame::OnRedrawPosition(wxCommandEvent &event) { if (notebook_->GetSelection() == 0) { tracker_panel_->Refresh(); } else if (notebook_->GetSelection() == 2) { panels_panel_->Refresh(); } } void TrackerFrame::OnConnectToAp(ApConnectEvent &event) { AP_Connect(event.GetServer(), event.GetUser(), event.GetPass()); } void TrackerFrame::CheckForUpdates(bool manual) { wxWebRequest request = wxWebSession::GetDefault().CreateRequest( this, "https://code.fourisland.com/lingo-ap-tracker/plain/VERSION"); if (!request.IsOk()) { if (manual) { wxMessageBox("Could not check for updates.", "Error", wxOK | wxICON_ERROR); } else { SetStatusText("Could not check for updates."); } return; } Bind(wxEVT_WEBREQUEST_STATE, [this, manual](wxWebRequestEvent &evt) { if (evt.GetState() == wxWebRequest::State_Completed) { std::string response = evt.GetResponse().AsString().ToStdString(); Version latest_version(response); if (kTrackerVersion < latest_version) { std::ostringstream message_text; message_text << "There is a newer version of Lingo AP Tracker " "available. You have " << kTrackerVersion.ToString() << ", and the latest version is " << latest_version.ToString() << ". Would you like to update?"; if (wxMessageBox(message_text.str(), "Update available", wxYES_NO) == wxYES) { wxLaunchDefaultBrowser( "https://code.fourisland.com/lingo-ap-tracker/about/" "CHANGELOG.md"); } } else if (manual) { wxMessageBox("Lingo AP Tracker is up to date!", "Lingo AP Tracker", wxOK); } } else if (evt.GetState() == wxWebRequest::State_Failed) { if (manual) { wxMessageBox("Could not check for updates.", "Error", wxOK | wxICON_ERROR); } else { SetStatusText("Could not check for updates."); } } }); request.Start(); }