about summary refs log tree commit diff stats
path: root/data/maps/the_tree/rooms
Commit message (Expand)AuthorAgeFilesLines
* Added display names to portsStar Rauchenberger2025-09-282-0/+5
* [Data] Fixed some stuff for worldport shuffleStar Rauchenberger2025-09-221-1/+1
* [Data] Annotate shuffleable portsStar Rauchenberger2025-09-212-0/+12
* Added the_treeStar Rauchenberger2025-08-262-0/+244
id='n75' href='#n75'>75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
#include "tracker_state.h"

#include <list>
#include <set>

#include "ap_state.h"
#include "game_data.h"

bool IsDoorReachable_Helper(int door_id, const std::set<int>& reachable_rooms);

bool IsPanelReachable_Helper(int panel_id,
                             const std::set<int>& reachable_rooms) {
  const Panel& panel_obj = GetGameData().GetPanel(panel_id);

  if (!reachable_rooms.count(panel_obj.room)) {
    return false;
  }

  for (int room_id : panel_obj.required_rooms) {
    if (!reachable_rooms.count(room_id)) {
      return false;
    }
  }

  for (int door_id : panel_obj.required_doors) {
    if (!IsDoorReachable_Helper(door_id, reachable_rooms)) {
      return false;
    }
  }

  if (GetAPState().IsColorShuffle()) {
    for (LingoColor color : panel_obj.colors) {
      if (!GetAPState().HasColorItem(color)) {
        return false;
      }
    }
  }

  return true;
}

bool IsDoorReachable_Helper(int door_id, const std::set<int>& reachable_rooms) {
  const Door& door_obj = GetGameData().GetDoor(door_id);

  if (GetAPState().GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) {
    if (!reachable_rooms.count(door_obj.room)) {
      return false;
    }

    for (int panel_id : door_obj.panels) {
      if (!IsPanelReachable_Helper(panel_id, reachable_rooms)) {
        return false;
      }
    }

    return true;
  } else if (GetAPState().GetDoorShuffleMode() == kSIMPLE_DOORS &&
             !door_obj.group_name.empty()) {
    return GetAPState().HasItem(door_obj.group_name);
  } else {
    return GetAPState().HasItem(door_obj.item_name);
  }
}

void TrackerState::CalculateState() {
  reachability_.clear();

  std::set<int> reachable_rooms;

  std::list<Exit> flood_boundary;
  flood_boundary.push_back(
      {.destination_room = GetGameData().GetRoomByName("Menu")});

  bool reachable_changed = true;
  while (reachable_changed) {
    reachable_changed = false;

    std::list<Exit> new_boundary;
    for (const Exit& room_exit : flood_boundary) {
      if (reachable_rooms.count(room_exit.destination_room)) {
        continue;
      }

      bool valid_transition = false;
      if (room_exit.door.has_value()) {
        if (IsDoorReachable_Helper(*room_exit.door, reachable_rooms)) {
          valid_transition = true;
        } else if (GetAPState().GetDoorShuffleMode() == kNO_DOORS) {
          new_boundary.push_back(room_exit);
        }
      } else {
        valid_transition = true;
      }

      if (valid_transition) {
        reachable_rooms.insert(room_exit.destination_room);
        reachable_changed = true;

        const Room& room_obj =
            GetGameData().GetRoom(room_exit.destination_room);
        for (const Exit& out_edge : room_obj.exits) {
          new_boundary.push_back(out_edge);
        }
      }
    }

    flood_boundary = new_boundary;
  }

  for (const MapArea& map_area : GetGameData().GetMapAreas()) {
    for (int section_id = 0; section_id < map_area.locations.size();
         section_id++) {
      const Location& location_section = map_area.locations.at(section_id);
      bool reachable = reachable_rooms.count(location_section.room);
      if (reachable) {
        for (int panel_id : location_section.panels) {
          reachable &= IsPanelReachable_Helper(panel_id, reachable_rooms);
        }
      }

      reachability_[{map_area.id, section_id}] = reachable;
    }
  }
}

bool TrackerState::IsLocationReachable(int area_id, int section_id) {
  std::tuple<int, int> key = {area_id, section_id};

  if (reachability_.count(key)) {
    return reachability_.at(key);
  } else {
    return false;
  }
}

TrackerState& GetTrackerState() {
  static TrackerState* instance = new TrackerState();
  return *instance;
}