about summary refs log blame commit diff stats
path: root/src/tracker_frame.cpp
blob: 587d87b5c5d5a7f4852b6c026d52704d354c1ef2 (plain) (tree)
1
2
3
4
5
6
7
8
                          
                     
                        
                        
                       
                        
                        



                            
                              
                              
                       
                      
                            
                       
                           
                          
                    
 














                                                              
                      
                    
                           

                  
                        
                     
  
 
                                            
                                               
                                                
                                              
 
                            
                                                                                
                             
                           
                            
 
                                  
                                                             
                                                                 
                                             
                              





                                                                          
                                  
                                                              

                                       
                                     



                                     

                                                             
                                                                      
                                                                 
                                                          

                                                                       
                                                                       
                                                       
                                                             
                                                               
                                                          
 
                                                              
                                                        
                                                          



                                               
 
                                                                    
                                                                   
                            
                     
 















                                                                          
 

                                    




                                                                          
                                                 
 


                                              



                                                                
 


                                                  
                                                   


                                                    
                                      


                                         

                                                                 
 
                                                       

                                   





                                                                               
                                                 



                                                             
                                     


                                                                   
                              
                                                          
   
 









                                                         





                                                           
                                                                  




                              


                                                             




                                                    
                                                     








                                                         
                                                      



















                                                                           
                                                        

                                         


                                                              

            
                                                          














                                                                                
                                      

                                         
   

                                                           




                                                            
                                              
   
 
 


                                                                  
                                                                  
                                                                          













                                                                        




                                                                       


                                                      




                                                                             















                                                                           
#include "tracker_frame.h"

#include <fmt/core.h>
#include <wx/aboutdlg.h>
#include <wx/choicebk.h>
#include <wx/filedlg.h>
#include <wx/notebook.h>
#include <wx/stdpaths.h>
#include <wx/webrequest.h>

#include <nlohmann/json.hpp>
#include <sstream>

#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<std::string> 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<int>(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<ConnectionDetails> 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<UpdateIndicatorsMode>(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();
}