#include "tracker_state.h" #include #include #include #include #include #include #include "ap_state.h" #include "game_data.h" namespace { struct Requirements { bool disabled = false; std::set doors; // non-grouped, handles progressive std::set items; // all other items std::set rooms; // maybe bool mastery = false; // maybe bool panel_hunt = false; // maybe void Merge(const Requirements& rhs) { if (rhs.disabled) { return; } for (int id : rhs.doors) { doors.insert(id); } for (int id : rhs.items) { items.insert(id); } for (int id : rhs.rooms) { rooms.insert(id); } mastery = mastery || rhs.mastery; panel_hunt = panel_hunt || rhs.panel_hunt; } }; class RequirementCalculator { public: void Reset() { doors_.clear(); panels_.clear(); } const Requirements& GetDoor(int door_id) { if (!doors_.count(door_id)) { Requirements requirements; const Door& door_obj = GD_GetDoor(door_id); if (door_obj.type == DoorType::kSunPainting) { if (!AP_IsPilgrimageEnabled()) { requirements.items.insert(door_obj.ap_item_id); } else { requirements.disabled = true; } } else if (door_obj.type == DoorType::kSunwarp) { switch (AP_GetSunwarpAccess()) { case kSUNWARP_ACCESS_NORMAL: // Do nothing. break; case kSUNWARP_ACCESS_DISABLED: requirements.disabled = true; break; case kSUNWARP_ACCESS_UNLOCK: requirements.items.insert(door_obj.group_ap_item_id); break; case kSUNWARP_ACCESS_INDIVIDUAL: case kSUNWARP_ACCESS_PROGRESSIVE: requirements.doors.insert(door_obj.id); break; } } else if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { requirements.rooms.insert(door_obj.room); for (int panel_id : door_obj.panels) { const Requirements& panel_reqs = GetPanel(panel_id); requirements.Merge(panel_reqs); } } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && !door_obj.group_name.empty()) { requirements.items.insert(door_obj.group_ap_item_id); } else { requirements.doors.insert(door_obj.id); } doors_[door_id] = requirements; } return doors_[door_id]; } const Requirements& GetPanel(int panel_id) { if (!panels_.count(panel_id)) { Requirements requirements; const Panel& panel_obj = GD_GetPanel(panel_id); requirements.rooms.insert(panel_obj.room); if (panel_obj.name == "THE MASTER") { requirements.mastery = true; } if ((panel_obj.name == "ANOTHER TRY" || panel_obj.name == "LEVEL 2") && AP_GetLevel2Requirement() > 1) { requirements.panel_hunt = true; } for (int room_id : panel_obj.required_rooms) { requirements.rooms.insert(room_id); } for (int door_id : panel_obj.required_doors) { const Requirements& door_reqs = GetDoor(door_id); requirements.Merge(door_reqs); } for (int panel_id : panel_obj.required_panels) { const Requirements& panel_reqs = GetPanel(panel_id); requirements.Merge(panel_reqs); } if (AP_IsColorShuffle()) { for (LingoColor color : panel_obj.colors) { requirements.items.insert(GD_GetItemIdForColor(color)); } } panels_[panel_id] = requirements; } return panels_[panel_id]; } private: std::map doors_; std::map panels_; }; struct TrackerState { std::map reachability; std::set reachable_doors; std::set reachable_paintings; std::mutex reachability_mutex; RequirementCalculator requirements; std::map> door_reports; }; enum Decision { kYes, kNo, kMaybe }; TrackerState& GetState() { static TrackerState* instance = new TrackerState(); return *instance; } class StateCalculator; struct StateCalculatorOptions { int start; bool pilgrimage = false; StateCalculator* parent = nullptr; }; class StateCalculator { public: StateCalculator() = default; explicit StateCalculator(StateCalculatorOptions options) : options_(options) {} void Calculate() { std::list panel_boundary; std::list painting_boundary; std::list flood_boundary; flood_boundary.push_back({.destination_room = options_.start}); bool reachable_changed = true; while (reachable_changed) { reachable_changed = false; std::list new_boundary; std::list new_panel_boundary; for (int panel_id : panel_boundary) { if (solveable_panels_.count(panel_id)) { continue; } Decision panel_reachable = IsPanelReachable(panel_id); if (panel_reachable == kYes) { solveable_panels_.insert(panel_id); reachable_changed = true; } else if (panel_reachable == kMaybe) { new_panel_boundary.push_back(panel_id); } }
#include "tracker_config.h"

#include <yaml-cpp/yaml.h>

#include <fstream>

#include "global.h"

void TrackerConfig::Load() {
  try {
    YAML::Node file = YAML::LoadFile(filename_);

    connection_details.ap_server = file["ap_server"].as<std::string>();
    connection_details.ap_player = file["ap_player"].as<std::string>();
    connection_details.ap_password = file["ap_password"].as<std::string>();
    asked_to_check_for_updates = file["asked_to_check_for_updates"].as<bool>();
    should_check_for_updates = file["should_check_for_updates"].as<bool>();
    hybrid_areas = file["hybrid_areas"].as<bool>();
    show_hunt_panels = file["show_hunt_panels"].as<bool>();

    if (file["connection_history"]) {
      for (const auto& connection : file["connection_history"]) {
        connection_history.push_back(ConnectionDetails{
          .ap_server = connection["ap_server"].as<std::string>(),
          .ap_player = connection["ap_player"].as<std::string>(),
          .ap_password = connection["ap_password"].as<std::string>()
        });
      }
    }
  } catch (const std::exception&) {
    // It's fine if the file can't be loaded.
  }
}

void TrackerConfig::Save() {
  YAML::Node output;
  output["ap_server"] = connection_details.ap_server;
  output["ap_player"] = connection_details.ap_player;
  output["ap_password"] = connection_details.ap_password;
  output["asked_to_check_for_updates"] = asked_to_check_for_updates;
  output["should_check_for_updates"] = should_check_for_updates;
  output["hybrid_areas"] = hybrid_areas;
  output["show_hunt_panels"] = show_hunt_panels;
  
  output.remove("connection_history");
  for (const ConnectionDetails& details : connection_history) {
    YAML::Node connection;
    connection["ap_server"] = details.ap_server;
    connection["ap_player"] = details.ap_player;
    connection["ap_password"] = details.ap_password;

    output["connection_history"].push_back(connection);
  }

  std::ofstream filewriter(filename_);
  filewriter << output;
}

TrackerConfig& GetTrackerConfig() {
  static TrackerConfig* instance =
      new TrackerConfig(GetAbsolutePath("config.yaml"));
  return *instance;
}