diff options
Diffstat (limited to 'src/ap_state.cpp')
-rw-r--r-- | src/ap_state.cpp | 211 |
1 files changed, 151 insertions, 60 deletions
diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 4ac0cce..8438649 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <any> | 10 | #include <any> |
11 | #include <apclient.hpp> | 11 | #include <apclient.hpp> |
12 | #include <apuuid.hpp> | 12 | #include <apuuid.hpp> |
13 | #include <bitset> | ||
13 | #include <chrono> | 14 | #include <chrono> |
14 | #include <exception> | 15 | #include <exception> |
15 | #include <filesystem> | 16 | #include <filesystem> |
@@ -28,8 +29,8 @@ | |||
28 | #include "tracker_state.h" | 29 | #include "tracker_state.h" |
29 | 30 | ||
30 | constexpr int AP_MAJOR = 0; | 31 | constexpr int AP_MAJOR = 0; |
31 | constexpr int AP_MINOR = 4; | 32 | constexpr int AP_MINOR = 6; |
32 | constexpr int AP_REVISION = 5; | 33 | constexpr int AP_REVISION = 1; |
33 | 34 | ||
34 | constexpr const char* CERT_STORE_PATH = "cacert.pem"; | 35 | constexpr const char* CERT_STORE_PATH = "cacert.pem"; |
35 | constexpr int ITEM_HANDLING = 7; // <- all | 36 | constexpr int ITEM_HANDLING = 7; // <- all |
@@ -37,8 +38,24 @@ constexpr int ITEM_HANDLING = 7; // <- all | |||
37 | constexpr int CONNECTION_TIMEOUT = 50000; // 50 seconds | 38 | constexpr int CONNECTION_TIMEOUT = 50000; // 50 seconds |
38 | constexpr int CONNECTION_BACKOFF_INTERVAL = 100; | 39 | constexpr int CONNECTION_BACKOFF_INTERVAL = 100; |
39 | 40 | ||
41 | constexpr int PANEL_COUNT = 803; | ||
42 | constexpr int PANEL_BITFIELD_LENGTH = 48; | ||
43 | constexpr int PANEL_BITFIELDS = 17; | ||
44 | |||
40 | namespace { | 45 | namespace { |
41 | 46 | ||
47 | const std::set<long> kNonProgressionItems = { | ||
48 | 444409, // :) | ||
49 | 444575, // The Feeling of Being Lost | ||
50 | 444576, // Wanderlust | ||
51 | 444577, // Empty White Hallways | ||
52 | 444410, // Slowness Trap | ||
53 | 444411, // Iceland Trap | ||
54 | 444412, // Atbash Trap | ||
55 | 444413, // Puzzle Skip | ||
56 | 444680, // Speed Boost | ||
57 | }; | ||
58 | |||
42 | struct APState { | 59 | struct APState { |
43 | // Initialized on main thread | 60 | // Initialized on main thread |
44 | bool initialized = false; | 61 | bool initialized = false; |
@@ -67,10 +84,12 @@ struct APState { | |||
67 | std::set<int64_t> checked_locations; | 84 | std::set<int64_t> checked_locations; |
68 | std::map<std::string, std::any> data_storage; | 85 | std::map<std::string, std::any> data_storage; |
69 | std::optional<std::tuple<int, int>> player_pos; | 86 | std::optional<std::tuple<int, int>> player_pos; |
87 | std::bitset<PANEL_COUNT> solved_panels; | ||
70 | 88 | ||
71 | DoorShuffleMode door_shuffle_mode = kNO_DOORS; | 89 | DoorShuffleMode door_shuffle_mode = kNO_DOORS; |
72 | bool group_doors = false; | 90 | bool group_doors = false; |
73 | bool color_shuffle = false; | 91 | bool color_shuffle = false; |
92 | PanelShuffleMode panel_shuffle_mode = kNO_PANELS; | ||
74 | bool painting_shuffle = false; | 93 | bool painting_shuffle = false; |
75 | int mastery_requirement = 21; | 94 | int mastery_requirement = 21; |
76 | int level_2_requirement = 223; | 95 | int level_2_requirement = 223; |
@@ -82,6 +101,7 @@ struct APState { | |||
82 | bool pilgrimage_allows_paintings = false; | 101 | bool pilgrimage_allows_paintings = false; |
83 | SunwarpAccess sunwarp_access = kSUNWARP_ACCESS_NORMAL; | 102 | SunwarpAccess sunwarp_access = kSUNWARP_ACCESS_NORMAL; |
84 | bool sunwarp_shuffle = false; | 103 | bool sunwarp_shuffle = false; |
104 | bool postgame_shuffle = true; | ||
85 | 105 | ||
86 | std::map<std::string, std::string> painting_mapping; | 106 | std::map<std::string, std::string> painting_mapping; |
87 | std::set<std::string> painting_codomain; | 107 | std::set<std::string> painting_codomain; |
@@ -128,10 +148,12 @@ struct APState { | |||
128 | checked_locations.clear(); | 148 | checked_locations.clear(); |
129 | data_storage.clear(); | 149 | data_storage.clear(); |
130 | player_pos = std::nullopt; | 150 | player_pos = std::nullopt; |
151 | solved_panels.reset(); | ||
131 | victory_data_storage_key.clear(); | 152 | victory_data_storage_key.clear(); |
132 | door_shuffle_mode = kNO_DOORS; | 153 | door_shuffle_mode = kNO_DOORS; |
133 | group_doors = false; | 154 | group_doors = false; |
134 | color_shuffle = false; | 155 | color_shuffle = false; |
156 | panel_shuffle_mode = kNO_PANELS; | ||
135 | painting_shuffle = false; | 157 | painting_shuffle = false; |
136 | painting_mapping.clear(); | 158 | painting_mapping.clear(); |
137 | painting_codomain.clear(); | 159 | painting_codomain.clear(); |
@@ -146,6 +168,7 @@ struct APState { | |||
146 | sunwarp_access = kSUNWARP_ACCESS_NORMAL; | 168 | sunwarp_access = kSUNWARP_ACCESS_NORMAL; |
147 | sunwarp_shuffle = false; | 169 | sunwarp_shuffle = false; |
148 | sunwarp_mapping.clear(); | 170 | sunwarp_mapping.clear(); |
171 | postgame_shuffle = true; | ||
149 | } | 172 | } |
150 | 173 | ||
151 | apclient->set_room_info_handler( | 174 | apclient->set_room_info_handler( |
@@ -200,24 +223,13 @@ struct APState { | |||
200 | return checked_locations.count(location_id); | 223 | return checked_locations.count(location_id); |
201 | } | 224 | } |
202 | 225 | ||
203 | bool HasCheckedHuntPanel(int location_id) { | ||
204 | std::lock_guard state_guard(state_mutex); | ||
205 | |||
206 | std::string key = | ||
207 | fmt::format("{}Hunt|{}", data_storage_prefix, location_id); | ||
208 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); | ||
209 | } | ||
210 | |||
211 | bool HasItem(int item_id, int quantity) { | 226 | bool HasItem(int item_id, int quantity) { |
212 | return inventory.count(item_id) && inventory.at(item_id) >= quantity; | 227 | return inventory.count(item_id) && inventory.at(item_id) >= quantity; |
213 | } | 228 | } |
214 | 229 | ||
215 | bool HasAchievement(const std::string& name) { | 230 | bool HasItemSafe(int item_id, int quantity) { |
216 | std::lock_guard state_guard(state_mutex); | 231 | std::lock_guard state_guard(state_mutex); |
217 | 232 | return HasItem(item_id, quantity); | |
218 | std::string key = | ||
219 | fmt::format("{}Achievement|{}", data_storage_prefix, name); | ||
220 | return data_storage.count(key) && std::any_cast<bool>(data_storage.at(key)); | ||
221 | } | 233 | } |
222 | 234 | ||
223 | const std::set<std::string>& GetCheckedPaintings() { | 235 | const std::set<std::string>& GetCheckedPaintings() { |
@@ -241,7 +253,21 @@ struct APState { | |||
241 | checked_paintings.count(painting_mapping.at(painting_id))); | 253 | checked_paintings.count(painting_mapping.at(painting_id))); |
242 | } | 254 | } |
243 | 255 | ||
244 | std::string GetItemName(int id) { return apclient->get_item_name(id); } | 256 | void RevealPaintings() { |
257 | std::lock_guard state_guard(state_mutex); | ||
258 | |||
259 | std::vector<std::string> paintings; | ||
260 | for (const PaintingExit& painting : GD_GetPaintings()) { | ||
261 | paintings.push_back(painting.internal_id); | ||
262 | } | ||
263 | |||
264 | APClient::DataStorageOperation operation; | ||
265 | operation.operation = "replace"; | ||
266 | operation.value = paintings; | ||
267 | |||
268 | apclient->Set(fmt::format("{}Paintings", data_storage_prefix), "", true, | ||
269 | {operation}); | ||
270 | } | ||
245 | 271 | ||
246 | bool HasReachedGoal() { | 272 | bool HasReachedGoal() { |
247 | std::lock_guard state_guard(state_mutex); | 273 | std::lock_guard state_guard(state_mutex); |
@@ -251,6 +277,12 @@ struct APState { | |||
251 | 30; // CLIENT_GOAL | 277 | 30; // CLIENT_GOAL |
252 | } | 278 | } |
253 | 279 | ||
280 | bool IsPanelSolved(int solve_index) { | ||
281 | std::lock_guard state_guard(state_mutex); | ||
282 | |||
283 | return solved_panels.test(solve_index); | ||
284 | } | ||
285 | |||
254 | private: | 286 | private: |
255 | void Initialize() { | 287 | void Initialize() { |
256 | if (!initialized) { | 288 | if (!initialized) { |
@@ -258,16 +290,8 @@ struct APState { | |||
258 | 290 | ||
259 | std::thread([this]() { Thread(); }).detach(); | 291 | std::thread([this]() { Thread(); }).detach(); |
260 | 292 | ||
261 | for (int panel_id : GD_GetAchievementPanels()) { | 293 | for (int i = 0; i < PANEL_BITFIELDS; i++) { |
262 | tracked_data_storage_keys.push_back(fmt::format( | 294 | tracked_data_storage_keys.push_back(fmt::format("Panels_{}", i)); |
263 | "Achievement|{}", GD_GetPanel(panel_id).achievement_name)); | ||
264 | } | ||
265 | |||
266 | for (const MapArea& map_area : GD_GetMapAreas()) { | ||
267 | for (const Location& location : map_area.locations) { | ||
268 | tracked_data_storage_keys.push_back( | ||
269 | fmt::format("Hunt|{}", location.ap_location_id)); | ||
270 | } | ||
271 | } | 295 | } |
272 | 296 | ||
273 | tracked_data_storage_keys.push_back("PlayerPos"); | 297 | tracked_data_storage_keys.push_back("PlayerPos"); |
@@ -351,7 +375,7 @@ struct APState { | |||
351 | } | 375 | } |
352 | } | 376 | } |
353 | 377 | ||
354 | RefreshTracker(false); | 378 | RefreshTracker(StateUpdate{.cleared_locations = true}); |
355 | } | 379 | } |
356 | 380 | ||
357 | void OnSlotDisconnected() { | 381 | void OnSlotDisconnected() { |
@@ -373,37 +397,59 @@ struct APState { | |||
373 | } | 397 | } |
374 | 398 | ||
375 | void OnItemsReceived(const std::list<APClient::NetworkItem>& items) { | 399 | void OnItemsReceived(const std::list<APClient::NetworkItem>& items) { |
400 | std::vector<ItemState> item_states; | ||
401 | bool progression_items = false; | ||
402 | |||
376 | { | 403 | { |
377 | std::lock_guard state_guard(state_mutex); | 404 | std::lock_guard state_guard(state_mutex); |
378 | 405 | ||
406 | std::map<int64_t, int> index_by_item; | ||
407 | |||
379 | for (const APClient::NetworkItem& item : items) { | 408 | for (const APClient::NetworkItem& item : items) { |
380 | inventory[item.item]++; | 409 | inventory[item.item]++; |
381 | TrackerLog(fmt::format("Item: {}", item.item)); | 410 | TrackerLog(fmt::format("Item: {}", item.item)); |
411 | |||
412 | index_by_item[item.item] = item.index; | ||
413 | |||
414 | if (!kNonProgressionItems.count(item.item)) { | ||
415 | progression_items = true; | ||
416 | } | ||
417 | } | ||
418 | |||
419 | for (const auto& [item_id, item_index] : index_by_item) { | ||
420 | item_states.push_back(ItemState{.name = GD_GetItemName(item_id), | ||
421 | .amount = inventory[item_id], | ||
422 | .index = item_index}); | ||
382 | } | 423 | } |
383 | } | 424 | } |
384 | 425 | ||
385 | RefreshTracker(false); | 426 | RefreshTracker(StateUpdate{.items = item_states, |
427 | .progression_items = progression_items}); | ||
386 | } | 428 | } |
387 | 429 | ||
388 | void OnRetrieved(const std::map<std::string, nlohmann::json>& data) { | 430 | void OnRetrieved(const std::map<std::string, nlohmann::json>& data) { |
431 | StateUpdate state_update; | ||
432 | |||
389 | { | 433 | { |
390 | std::lock_guard state_guard(state_mutex); | 434 | std::lock_guard state_guard(state_mutex); |
391 | 435 | ||
392 | for (const auto& [key, value] : data) { | 436 | for (const auto& [key, value] : data) { |
393 | HandleDataStorage(key, value); | 437 | HandleDataStorage(key, value, state_update); |
394 | } | 438 | } |
395 | } | 439 | } |
396 | 440 | ||
397 | RefreshTracker(false); | 441 | RefreshTracker(state_update); |
398 | } | 442 | } |
399 | 443 | ||
400 | void OnSetReply(const std::string& key, const nlohmann::json& value) { | 444 | void OnSetReply(const std::string& key, const nlohmann::json& value) { |
445 | StateUpdate state_update; | ||
446 | |||
401 | { | 447 | { |
402 | std::lock_guard state_guard(state_mutex); | 448 | std::lock_guard state_guard(state_mutex); |
403 | HandleDataStorage(key, value); | 449 | HandleDataStorage(key, value, state_update); |
404 | } | 450 | } |
405 | 451 | ||
406 | RefreshTracker(false); | 452 | RefreshTracker(state_update); |
407 | } | 453 | } |
408 | 454 | ||
409 | void OnSlotConnected(std::string player, std::string server, | 455 | void OnSlotConnected(std::string player, std::string server, |
@@ -435,6 +481,7 @@ struct APState { | |||
435 | } | 481 | } |
436 | } | 482 | } |
437 | color_shuffle = slot_data["shuffle_colors"].get<int>() == 1; | 483 | color_shuffle = slot_data["shuffle_colors"].get<int>() == 1; |
484 | panel_shuffle_mode = slot_data["shuffle_panels"].get<PanelShuffleMode>(); | ||
438 | painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1; | 485 | painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1; |
439 | mastery_requirement = slot_data["mastery_achievements"].get<int>(); | 486 | mastery_requirement = slot_data["mastery_achievements"].get<int>(); |
440 | level_2_requirement = slot_data["level_2_requirement"].get<int>(); | 487 | level_2_requirement = slot_data["level_2_requirement"].get<int>(); |
@@ -456,6 +503,9 @@ struct APState { | |||
456 | : kSUNWARP_ACCESS_NORMAL; | 503 | : kSUNWARP_ACCESS_NORMAL; |
457 | sunwarp_shuffle = slot_data.contains("shuffle_sunwarps") && | 504 | sunwarp_shuffle = slot_data.contains("shuffle_sunwarps") && |
458 | slot_data["shuffle_sunwarps"].get<int>() == 1; | 505 | slot_data["shuffle_sunwarps"].get<int>() == 1; |
506 | postgame_shuffle = slot_data.contains("shuffle_postgame") | ||
507 | ? (slot_data["shuffle_postgame"].get<int>() == 1) | ||
508 | : true; | ||
459 | 509 | ||
460 | if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { | 510 | if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { |
461 | painting_mapping.clear(); | 511 | painting_mapping.clear(); |
@@ -497,7 +547,7 @@ struct APState { | |||
497 | } | 547 | } |
498 | 548 | ||
499 | ResetReachabilityRequirements(); | 549 | ResetReachabilityRequirements(); |
500 | RefreshTracker(true); | 550 | RefreshTracker(std::nullopt); |
501 | } | 551 | } |
502 | 552 | ||
503 | void OnSlotRefused(const std::list<std::string>& errors) { | 553 | void OnSlotRefused(const std::list<std::string>& errors) { |
@@ -540,19 +590,40 @@ struct APState { | |||
540 | } | 590 | } |
541 | 591 | ||
542 | // Assumes state mutex is locked. | 592 | // Assumes state mutex is locked. |
543 | void HandleDataStorage(const std::string& key, const nlohmann::json& value) { | 593 | void HandleDataStorage(const std::string& key, const nlohmann::json& value, StateUpdate& state_update) { |
544 | if (value.is_boolean()) { | 594 | if (value.is_boolean()) { |
545 | data_storage[key] = value.get<bool>(); | 595 | data_storage[key] = value.get<bool>(); |
546 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, | 596 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, |
547 | (value.get<bool>() ? "true" : "false"))); | 597 | (value.get<bool>() ? "true" : "false"))); |
598 | |||
548 | } else if (value.is_number()) { | 599 | } else if (value.is_number()) { |
549 | data_storage[key] = value.get<int>(); | 600 | data_storage[key] = value.get<int>(); |
550 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, | 601 | TrackerLog(fmt::format("Data storage {} retrieved as {}", key, |
551 | value.get<int>())); | 602 | value.get<int>())); |
603 | |||
604 | if (key == victory_data_storage_key) { | ||
605 | state_update.cleared_locations = true; | ||
606 | } else if (key.find("Panels_") != std::string::npos) { | ||
607 | int bitfield_num = | ||
608 | std::stoi(key.substr(data_storage_prefix.size() + 7)); | ||
609 | uint64_t bitfield_value = value.get<uint64_t>(); | ||
610 | for (int i = 0; i < PANEL_BITFIELD_LENGTH; i++) { | ||
611 | if ((bitfield_value & (1LL << i)) != 0) { | ||
612 | int solve_index = bitfield_num * PANEL_BITFIELD_LENGTH + i; | ||
613 | |||
614 | if (!solved_panels.test(solve_index)) { | ||
615 | state_update.panels.insert(solve_index); | ||
616 | } | ||
617 | |||
618 | solved_panels.set(solve_index); | ||
619 | } | ||
620 | } | ||
621 | } | ||
552 | } else if (value.is_object()) { | 622 | } else if (value.is_object()) { |
553 | if (key.ends_with("PlayerPos")) { | 623 | if (key.ends_with("PlayerPos")) { |
554 | auto map_value = value.get<std::map<std::string, int>>(); | 624 | auto map_value = value.get<std::map<std::string, int>>(); |
555 | player_pos = std::tuple<int, int>(map_value["x"], map_value["z"]); | 625 | player_pos = std::tuple<int, int>(map_value["x"], map_value["z"]); |
626 | state_update.player_position = true; | ||
556 | } else { | 627 | } else { |
557 | data_storage[key] = value.get<std::map<std::string, int>>(); | 628 | data_storage[key] = value.get<std::map<std::string, int>>(); |
558 | } | 629 | } |
@@ -561,6 +632,7 @@ struct APState { | |||
561 | } else if (value.is_null()) { | 632 | } else if (value.is_null()) { |
562 | if (key.ends_with("PlayerPos")) { | 633 | if (key.ends_with("PlayerPos")) { |
563 | player_pos = std::nullopt; | 634 | player_pos = std::nullopt; |
635 | state_update.player_position = true; | ||
564 | } else { | 636 | } else { |
565 | data_storage.erase(key); | 637 | data_storage.erase(key); |
566 | } | 638 | } |
@@ -572,6 +644,8 @@ struct APState { | |||
572 | if (key.ends_with("Paintings")) { | 644 | if (key.ends_with("Paintings")) { |
573 | data_storage[key] = | 645 | data_storage[key] = |
574 | std::set<std::string>(list_value.begin(), list_value.end()); | 646 | std::set<std::string>(list_value.begin(), list_value.end()); |
647 | state_update.paintings = | ||
648 | std::vector<std::string>(list_value.begin(), list_value.end()); | ||
575 | } else { | 649 | } else { |
576 | data_storage[key] = list_value; | 650 | data_storage[key] = list_value; |
577 | } | 651 | } |
@@ -582,29 +656,34 @@ struct APState { | |||
582 | } | 656 | } |
583 | 657 | ||
584 | // State mutex should NOT be locked. | 658 | // State mutex should NOT be locked. |
585 | void RefreshTracker(bool reset) { | 659 | // nullopt state_update indicates a reset. |
660 | void RefreshTracker(std::optional<StateUpdate> state_update) { | ||
586 | TrackerLog("Refreshing display..."); | 661 | TrackerLog("Refreshing display..."); |
587 | 662 | ||
588 | std::string prev_msg; | 663 | if (!state_update || state_update->progression_items || |
589 | { | 664 | !state_update->paintings.empty()) { |
590 | std::lock_guard state_guard(state_mutex); | 665 | std::string prev_msg; |
666 | { | ||
667 | std::lock_guard state_guard(state_mutex); | ||
591 | 668 | ||
592 | prev_msg = status_message; | 669 | prev_msg = status_message; |
593 | SetStatusMessage(fmt::format("{} Recalculating...", status_message)); | 670 | SetStatusMessage(fmt::format("{} Recalculating...", status_message)); |
594 | } | 671 | } |
595 | 672 | ||
596 | RecalculateReachability(); | 673 | RecalculateReachability(); |
597 | 674 | ||
598 | if (reset) { | 675 | { |
599 | tracker_frame->ResetIndicators(); | 676 | std::lock_guard state_guard(state_mutex); |
600 | } else { | ||
601 | tracker_frame->UpdateIndicators(); | ||
602 | } | ||
603 | 677 | ||
604 | { | 678 | SetStatusMessage(prev_msg); |
605 | std::lock_guard state_guard(state_mutex); | 679 | } |
680 | } | ||
681 | |||
606 | 682 | ||
607 | SetStatusMessage(prev_msg); | 683 | if (!state_update) { |
684 | tracker_frame->ResetIndicators(); | ||
685 | } else { | ||
686 | tracker_frame->UpdateIndicators(*state_update); | ||
608 | } | 687 | } |
609 | } | 688 | } |
610 | 689 | ||
@@ -639,16 +718,12 @@ bool AP_HasCheckedGameLocation(int location_id) { | |||
639 | return GetState().HasCheckedGameLocation(location_id); | 718 | return GetState().HasCheckedGameLocation(location_id); |
640 | } | 719 | } |
641 | 720 | ||
642 | bool AP_HasCheckedHuntPanel(int location_id) { | ||
643 | return GetState().HasCheckedHuntPanel(location_id); | ||
644 | } | ||
645 | |||
646 | bool AP_HasItem(int item_id, int quantity) { | 721 | bool AP_HasItem(int item_id, int quantity) { |
647 | return GetState().HasItem(item_id, quantity); | 722 | return GetState().HasItem(item_id, quantity); |
648 | } | 723 | } |
649 | 724 | ||
650 | std::string AP_GetItemName(int item_id) { | 725 | bool AP_HasItemSafe(int item_id, int quantity) { |
651 | return GetState().GetItemName(item_id); | 726 | return GetState().HasItemSafe(item_id, quantity); |
652 | } | 727 | } |
653 | 728 | ||
654 | DoorShuffleMode AP_GetDoorShuffleMode() { | 729 | DoorShuffleMode AP_GetDoorShuffleMode() { |
@@ -695,6 +770,8 @@ bool AP_IsPaintingChecked(const std::string& painting_id) { | |||
695 | return GetState().IsPaintingChecked(painting_id); | 770 | return GetState().IsPaintingChecked(painting_id); |
696 | } | 771 | } |
697 | 772 | ||
773 | void AP_RevealPaintings() { GetState().RevealPaintings(); } | ||
774 | |||
698 | int AP_GetMasteryRequirement() { | 775 | int AP_GetMasteryRequirement() { |
699 | std::lock_guard state_guard(GetState().state_mutex); | 776 | std::lock_guard state_guard(GetState().state_mutex); |
700 | 777 | ||
@@ -707,6 +784,12 @@ int AP_GetLevel2Requirement() { | |||
707 | return GetState().level_2_requirement; | 784 | return GetState().level_2_requirement; |
708 | } | 785 | } |
709 | 786 | ||
787 | LocationChecks AP_GetLocationsChecks() { | ||
788 | std::lock_guard state_guard(GetState().state_mutex); | ||
789 | |||
790 | return GetState().location_checks; | ||
791 | } | ||
792 | |||
710 | bool AP_IsLocationVisible(int classification) { | 793 | bool AP_IsLocationVisible(int classification) { |
711 | std::lock_guard state_guard(GetState().state_mutex); | 794 | std::lock_guard state_guard(GetState().state_mutex); |
712 | 795 | ||
@@ -734,14 +817,16 @@ bool AP_IsLocationVisible(int classification) { | |||
734 | return (world_state & classification); | 817 | return (world_state & classification); |
735 | } | 818 | } |
736 | 819 | ||
737 | VictoryCondition AP_GetVictoryCondition() { | 820 | PanelShuffleMode AP_GetPanelShuffleMode() { |
738 | std::lock_guard state_guard(GetState().state_mutex); | 821 | std::lock_guard state_guard(GetState().state_mutex); |
739 | 822 | ||
740 | return GetState().victory_condition; | 823 | return GetState().panel_shuffle_mode; |
741 | } | 824 | } |
742 | 825 | ||
743 | bool AP_HasAchievement(const std::string& achievement_name) { | 826 | VictoryCondition AP_GetVictoryCondition() { |
744 | return GetState().HasAchievement(achievement_name); | 827 | std::lock_guard state_guard(GetState().state_mutex); |
828 | |||
829 | return GetState().victory_condition; | ||
745 | } | 830 | } |
746 | 831 | ||
747 | bool AP_HasEarlyColorHallways() { | 832 | bool AP_HasEarlyColorHallways() { |
@@ -784,6 +869,8 @@ std::map<int, SunwarpMapping> AP_GetSunwarpMapping() { | |||
784 | return GetState().sunwarp_mapping; | 869 | return GetState().sunwarp_mapping; |
785 | } | 870 | } |
786 | 871 | ||
872 | bool AP_IsPostgameShuffle() { return GetState().postgame_shuffle; } | ||
873 | |||
787 | bool AP_HasReachedGoal() { return GetState().HasReachedGoal(); } | 874 | bool AP_HasReachedGoal() { return GetState().HasReachedGoal(); } |
788 | 875 | ||
789 | std::optional<std::tuple<int, int>> AP_GetPlayerPosition() { | 876 | std::optional<std::tuple<int, int>> AP_GetPlayerPosition() { |
@@ -791,3 +878,7 @@ std::optional<std::tuple<int, int>> AP_GetPlayerPosition() { | |||
791 | 878 | ||
792 | return GetState().player_pos; | 879 | return GetState().player_pos; |
793 | } | 880 | } |
881 | |||
882 | bool AP_IsPanelSolved(int solve_index) { | ||
883 | return GetState().IsPanelSolved(solve_index); | ||
884 | } | ||