about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/ap_state.cpp15
-rw-r--r--src/ap_state.h4
-rw-r--r--src/game_data.cpp119
-rw-r--r--src/game_data.h8
-rw-r--r--src/tracker_state.cpp49
-rw-r--r--src/version.cpp5
6 files changed, 175 insertions, 25 deletions
diff --git a/src/ap_state.cpp b/src/ap_state.cpp index f8d4ee0..fbd8d12 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp
@@ -60,6 +60,7 @@ struct APState {
60 std::optional<std::tuple<int, int>> player_pos; 60 std::optional<std::tuple<int, int>> player_pos;
61 61
62 DoorShuffleMode door_shuffle_mode = kNO_DOORS; 62 DoorShuffleMode door_shuffle_mode = kNO_DOORS;
63 bool group_doors = false;
63 bool color_shuffle = false; 64 bool color_shuffle = false;
64 bool painting_shuffle = false; 65 bool painting_shuffle = false;
65 int mastery_requirement = 21; 66 int mastery_requirement = 21;
@@ -140,6 +141,7 @@ struct APState {
140 player_pos = std::nullopt; 141 player_pos = std::nullopt;
141 victory_data_storage_key.clear(); 142 victory_data_storage_key.clear();
142 door_shuffle_mode = kNO_DOORS; 143 door_shuffle_mode = kNO_DOORS;
144 group_doors = false;
143 color_shuffle = false; 145 color_shuffle = false;
144 painting_shuffle = false; 146 painting_shuffle = false;
145 painting_mapping.clear(); 147 painting_mapping.clear();
@@ -236,6 +238,17 @@ struct APState {
236 data_storage_prefix = 238 data_storage_prefix =
237 fmt::format("Lingo_{}_", apclient->get_player_number()); 239 fmt::format("Lingo_{}_", apclient->get_player_number());
238 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>(); 240 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>();
241 if (slot_data.contains("group_doors")) {
242 group_doors = slot_data.contains("group_doors") &&
243 slot_data["group_doors"].get<int>() == 1;
244 } else {
245 // If group_doors doesn't exist yet, that means kPANELS_MODE is actually
246 // kSIMPLE_DOORS.
247 if (door_shuffle_mode == kPANELS_MODE) {
248 door_shuffle_mode = kDOORS_MODE;
249 group_doors = true;
250 }
251 }
239 color_shuffle = slot_data["shuffle_colors"].get<int>() == 1; 252 color_shuffle = slot_data["shuffle_colors"].get<int>() == 1;
240 painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1; 253 painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1;
241 mastery_requirement = slot_data["mastery_achievements"].get<int>(); 254 mastery_requirement = slot_data["mastery_achievements"].get<int>();
@@ -534,6 +547,8 @@ std::string AP_GetItemName(int item_id) {
534 547
535DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } 548DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; }
536 549
550bool AP_AreDoorsGrouped() { return GetState().group_doors; }
551
537bool AP_IsColorShuffle() { return GetState().color_shuffle; } 552bool AP_IsColorShuffle() { return GetState().color_shuffle; }
538 553
539bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } 554bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; }
diff --git a/src/ap_state.h b/src/ap_state.h index f8936e5..e06d4ff 100644 --- a/src/ap_state.h +++ b/src/ap_state.h
@@ -11,7 +11,7 @@
11 11
12class TrackerFrame; 12class TrackerFrame;
13 13
14enum DoorShuffleMode { kNO_DOORS = 0, kSIMPLE_DOORS = 1, kCOMPLEX_DOORS = 2 }; 14enum DoorShuffleMode { kNO_DOORS = 0, kPANELS_MODE = 1, kDOORS_MODE = 2 };
15 15
16enum VictoryCondition { 16enum VictoryCondition {
17 kTHE_END = 0, 17 kTHE_END = 0,
@@ -55,6 +55,8 @@ std::string AP_GetItemName(int item_id);
55 55
56DoorShuffleMode AP_GetDoorShuffleMode(); 56DoorShuffleMode AP_GetDoorShuffleMode();
57 57
58bool AP_AreDoorsGrouped();
59
58bool AP_IsColorShuffle(); 60bool AP_IsColorShuffle();
59 61
60bool AP_IsPaintingShuffle(); 62bool AP_IsPaintingShuffle();
diff --git a/src/game_data.cpp b/src/game_data.cpp index b8e1386..0786edb 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp
@@ -42,6 +42,7 @@ struct GameData {
42 std::vector<Room> rooms_; 42 std::vector<Room> rooms_;
43 std::vector<Door> doors_; 43 std::vector<Door> doors_;
44 std::vector<Panel> panels_; 44 std::vector<Panel> panels_;
45 std::vector<PanelDoor> panel_doors_;
45 std::vector<MapArea> map_areas_; 46 std::vector<MapArea> map_areas_;
46 std::vector<SubwayItem> subway_items_; 47 std::vector<SubwayItem> subway_items_;
47 std::vector<PaintingExit> paintings_; 48 std::vector<PaintingExit> paintings_;
@@ -49,6 +50,7 @@ struct GameData {
49 std::map<std::string, int> room_by_id_; 50 std::map<std::string, int> room_by_id_;
50 std::map<std::string, int> door_by_id_; 51 std::map<std::string, int> door_by_id_;
51 std::map<std::string, int> panel_by_id_; 52 std::map<std::string, int> panel_by_id_;
53 std::map<std::string, int> panel_doors_by_id_;
52 std::map<std::string, int> area_by_id_; 54 std::map<std::string, int> area_by_id_;
53 std::map<std::string, int> painting_by_id_; 55 std::map<std::string, int> painting_by_id_;
54 56
@@ -430,6 +432,55 @@ struct GameData {
430 } 432 }
431 } 433 }
432 434
435 if (room_it.second["panel_doors"]) {
436 for (const auto &panel_door_it : room_it.second["panel_doors"]) {
437 std::string panel_door_name = panel_door_it.first.as<std::string>();
438 int panel_door_id =
439 AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name);
440
441 for (const auto &panel_node : panel_door_it.second["panels"]) {
442 int panel_id = -1;
443
444 if (panel_node.IsScalar()) {
445 panel_id = AddOrGetPanel(rooms_[room_id].name,
446 panel_node.as<std::string>());
447 } else {
448 panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(),
449 panel_node["panel"].as<std::string>());
450 }
451
452 Panel &panel = panels_[panel_id];
453 panel.panel_door = panel_door_id;
454 }
455
456 if (ids_config["panel_doors"] &&
457 ids_config["panel_doors"][rooms_[room_id].name] &&
458 ids_config["panel_doors"][rooms_[room_id].name]
459 [panel_door_name]) {
460 panel_doors_[panel_door_id].ap_item_id =
461 ids_config["panel_doors"][rooms_[room_id].name][panel_door_name]
462 .as<int>();
463 } else {
464 TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}",
465 rooms_[room_id].name, panel_door_name));
466 }
467
468 if (panel_door_it.second["panel_group"]) {
469 std::string panel_group =
470 panel_door_it.second["panel_group"].as<std::string>();
471
472 if (ids_config["panel_groups"] &&
473 ids_config["panel_groups"][panel_group]) {
474 panel_doors_[panel_door_id].group_ap_item_id =
475 ids_config["panel_groups"][panel_group].as<int>();
476 } else {
477 TrackerLog(fmt::format(
478 "Missing AP item ID for panel door group {}", panel_group));
479 }
480 }
481 }
482 }
483
433 if (room_it.second["paintings"]) { 484 if (room_it.second["paintings"]) {
434 for (const auto &painting : room_it.second["paintings"]) { 485 for (const auto &painting : room_it.second["paintings"]) {
435 std::string internal_id = painting["id"].as<std::string>(); 486 std::string internal_id = painting["id"].as<std::string>();
@@ -483,23 +534,47 @@ struct GameData {
483 progressive_item_name)); 534 progressive_item_name));
484 } 535 }
485 536
486 int index = 1; 537 if (progression_it.second["doors"]) {
487 for (const auto &stage : progression_it.second) { 538 int index = 1;
488 int door_id = -1; 539 for (const auto &stage : progression_it.second["doors"]) {
540 int door_id = -1;
489 541
490 if (stage.IsScalar()) { 542 if (stage.IsScalar()) {
491 door_id = 543 door_id =
492 AddOrGetDoor(rooms_[room_id].name, stage.as<std::string>()); 544 AddOrGetDoor(rooms_[room_id].name, stage.as<std::string>());
493 } else { 545 } else {
494 door_id = AddOrGetDoor(stage["room"].as<std::string>(), 546 door_id = AddOrGetDoor(stage["room"].as<std::string>(),
495 stage["door"].as<std::string>()); 547 stage["door"].as<std::string>());
548 }
549
550 doors_[door_id].progressives.push_back(
551 {.item_name = progressive_item_name,
552 .ap_item_id = progressive_item_id,
553 .quantity = index});
554 index++;
496 } 555 }
556 }
497 557
498 doors_[door_id].progressives.push_back( 558 if (progression_it.second["panel_doors"]) {
499 {.item_name = progressive_item_name, 559 int index = 1;
500 .ap_item_id = progressive_item_id, 560 for (const auto &stage : progression_it.second["panel_doors"]) {
501 .quantity = index}); 561 int panel_door_id = -1;
502 index++; 562
563 if (stage.IsScalar()) {
564 panel_door_id = AddOrGetPanelDoor(rooms_[room_id].name,
565 stage.as<std::string>());
566 } else {
567 panel_door_id =
568 AddOrGetPanelDoor(stage["room"].as<std::string>(),
569 stage["panel_door"].as<std::string>());
570 }
571
572 panel_doors_[panel_door_id].progressives.push_back(
573 {.item_name = progressive_item_name,
574 .ap_item_id = progressive_item_id,
575 .quantity = index});
576 index++;
577 }
503 } 578 }
504 } 579 }
505 } 580 }
@@ -760,6 +835,18 @@ struct GameData {
760 return panel_by_id_[full_name]; 835 return panel_by_id_[full_name];
761 } 836 }
762 837
838 int AddOrGetPanelDoor(std::string room, std::string panel) {
839 std::string full_name = room + " - " + panel;
840
841 if (!panel_doors_by_id_.count(full_name)) {
842 int panel_door_id = panel_doors_.size();
843 panel_doors_by_id_[full_name] = panel_door_id;
844 panel_doors_.push_back({});
845 }
846
847 return panel_doors_by_id_[full_name];
848 }
849
763 int AddOrGetArea(std::string area) { 850 int AddOrGetArea(std::string area) {
764 if (!area_by_id_.count(area)) { 851 if (!area_by_id_.count(area)) {
765 if (loaded_area_data_) { 852 if (loaded_area_data_) {
@@ -810,6 +897,10 @@ const std::vector<Door> &GD_GetDoors() { return GetState().doors_; }
810 897
811const Door &GD_GetDoor(int door_id) { return GetState().doors_.at(door_id); } 898const Door &GD_GetDoor(int door_id) { return GetState().doors_.at(door_id); }
812 899
900const PanelDoor &GD_GetPanelDoor(int panel_door_id) {
901 return GetState().panel_doors_.at(panel_door_id);
902}
903
813int GD_GetDoorByName(const std::string &name) { 904int GD_GetDoorByName(const std::string &name) {
814 return GetState().door_by_id_.at(name); 905 return GetState().door_by_id_.at(name);
815} 906}
diff --git a/src/game_data.h b/src/game_data.h index 71bc533..6f287cf 100644 --- a/src/game_data.h +++ b/src/game_data.h
@@ -56,6 +56,7 @@ struct Panel {
56 bool non_counting = false; 56 bool non_counting = false;
57 int ap_location_id = -1; 57 int ap_location_id = -1;
58 bool hunt = false; 58 bool hunt = false;
59 int panel_door = -1;
59}; 60};
60 61
61struct ProgressiveRequirement { 62struct ProgressiveRequirement {
@@ -83,6 +84,12 @@ struct Door {
83 DoorType type = DoorType::kNormal; 84 DoorType type = DoorType::kNormal;
84}; 85};
85 86
87struct PanelDoor {
88 int ap_item_id = -1;
89 int group_ap_item_id = -1;
90 std::vector<ProgressiveRequirement> progressives;
91};
92
86struct Exit { 93struct Exit {
87 int source_room; 94 int source_room;
88 int destination_room; 95 int destination_room;
@@ -161,6 +168,7 @@ const std::vector<Door>& GD_GetDoors();
161const Door& GD_GetDoor(int door_id); 168const Door& GD_GetDoor(int door_id);
162int GD_GetDoorByName(const std::string& name); 169int GD_GetDoorByName(const std::string& name);
163const Panel& GD_GetPanel(int panel_id); 170const Panel& GD_GetPanel(int panel_id);
171const PanelDoor& GD_GetPanelDoor(int panel_door_id);
164const PaintingExit& GD_GetPaintingExit(int painting_id); 172const PaintingExit& GD_GetPaintingExit(int painting_id);
165int GD_GetPaintingByName(const std::string& name); 173int GD_GetPaintingByName(const std::string& name);
166const std::vector<int>& GD_GetAchievementPanels(); 174const std::vector<int>& GD_GetAchievementPanels();
diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index bd63076..18bb499 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp
@@ -19,11 +19,12 @@ namespace {
19struct Requirements { 19struct Requirements {
20 bool disabled = false; 20 bool disabled = false;
21 21
22 std::set<int> doors; // non-grouped, handles progressive 22 std::set<int> doors; // non-grouped, handles progressive
23 std::set<int> items; // all other items 23 std::set<int> panel_doors; // non-grouped, handles progressive
24 std::set<int> rooms; // maybe 24 std::set<int> items; // all other items
25 bool mastery = false; // maybe 25 std::set<int> rooms; // maybe
26 bool panel_hunt = false; // maybe 26 bool mastery = false; // maybe
27 bool panel_hunt = false; // maybe
27 28
28 void Merge(const Requirements& rhs) { 29 void Merge(const Requirements& rhs) {
29 if (rhs.disabled) { 30 if (rhs.disabled) {
@@ -33,6 +34,9 @@ struct Requirements {
33 for (int id : rhs.doors) { 34 for (int id : rhs.doors) {
34 doors.insert(id); 35 doors.insert(id);
35 } 36 }
37 for (int id : rhs.panel_doors) {
38 panel_doors.insert(id);
39 }
36 for (int id : rhs.items) { 40 for (int id : rhs.items) {
37 items.insert(id); 41 items.insert(id);
38 } 42 }
@@ -78,15 +82,14 @@ class RequirementCalculator {
78 requirements.doors.insert(door_obj.id); 82 requirements.doors.insert(door_obj.id);
79 break; 83 break;
80 } 84 }
81 } else if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { 85 } else if (AP_GetDoorShuffleMode() != kDOORS_MODE || door_obj.skip_item) {
82 requirements.rooms.insert(door_obj.room); 86 requirements.rooms.insert(door_obj.room);
83 87
84 for (int panel_id : door_obj.panels) { 88 for (int panel_id : door_obj.panels) {
85 const Requirements& panel_reqs = GetPanel(panel_id); 89 const Requirements& panel_reqs = GetPanel(panel_id);
86 requirements.Merge(panel_reqs); 90 requirements.Merge(panel_reqs);
87 } 91 }
88 } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && 92 } else if (AP_AreDoorsGrouped() && !door_obj.group_name.empty()) {
89 !door_obj.group_name.empty()) {
90 requirements.items.insert(door_obj.group_ap_item_id); 93 requirements.items.insert(door_obj.group_ap_item_id);
91 } else { 94 } else {
92 requirements.doors.insert(door_obj.id); 95 requirements.doors.insert(door_obj.id);
@@ -133,6 +136,17 @@ class RequirementCalculator {
133 requirements.items.insert(GD_GetItemIdForColor(color)); 136 requirements.items.insert(GD_GetItemIdForColor(color));
134 } 137 }
135 } 138 }
139
140 if (panel_obj.panel_door != -1 &&
141 AP_GetDoorShuffleMode() == kPANELS_MODE) {
142 const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_obj.panel_door);
143
144 if (panel_door_obj.group_ap_item_id != -1 && AP_AreDoorsGrouped()) {
145 requirements.items.insert(panel_door_obj.group_ap_item_id);
146 } else {
147 requirements.panel_doors.insert(panel_obj.panel_door);
148 }
149 }
136 150
137 panels_[panel_id] = requirements; 151 panels_[panel_id] = requirements;
138 } 152 }
@@ -295,7 +309,7 @@ class StateCalculator {
295 if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") { 309 if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") {
296 new_boundary.push_back( 310 new_boundary.push_back(
297 {.source_room = room_exit.destination_room, 311 {.source_room = room_exit.destination_room,
298 .destination_room = GD_GetRoomByName("Outside The Undeterred"), 312 .destination_room = GD_GetRoomByName("Color Hallways"),
299 .type = EntranceType::kPainting}); 313 .type = EntranceType::kPainting});
300 } 314 }
301 315
@@ -375,7 +389,8 @@ class StateCalculator {
375 } 389 }
376 390
377 private: 391 private:
378 Decision IsNonGroupedDoorReachable(const Door& door_obj) { 392 template <typename T>
393 Decision IsNonGroupedDoorReachable(const T& door_obj) {
379 bool has_item = AP_HasItem(door_obj.ap_item_id); 394 bool has_item = AP_HasItem(door_obj.ap_item_id);
380 395
381 if (!has_item) { 396 if (!has_item) {
@@ -411,6 +426,20 @@ class StateCalculator {
411 } 426 }
412 } 427 }
413 428
429 for (int panel_door_id : reqs.panel_doors) {
430 const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_door_id);
431 Decision decision = IsNonGroupedDoorReachable(panel_door_obj);
432
433 if (report) {
434 (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] =
435 (decision == kYes);
436 }
437
438 if (decision != kYes) {
439 final_decision = decision;
440 }
441 }
442
414 for (int item_id : reqs.items) { 443 for (int item_id : reqs.items) {
415 bool has_item = AP_HasItem(item_id); 444 bool has_item = AP_HasItem(item_id);
416 if (report) { 445 if (report) {
diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 0000000..3b4d5f3 --- /dev/null +++ b/src/version.cpp
@@ -0,0 +1,5 @@
1#include "version.h"
2
3std::ostream& operator<<(std::ostream& out, const Version& ver) {
4 return out << "v" << ver.major << "." << ver.minor << "." << ver.revision;
5}