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



                            
                              
                              
                       
                      
                       
                       
                         
                           
                            
                       
                           
                          
                    
 














                                                              
                      
                    
                           

                  
                        
                     
                    
  
 
                                            
                                                 
                                               
                                              
 
                            
                                                                                
                             
                           
                            
 
                           

                                             
                                  
                                                             
                                                                 
                                             
                              

                                                                          
                                                             


                                     
                                  
                                                              

                                       
                                     



                                     

                                                             
                                                                      
                                                                 
                                                          
                                                                
                                                                        
                                                                       
                                                                       
                                                                             
                                                       
                                                             
                                                          
 
                                                  
                                                          
                                                          
                                                                          
 
                                                        
                                                          

                                            

                                                

                                                    
                                                         


                                               
 
                                                                              
 
                       
 













                                                                          
                                                  
   
 

                                    




                                                                          
                                                 
 


                                              
                                                                              
 
                                                   


                                                    
                                      


                                         

                                                                 
 
                                                       

                                   





                                                                               
                                                 



                                                             
                                     


                                                                   
                              
                                                          
   
 









                                                         





                                                           
                                                                  
                                                               
                              

                                                            

   
                                                             
                                                 
 




                                                    
                                                     



                                       


















                                                                          



                                                         
                                                      



















                                                                           


                                                                  
                                                        
                                                   
                                         
                                 
                             
                                     
                           


                                                              

            
                                                             
 
                              



                                                  
                                                     

                                         
 
           
                                                         
                                                                   


                                                                          
                                    
                                                       







                                                                          
 


                                           


                                               
                                                       
   
 
 


                                                           

                                                                  
#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/splitter.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 "items_pane.h"
#include "log_dialog.h"
#include "logger.h"
#include "options_pane.h"
#include "paintings_pane.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,
  ID_LOG_DIALOG = 8,
};

wxDEFINE_EVENT(STATE_RESET, wxCommandEvent);
wxDEFINE_EVENT(STATE_CHANGED, StateChangedEvent);
wxDEFINE_EVENT(STATUS_CHANGED, 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);

  SetTheIconCache(&icons_);

  updater_ = std::make_unique<Updater>(this);
  updater_->Cleanup();

  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--");
  menuView->AppendSeparator();
  menuView->Append(ID_LOG_DIALOG, "Show Log Window\tCtrl-L");

  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_MENU, &TrackerFrame::OnOpenLogWindow, this, ID_LOG_DIALOG);
  Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this);
  Bind(wxEVT_MENU, &TrackerFrame::OnOpenFile, this, ID_OPEN_SAVE_FILE);
  Bind(wxEVT_SPLITTER_SASH_POS_CHANGED, &TrackerFrame::OnSashPositionChanged,
       this);
  Bind(STATE_RESET, &TrackerFrame::OnStateReset, this);
  Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this);
  Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this);
  Bind(CONNECT_TO_AP, &TrackerFrame::OnConnectToAp, this);

  wxSize logicalSize = FromDIP(wxSize(1280, 728));

  splitter_window_ = new wxSplitterWindow(this, wxID_ANY);
  splitter_window_->SetMinimumPaneSize(logicalSize.x / 5);

  wxChoicebook *choicebook = new wxChoicebook(splitter_window_, wxID_ANY);

  achievements_pane_ = new AchievementsPane(choicebook);
  choicebook->AddPage(achievements_pane_, "Achievements");

  items_pane_ = new ItemsPane(choicebook);
  choicebook->AddPage(items_pane_, "Items");

  options_pane_ = new OptionsPane(choicebook);
  choicebook->AddPage(options_pane_, "Options");

  paintings_pane_ = new PaintingsPane(choicebook);
  choicebook->AddPage(paintings_pane_, "Paintings");

  notebook_ = new wxNotebook(splitter_window_, wxID_ANY);
  tracker_panel_ = new TrackerPanel(notebook_);
  subway_map_ = new SubwayMap(notebook_);
  notebook_->AddPage(tracker_panel_, "Map");
  notebook_->AddPage(subway_map_, "Subway");

  splitter_window_->SplitVertically(choicebook, notebook_, logicalSize.x / 4);

  SetSize(logicalSize);

  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) {
    updater_->CheckForUpdates(/*invisible=*/true);
  }

  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(StateUpdate state) {
  QueueEvent(new StateChangedEvent(STATE_CHANGED, GetId(), std::move(state)));
}

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().track_position = dlg.GetTrackPosition();
    GetTrackerConfig().Save();

    UpdateIndicators(StateUpdate{.cleared_locations = true,
                                 .player_position = true,
                                 .changed_settings = true});
  }
}

void TrackerFrame::OnCheckForUpdates(wxCommandEvent &event) {
  updater_->CheckForUpdates(/*invisible=*/false);
}

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::OnOpenLogWindow(wxCommandEvent &event) {
  if (log_dialog_ == nullptr) {
    log_dialog_ = new LogDialog(this);
    log_dialog_->Show();
    TrackerSetLogDialog(log_dialog_);

    log_dialog_->Bind(wxEVT_CLOSE_WINDOW, &TrackerFrame::OnCloseLogWindow,
                      this);
  } else {
    log_dialog_->SetFocus();
  }
}

void TrackerFrame::OnCloseLogWindow(wxCloseEvent& event) {
  TrackerSetLogDialog(nullptr);
  log_dialog_ = nullptr;

  event.Skip();
}

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::OnSashPositionChanged(wxSplitterEvent& event) {
  notebook_->Refresh();
}

void TrackerFrame::OnStateReset(wxCommandEvent &event) {
  tracker_panel_->UpdateIndicators(/*reset=*/true);
  achievements_pane_->UpdateIndicators();
  items_pane_->ResetIndicators();
  options_pane_->OnConnect();
  paintings_pane_->ResetIndicators();
  subway_map_->OnConnect();
  if (panels_panel_ != nullptr) {
    notebook_->DeletePage(notebook_->FindPage(panels_panel_));
    panels_panel_ = nullptr;
  }
  Refresh();
}

void TrackerFrame::OnStateChanged(StateChangedEvent &event) {
  const StateUpdate &state = event.GetState();

  if (state.open_panels_tab) {
    if (panels_panel_ == nullptr) {
      panels_panel_ = new TrackerPanel(notebook_);
      panels_panel_->SetPanelsMode();
      notebook_->AddPage(panels_panel_, "Panels");
    }
    panels_panel_->UpdateIndicators(/*reset=*/false);
    if (notebook_->GetSelection() == 2) {
      Refresh();
    }

    return;
  }
  
  if (!state.items.empty() || !state.paintings.empty() ||
      state.cleared_locations ||
      (state.hunt_panels && GetTrackerConfig().show_hunt_panels)) {
    // TODO: The only real reason to reset tracker_panel during an active
    // connection is if the hunt panels setting changes. If we remove hunt
    // panels later, we can get rid of this.
    tracker_panel_->UpdateIndicators(/*reset=*/state.changed_settings);
    subway_map_->UpdateIndicators();
    if (panels_panel_ != nullptr) {
      panels_panel_->UpdateIndicators(/*reset=*/false);
    }
    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 (state.achievements) {
    achievements_pane_->UpdateIndicators();
  }

  if (!state.items.empty()) {
    items_pane_->UpdateIndicators(state.items);
  }

  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());
}