#include "tracker_state.h" #include #include #include #include #include #include "ap_state.h" #include "game_data.h" #include "logger.h" namespace { struct TrackerState { std::map, bool> reachability; }; TrackerState& GetState() { static TrackerState* instance = new TrackerState(); return *instance; } bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms); bool IsPanelReachable_Helper(int panel_id, const std::set& reachable_rooms) { const Panel& panel_obj = GD_GetPanel(panel_id); if (!reachable_rooms.count(panel_obj.room)) { return false; } if (panel_obj.name == "THE MASTER") { int achievements_accessible = 0; for (int achieve_id : GD_GetAchievementPanels()) { if (IsPanelReachable_Helper(achieve_id, reachable_rooms)) { achievements_accessible++; if (achievements_accessible >= AP_GetMasteryRequirement()) { break; } } } return (achievements_accessible >= AP_GetMasteryRequirement()); } if (panel_obj.name == "LEVEL 2" && AP_GetVictoryCondition() == kLEVEL_2) { int counting_panels_accessible = 0; for (int reachable_room : reachable_rooms) { const Room& room = GD_GetRoom(reachable_room); for (int roomed_panel_id : room.panels) { const Panel& roomed_panel = GD_GetPanel(roomed_panel_id); if (!roomed_panel.non_counting && IsPanelReachable_Helper(roomed_panel_id, reachable_rooms)) { counting_panels_accessible++; } } } return (counting_panels_accessible >= AP_GetLevel2Requirement()); } 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; } } for (int panel_id : panel_obj.required_panels) { if (!IsPanelReachable_Helper(panel_id, reachable_rooms)) { return false; } } if (AP_IsColorShuffle()) { for (LingoColor color : panel_obj.colors) { if (!AP_HasColorItem(color)) { return false; } } } return true; } bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms) { const Door& door_obj = GD_GetDoor(door_id); if (AP_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 (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && !door_obj.group_name.empty()) { return AP_HasItem(door_obj.group_name); } else { bool has_item = AP_HasItem(door_obj.item_name); if (!has_item) { for (const ProgressiveRequirement& prog_req : door_obj.progressives) { if (AP_HasItem(prog_req.item_name, prog_req.quantity)) { has_item = true; break; } } } return has_item; } } } // namespace void RecalculateReachability() { GetState().reachability.clear(); std::set reachable_rooms; std::list flood_boundary; flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")}); bool reachable_changed = true; while (reachable_changed) { reachable_changed = false; std::list 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 { 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 = GD_GetRoom(room_exit.destination_room); for (const Exit& out_edge : room_obj.exits) { if (!out_edge.painting || !AP_IsPaintingShuffle()) { new_boundary.push_back(out_edge); } } if (AP_IsPaintingShuffle()) { for (const PaintingExit& out_edge : room_obj.paintings) { if (AP_GetP
name: "South Right Painting"
paintings {
  name: "BEE2"
  path: "Components/Paintings/bee2"
}