diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2024-06-06 15:54:41 -0400 | 
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2024-06-06 15:54:41 -0400 | 
| commit | 8ddab49cc13d809ca75dcd7f645661a3d3cb05c4 (patch) | |
| tree | ba1e5f3237dbb7cdc939c35e193f5e6e46845a77 /src/tracker_state.cpp | |
| parent | ac38dd0a5c394eefc39b7a8cf7b96762f18c8b31 (diff) | |
| parent | 6f5287b3921c843a6b322ccbdfcbef00a8f16980 (diff) | |
| download | lingo-ap-tracker-8ddab49cc13d809ca75dcd7f645661a3d3cb05c4.tar.gz lingo-ap-tracker-8ddab49cc13d809ca75dcd7f645661a3d3cb05c4.tar.bz2 lingo-ap-tracker-8ddab49cc13d809ca75dcd7f645661a3d3cb05c4.zip | |
Merge branch 'subway'
Diffstat (limited to 'src/tracker_state.cpp')
| -rw-r--r-- | src/tracker_state.cpp | 400 | 
1 files changed, 308 insertions, 92 deletions
| diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 640a159..66e7751 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp | |||
| @@ -12,9 +12,142 @@ | |||
| 12 | 12 | ||
| 13 | namespace { | 13 | namespace { | 
| 14 | 14 | ||
| 15 | struct Requirements { | ||
| 16 | bool disabled = false; | ||
| 17 | |||
| 18 | std::set<int> doors; // non-grouped, handles progressive | ||
| 19 | std::set<int> items; // all other items | ||
| 20 | std::set<int> rooms; // maybe | ||
| 21 | bool mastery = false; // maybe | ||
| 22 | bool panel_hunt = false; // maybe | ||
| 23 | |||
| 24 | void Merge(const Requirements& rhs) { | ||
| 25 | if (rhs.disabled) { | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | |||
| 29 | for (int id : rhs.doors) { | ||
| 30 | doors.insert(id); | ||
| 31 | } | ||
| 32 | for (int id : rhs.items) { | ||
| 33 | items.insert(id); | ||
| 34 | } | ||
| 35 | for (int id : rhs.rooms) { | ||
| 36 | rooms.insert(id); | ||
| 37 | } | ||
| 38 | mastery = mastery || rhs.mastery; | ||
| 39 | panel_hunt = panel_hunt || rhs.panel_hunt; | ||
| 40 | } | ||
| 41 | }; | ||
| 42 | |||
| 43 | class RequirementCalculator { | ||
| 44 | public: | ||
| 45 | void Reset() { | ||
| 46 | doors_.clear(); | ||
| 47 | panels_.clear(); | ||
| 48 | } | ||
| 49 | |||
| 50 | const Requirements& GetDoor(int door_id) { | ||
| 51 | if (!doors_.count(door_id)) { | ||
| 52 | Requirements requirements; | ||
| 53 | const Door& door_obj = GD_GetDoor(door_id); | ||
| 54 | |||
| 55 | if (door_obj.type == DoorType::kSunPainting) { | ||
| 56 | if (!AP_IsPilgrimageEnabled()) { | ||
| 57 | requirements.items.insert(door_obj.ap_item_id); | ||
| 58 | } else { | ||
| 59 | requirements.disabled = true; | ||
| 60 | } | ||
| 61 | } else if (door_obj.type == DoorType::kSunwarp) { | ||
| 62 | switch (AP_GetSunwarpAccess()) { | ||
| 63 | case kSUNWARP_ACCESS_NORMAL: | ||
| 64 | // Do nothing. | ||
| 65 | break; | ||
| 66 | case kSUNWARP_ACCESS_DISABLED: | ||
| 67 | requirements.disabled = true; | ||
| 68 | break; | ||
| 69 | case kSUNWARP_ACCESS_UNLOCK: | ||
| 70 | requirements.items.insert(door_obj.group_ap_item_id); | ||
| 71 | break; | ||
| 72 | case kSUNWARP_ACCESS_INDIVIDUAL: | ||
| 73 | case kSUNWARP_ACCESS_PROGRESSIVE: | ||
| 74 | requirements.doors.insert(door_obj.id); | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | } else if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { | ||
| 78 | requirements.rooms.insert(door_obj.room); | ||
| 79 | |||
| 80 | for (int panel_id : door_obj.panels) { | ||
| 81 | const Requirements& panel_reqs = GetPanel(panel_id); | ||
| 82 | requirements.Merge(panel_reqs); | ||
| 83 | } | ||
| 84 | } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && | ||
| 85 | !door_obj.group_name.empty()) { | ||
| 86 | requirements.items.insert(door_obj.group_ap_item_id); | ||
| 87 | } else { | ||
| 88 | requirements.doors.insert(door_obj.id); | ||
| 89 | } | ||
| 90 | |||
| 91 | doors_[door_id] = requirements; | ||
| 92 | } | ||
| 93 | |||
| 94 | return doors_[door_id]; | ||
| 95 | } | ||
| 96 | |||
| 97 | const Requirements& GetPanel(int panel_id) { | ||
| 98 | if (!panels_.count(panel_id)) { | ||
| 99 | Requirements requirements; | ||
| 100 | const Panel& panel_obj = GD_GetPanel(panel_id); | ||
| 101 | |||
| 102 | requirements.rooms.insert(panel_obj.room); | ||
| 103 | |||
| 104 | if (panel_obj.name == "THE MASTER") { | ||
| 105 | requirements.mastery = true; | ||
| 106 | } | ||
| 107 | |||
| 108 | if ((panel_obj.name == "ANOTHER TRY" || panel_obj.name == "LEVEL 2") && | ||
| 109 | AP_GetLevel2Requirement() > 1) { | ||
| 110 | requirements.panel_hunt = true; | ||
| 111 | } | ||
| 112 | |||
| 113 | for (int room_id : panel_obj.required_rooms) { | ||
| 114 | requirements.rooms.insert(room_id); | ||
| 115 | } | ||
| 116 | |||
| 117 | for (int door_id : panel_obj.required_doors) { | ||
| 118 | const Requirements& door_reqs = GetDoor(door_id); | ||
| 119 | requirements.Merge(door_reqs); | ||
| 120 | } | ||
| 121 | |||
| 122 | for (int panel_id : panel_obj.required_panels) { | ||
| 123 | const Requirements& panel_reqs = GetPanel(panel_id); | ||
| 124 | requirements.Merge(panel_reqs); | ||
| 125 | } | ||
| 126 | |||
| 127 | if (AP_IsColorShuffle()) { | ||
| 128 | for (LingoColor color : panel_obj.colors) { | ||
| 129 | requirements.items.insert(GD_GetItemIdForColor(color)); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | panels_[panel_id] = requirements; | ||
| 134 | } | ||
| 135 | |||
| 136 | return panels_[panel_id]; | ||
| 137 | } | ||
| 138 | |||
| 139 | private: | ||
| 140 | std::map<int, Requirements> doors_; | ||
| 141 | std::map<int, Requirements> panels_; | ||
| 142 | }; | ||
| 143 | |||
| 15 | struct TrackerState { | 144 | struct TrackerState { | 
| 16 | std::map<int, bool> reachability; | 145 | std::map<int, bool> reachability; | 
| 146 | std::set<int> reachable_doors; | ||
| 147 | std::set<int> reachable_paintings; | ||
| 17 | std::mutex reachability_mutex; | 148 | std::mutex reachability_mutex; | 
| 149 | RequirementCalculator requirements; | ||
| 150 | std::map<int, std::map<std::string, bool>> door_reports; | ||
| 18 | }; | 151 | }; | 
| 19 | 152 | ||
| 20 | enum Decision { kYes, kNo, kMaybe }; | 153 | enum Decision { kYes, kNo, kMaybe }; | 
| @@ -41,6 +174,7 @@ class StateCalculator { | |||
| 41 | 174 | ||
| 42 | void Calculate() { | 175 | void Calculate() { | 
| 43 | std::list<int> panel_boundary; | 176 | std::list<int> panel_boundary; | 
| 177 | std::list<int> painting_boundary; | ||
| 44 | std::list<Exit> flood_boundary; | 178 | std::list<Exit> flood_boundary; | 
| 45 | flood_boundary.push_back({.destination_room = options_.start}); | 179 | flood_boundary.push_back({.destination_room = options_.start}); | 
| 46 | 180 | ||
| @@ -48,6 +182,8 @@ class StateCalculator { | |||
| 48 | while (reachable_changed) { | 182 | while (reachable_changed) { | 
| 49 | reachable_changed = false; | 183 | reachable_changed = false; | 
| 50 | 184 | ||
| 185 | std::list<Exit> new_boundary; | ||
| 186 | |||
| 51 | std::list<int> new_panel_boundary; | 187 | std::list<int> new_panel_boundary; | 
| 52 | for (int panel_id : panel_boundary) { | 188 | for (int panel_id : panel_boundary) { | 
| 53 | if (solveable_panels_.count(panel_id)) { | 189 | if (solveable_panels_.count(panel_id)) { | 
| @@ -63,7 +199,33 @@ class StateCalculator { | |||
| 63 | } | 199 | } | 
| 64 | } | 200 | } | 
| 65 | 201 | ||
| 66 | std::list<Exit> new_boundary; | 202 | std::list<int> new_painting_boundary; | 
| 203 | for (int painting_id : painting_boundary) { | ||
| 204 | if (reachable_paintings_.count(painting_id)) { | ||
| 205 | continue; | ||
| 206 | } | ||
| 207 | |||
| 208 | Decision painting_reachable = IsPaintingReachable(painting_id); | ||
| 209 | if (painting_reachable == kYes) { | ||
| 210 | reachable_paintings_.insert(painting_id); | ||
| 211 | reachable_changed = true; | ||
| 212 | |||
| 213 | PaintingExit cur_painting = GD_GetPaintingExit(painting_id); | ||
| 214 | if (AP_GetPaintingMapping().count(cur_painting.internal_id) && | ||
| 215 | AP_GetCheckedPaintings().count(cur_painting.internal_id)) { | ||
| 216 | Exit painting_exit; | ||
| 217 | PaintingExit target_painting = | ||
| 218 | GD_GetPaintingExit(GD_GetPaintingByName( | ||
| 219 | AP_GetPaintingMapping().at(cur_painting.internal_id))); | ||
| 220 | painting_exit.destination_room = target_painting.room; | ||
| 221 | |||
| 222 | new_boundary.push_back(painting_exit); | ||
| 223 | } | ||
| 224 | } else if (painting_reachable == kMaybe) { | ||
| 225 | new_painting_boundary.push_back(painting_id); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 67 | for (const Exit& room_exit : flood_boundary) { | 229 | for (const Exit& room_exit : flood_boundary) { | 
| 68 | if (reachable_rooms_.count(room_exit.destination_room)) { | 230 | if (reachable_rooms_.count(room_exit.destination_room)) { | 
| 69 | continue; | 231 | continue; | 
| @@ -98,15 +260,8 @@ class StateCalculator { | |||
| 98 | } | 260 | } | 
| 99 | 261 | ||
| 100 | if (AP_IsPaintingShuffle()) { | 262 | if (AP_IsPaintingShuffle()) { | 
| 101 | for (const PaintingExit& out_edge : room_obj.paintings) { | 263 | for (int out_edge : room_obj.paintings) { | 
| 102 | if (AP_GetPaintingMapping().count(out_edge.id)) { | 264 | new_painting_boundary.push_back(out_edge); | 
| 103 | Exit painting_exit; | ||
| 104 | painting_exit.destination_room = GD_GetRoomForPainting( | ||
| 105 | AP_GetPaintingMapping().at(out_edge.id)); | ||
| 106 | painting_exit.door = out_edge.door; | ||
| 107 | |||
| 108 | new_boundary.push_back(painting_exit); | ||
| 109 | } | ||
| 110 | } | 265 | } | 
| 111 | } | 266 | } | 
| 112 | 267 | ||
| @@ -155,6 +310,13 @@ class StateCalculator { | |||
| 155 | 310 | ||
| 156 | flood_boundary = new_boundary; | 311 | flood_boundary = new_boundary; | 
| 157 | panel_boundary = new_panel_boundary; | 312 | panel_boundary = new_panel_boundary; | 
| 313 | painting_boundary = new_painting_boundary; | ||
| 314 | } | ||
| 315 | |||
| 316 | // Now that we know the full reachable area, let's make sure all doors are | ||
| 317 | // evaluated. | ||
| 318 | for (const Door& door : GD_GetDoors()) { | ||
| 319 | int discard = IsDoorReachable(door.id); | ||
| 158 | } | 320 | } | 
| 159 | } | 321 | } | 
| 160 | 322 | ||
| @@ -166,6 +328,14 @@ class StateCalculator { | |||
| 166 | 328 | ||
| 167 | const std::set<int>& GetSolveablePanels() const { return solveable_panels_; } | 329 | const std::set<int>& GetSolveablePanels() const { return solveable_panels_; } | 
| 168 | 330 | ||
| 331 | const std::set<int>& GetReachablePaintings() const { | ||
| 332 | return reachable_paintings_; | ||
| 333 | } | ||
| 334 | |||
| 335 | const std::map<int, std::map<std::string, bool>>& GetDoorReports() const { | ||
| 336 | return door_report_; | ||
| 337 | } | ||
| 338 | |||
| 169 | private: | 339 | private: | 
| 170 | Decision IsNonGroupedDoorReachable(const Door& door_obj) { | 340 | Decision IsNonGroupedDoorReachable(const Door& door_obj) { | 
| 171 | bool has_item = AP_HasItem(door_obj.ap_item_id); | 341 | bool has_item = AP_HasItem(door_obj.ap_item_id); | 
| @@ -182,68 +352,52 @@ class StateCalculator { | |||
| 182 | return has_item ? kYes : kNo; | 352 | return has_item ? kYes : kNo; | 
| 183 | } | 353 | } | 
| 184 | 354 | ||
| 185 | Decision IsDoorReachable_Helper(int door_id) { | 355 | Decision AreRequirementsSatisfied( | 
| 186 | const Door& door_obj = GD_GetDoor(door_id); | 356 | const Requirements& reqs, std::map<std::string, bool>* report = nullptr) { | 
| 187 | 357 | if (reqs.disabled) { | |
| 188 | if (!AP_IsPilgrimageEnabled() && door_obj.type == DoorType::kSunPainting) { | 358 | return kNo; | 
| 189 | return AP_HasItem(door_obj.ap_item_id) ? kYes : kNo; | ||
| 190 | } else if (door_obj.type == DoorType::kSunwarp) { | ||
| 191 | switch (AP_GetSunwarpAccess()) { | ||
| 192 | case kSUNWARP_ACCESS_NORMAL: | ||
| 193 | return kYes; | ||
| 194 | case kSUNWARP_ACCESS_DISABLED: | ||
| 195 | return kNo; | ||
| 196 | case kSUNWARP_ACCESS_UNLOCK: | ||
| 197 | return AP_HasItem(door_obj.group_ap_item_id) ? kYes : kNo; | ||
| 198 | case kSUNWARP_ACCESS_INDIVIDUAL: | ||
| 199 | case kSUNWARP_ACCESS_PROGRESSIVE: | ||
| 200 | return IsNonGroupedDoorReachable(door_obj); | ||
| 201 | } | ||
| 202 | } else if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { | ||
| 203 | if (!reachable_rooms_.count(door_obj.room)) { | ||
| 204 | return kMaybe; | ||
| 205 | } | ||
| 206 | |||
| 207 | for (int panel_id : door_obj.panels) { | ||
| 208 | if (!solveable_panels_.count(panel_id)) { | ||
| 209 | return kMaybe; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | return kYes; | ||
| 214 | } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && | ||
| 215 | !door_obj.group_name.empty()) { | ||
| 216 | return AP_HasItem(door_obj.group_ap_item_id) ? kYes : kNo; | ||
| 217 | } else { | ||
| 218 | return IsNonGroupedDoorReachable(door_obj); | ||
| 219 | } | 359 | } | 
| 220 | } | ||
| 221 | 360 | ||
| 222 | Decision IsDoorReachable(int door_id) { | 361 | Decision final_decision = kYes; | 
| 223 | if (options_.parent) { | ||
| 224 | return options_.parent->IsDoorReachable(door_id); | ||
| 225 | } | ||
| 226 | 362 | ||
| 227 | if (door_decisions_.count(door_id)) { | 363 | for (int door_id : reqs.doors) { | 
| 228 | return door_decisions_.at(door_id); | 364 | const Door& door_obj = GD_GetDoor(door_id); | 
| 365 | Decision decision = IsNonGroupedDoorReachable(door_obj); | ||
| 366 | |||
| 367 | if (report) { | ||
| 368 | (*report)[door_obj.item_name] = (decision == kYes); | ||
| 369 | } | ||
| 370 | |||
| 371 | if (decision != kYes) { | ||
| 372 | final_decision = decision; | ||
| 373 | } | ||
| 229 | } | 374 | } | 
| 230 | 375 | ||
| 231 | Decision result = IsDoorReachable_Helper(door_id); | 376 | for (int item_id : reqs.items) { | 
| 232 | if (result != kMaybe) { | 377 | bool has_item = AP_HasItem(item_id); | 
| 233 | door_decisions_[door_id] = result; | 378 | if (report) { | 
| 379 | (*report)[AP_GetItemName(item_id)] = has_item; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (!has_item) { | ||
| 383 | final_decision = kNo; | ||
| 384 | } | ||
| 234 | } | 385 | } | 
| 235 | 386 | ||
| 236 | return result; | 387 | for (int room_id : reqs.rooms) { | 
| 237 | } | 388 | bool reachable = reachable_rooms_.count(room_id); | 
| 238 | 389 | ||
| 239 | Decision IsPanelReachable(int panel_id) { | 390 | if (report) { | 
| 240 | const Panel& panel_obj = GD_GetPanel(panel_id); | 391 | std::string report_name = "Reach \"" + GD_GetRoom(room_id).name + "\""; | 
| 392 | (*report)[report_name] = reachable; | ||
| 393 | } | ||
| 241 | 394 | ||
| 242 | if (!reachable_rooms_.count(panel_obj.room)) { | 395 | if (!reachable && final_decision != kNo) { | 
| 243 | return kMaybe; | 396 | final_decision = kMaybe; | 
| 397 | } | ||
| 244 | } | 398 | } | 
| 245 | 399 | ||
| 246 | if (panel_obj.name == "THE MASTER") { | 400 | if (reqs.mastery) { | 
| 247 | int achievements_accessible = 0; | 401 | int achievements_accessible = 0; | 
| 248 | 402 | ||
| 249 | for (int achieve_id : GD_GetAchievementPanels()) { | 403 | for (int achieve_id : GD_GetAchievementPanels()) { | 
| @@ -256,12 +410,18 @@ class StateCalculator { | |||
| 256 | } | 410 | } | 
| 257 | } | 411 | } | 
| 258 | 412 | ||
| 259 | return (achievements_accessible >= AP_GetMasteryRequirement()) ? kYes | 413 | bool can_mastery = | 
| 260 | : kMaybe; | 414 | (achievements_accessible >= AP_GetMasteryRequirement()); | 
| 415 | if (report) { | ||
| 416 | (*report)["Mastery"] = can_mastery; | ||
| 417 | } | ||
| 418 | |||
| 419 | if (!can_mastery && final_decision != kNo) { | ||
| 420 | final_decision = kMaybe; | ||
| 421 | } | ||
| 261 | } | 422 | } | 
| 262 | 423 | ||
| 263 | if ((panel_obj.name == "ANOTHER TRY" || panel_obj.name == "LEVEL 2") && | 424 | if (reqs.panel_hunt) { | 
| 264 | AP_GetLevel2Requirement() > 1) { | ||
| 265 | int counting_panels_accessible = 0; | 425 | int counting_panels_accessible = 0; | 
| 266 | 426 | ||
| 267 | for (int solved_panel_id : solveable_panels_) { | 427 | for (int solved_panel_id : solveable_panels_) { | 
| @@ -272,41 +432,58 @@ class StateCalculator { | |||
| 272 | } | 432 | } | 
| 273 | } | 433 | } | 
| 274 | 434 | ||
| 275 | return (counting_panels_accessible >= AP_GetLevel2Requirement() - 1) | 435 | bool can_level2 = | 
| 276 | ? kYes | 436 | (counting_panels_accessible >= AP_GetLevel2Requirement() - 1); | 
| 277 | : kMaybe; | 437 | if (report) { | 
| 278 | } | 438 | std::string report_name = | 
| 439 | std::to_string(AP_GetLevel2Requirement()) + " Panels"; | ||
| 440 | (*report)[report_name] = can_level2; | ||
| 441 | } | ||
| 279 | 442 | ||
| 280 | for (int room_id : panel_obj.required_rooms) { | 443 | if (!can_level2 && final_decision != kNo) { | 
| 281 | if (!reachable_rooms_.count(room_id)) { | 444 | final_decision = kMaybe; | 
| 282 | return kMaybe; | ||
| 283 | } | 445 | } | 
| 284 | } | 446 | } | 
| 285 | 447 | ||
| 286 | for (int door_id : panel_obj.required_doors) { | 448 | return final_decision; | 
| 287 | Decision door_reachable = IsDoorReachable(door_id); | 449 | } | 
| 288 | if (door_reachable == kNo) { | 450 | |
| 289 | const Door& door_obj = GD_GetDoor(door_id); | 451 | Decision IsDoorReachable_Helper(int door_id) { | 
| 290 | return (door_obj.is_event || AP_GetDoorShuffleMode() == kNO_DOORS) | 452 | if (door_report_.count(door_id)) { | 
| 291 | ? kMaybe | 453 | door_report_[door_id].clear(); | 
| 292 | : kNo; | 454 | } else { | 
| 293 | } else if (door_reachable == kMaybe) { | 455 | door_report_[door_id] = {}; | 
| 294 | return kMaybe; | ||
| 295 | } | ||
| 296 | } | 456 | } | 
| 297 | 457 | ||
| 298 | for (int panel_id : panel_obj.required_panels) { | 458 | return AreRequirementsSatisfied(GetState().requirements.GetDoor(door_id), | 
| 299 | if (!solveable_panels_.count(panel_id)) { | 459 | &door_report_[door_id]); | 
| 300 | return kMaybe; | 460 | } | 
| 301 | } | 461 | |
| 462 | Decision IsDoorReachable(int door_id) { | ||
| 463 | if (options_.parent) { | ||
| 464 | return options_.parent->IsDoorReachable(door_id); | ||
| 302 | } | 465 | } | 
| 303 | 466 | ||
| 304 | if (AP_IsColorShuffle()) { | 467 | if (door_decisions_.count(door_id)) { | 
| 305 | for (LingoColor color : panel_obj.colors) { | 468 | return door_decisions_.at(door_id); | 
| 306 | if (!AP_HasItem(GD_GetItemIdForColor(color))) { | 469 | } | 
| 307 | return kNo; | 470 | |
| 308 | } | 471 | Decision result = IsDoorReachable_Helper(door_id); | 
| 309 | } | 472 | if (result != kMaybe) { | 
| 473 | door_decisions_[door_id] = result; | ||
| 474 | } | ||
| 475 | |||
| 476 | return result; | ||
| 477 | } | ||
| 478 | |||
| 479 | Decision IsPanelReachable(int panel_id) { | ||
| 480 | return AreRequirementsSatisfied(GetState().requirements.GetPanel(panel_id)); | ||
| 481 | } | ||
| 482 | |||
| 483 | Decision IsPaintingReachable(int painting_id) { | ||
| 484 | const PaintingExit& painting = GD_GetPaintingExit(painting_id); | ||
| 485 | if (painting.door) { | ||
| 486 | return IsDoorReachable(*painting.door); | ||
| 310 | } | 487 | } | 
| 311 | 488 | ||
| 312 | return kYes; | 489 | return kYes; | 
| @@ -395,10 +572,17 @@ class StateCalculator { | |||
| 395 | std::set<int> reachable_rooms_; | 572 | std::set<int> reachable_rooms_; | 
| 396 | std::map<int, Decision> door_decisions_; | 573 | std::map<int, Decision> door_decisions_; | 
| 397 | std::set<int> solveable_panels_; | 574 | std::set<int> solveable_panels_; | 
| 575 | std::set<int> reachable_paintings_; | ||
| 576 | std::map<int, std::map<std::string, bool>> door_report_; | ||
| 398 | }; | 577 | }; | 
| 399 | 578 | ||
| 400 | } // namespace | 579 | } // namespace | 
| 401 | 580 | ||
| 581 | void ResetReachabilityRequirements() { | ||
| 582 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
| 583 | GetState().requirements.Reset(); | ||
| 584 | } | ||
| 585 | |||
| 402 | void RecalculateReachability() { | 586 | void RecalculateReachability() { | 
| 403 | StateCalculator state_calculator({.start = GD_GetRoomByName("Menu")}); | 587 | StateCalculator state_calculator({.start = GD_GetRoomByName("Menu")}); | 
| 404 | state_calculator.Calculate(); | 588 | state_calculator.Calculate(); | 
| @@ -422,9 +606,23 @@ void RecalculateReachability() { | |||
| 422 | } | 606 | } | 
| 423 | } | 607 | } | 
| 424 | 608 | ||
| 609 | std::set<int> new_reachable_doors; | ||
| 610 | for (const auto& [door_id, decision] : state_calculator.GetDoorDecisions()) { | ||
| 611 | if (decision == kYes) { | ||
| 612 | new_reachable_doors.insert(door_id); | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | std::set<int> reachable_paintings = state_calculator.GetReachablePaintings(); | ||
| 617 | std::map<int, std::map<std::string, bool>> door_reports = | ||
| 618 | state_calculator.GetDoorReports(); | ||
| 619 | |||
| 425 | { | 620 | { | 
| 426 | std::lock_guard reachability_guard(GetState().reachability_mutex); | 621 | std::lock_guard reachability_guard(GetState().reachability_mutex); | 
| 427 | std::swap(GetState().reachability, new_reachability); | 622 | std::swap(GetState().reachability, new_reachability); | 
| 623 | std::swap(GetState().reachable_doors, new_reachable_doors); | ||
| 624 | std::swap(GetState().reachable_paintings, reachable_paintings); | ||
| 625 | std::swap(GetState().door_reports, door_reports); | ||
| 428 | } | 626 | } | 
| 429 | } | 627 | } | 
| 430 | 628 | ||
| @@ -437,3 +635,21 @@ bool IsLocationReachable(int location_id) { | |||
| 437 | return false; | 635 | return false; | 
| 438 | } | 636 | } | 
| 439 | } | 637 | } | 
| 638 | |||
| 639 | bool IsDoorOpen(int door_id) { | ||
| 640 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
| 641 | |||
| 642 | return GetState().reachable_doors.count(door_id); | ||
| 643 | } | ||
| 644 | |||
| 645 | bool IsPaintingReachable(int painting_id) { | ||
| 646 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
| 647 | |||
| 648 | return GetState().reachable_paintings.count(painting_id); | ||
| 649 | } | ||
| 650 | |||
| 651 | const std::map<std::string, bool>& GetDoorRequirements(int door_id) { | ||
| 652 | std::lock_guard reachability_guard(GetState().reachability_mutex); | ||
| 653 | |||
| 654 | return GetState().door_reports[door_id]; | ||
| 655 | } | ||
