diff options
Diffstat (limited to 'src/game_data.cpp')
-rw-r--r-- | src/game_data.cpp | 184 |
1 files changed, 123 insertions, 61 deletions
diff --git a/src/game_data.cpp b/src/game_data.cpp index a4a441d..588ffc8 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp | |||
@@ -12,32 +12,6 @@ | |||
12 | 12 | ||
13 | namespace { | 13 | namespace { |
14 | 14 | ||
15 | LingoColor GetColorForString(const std::string &str) { | ||
16 | if (str == "black") { | ||
17 | return LingoColor::kBlack; | ||
18 | } else if (str == "red") { | ||
19 | return LingoColor::kRed; | ||
20 | } else if (str == "blue") { | ||
21 | return LingoColor::kBlue; | ||
22 | } else if (str == "yellow") { | ||
23 | return LingoColor::kYellow; | ||
24 | } else if (str == "orange") { | ||
25 | return LingoColor::kOrange; | ||
26 | } else if (str == "green") { | ||
27 | return LingoColor::kGreen; | ||
28 | } else if (str == "gray") { | ||
29 | return LingoColor::kGray; | ||
30 | } else if (str == "brown") { | ||
31 | return LingoColor::kBrown; | ||
32 | } else if (str == "purple") { | ||
33 | return LingoColor::kPurple; | ||
34 | } else { | ||
35 | TrackerLog(fmt::format("Invalid color: {}", str)); | ||
36 | |||
37 | return LingoColor::kNone; | ||
38 | } | ||
39 | } | ||
40 | |||
41 | struct GameData { | 15 | struct GameData { |
42 | std::vector<Room> rooms_; | 16 | std::vector<Room> rooms_; |
43 | std::vector<Door> doors_; | 17 | std::vector<Door> doors_; |
@@ -55,10 +29,10 @@ struct GameData { | |||
55 | std::map<std::string, int> painting_by_id_; | 29 | std::map<std::string, int> painting_by_id_; |
56 | 30 | ||
57 | std::vector<int> door_definition_order_; | 31 | std::vector<int> door_definition_order_; |
58 | std::vector<int> room_definition_order_; | ||
59 | 32 | ||
60 | std::map<std::string, int> room_by_painting_; | 33 | std::map<std::string, int> room_by_painting_; |
61 | std::map<int, int> room_by_sunwarp_; | 34 | std::map<int, int> room_by_sunwarp_; |
35 | std::map<int, int> panel_by_solve_index_; | ||
62 | 36 | ||
63 | std::vector<int> achievement_panels_; | 37 | std::vector<int> achievement_panels_; |
64 | 38 | ||
@@ -69,6 +43,8 @@ struct GameData { | |||
69 | std::map<std::string, int> subway_item_by_painting_; | 43 | std::map<std::string, int> subway_item_by_painting_; |
70 | std::map<SubwaySunwarp, int> subway_item_by_sunwarp_; | 44 | std::map<SubwaySunwarp, int> subway_item_by_sunwarp_; |
71 | 45 | ||
46 | std::map<int, std::string> item_by_ap_id_; | ||
47 | |||
72 | bool loaded_area_data_ = false; | 48 | bool loaded_area_data_ = false; |
73 | std::set<std::string> malconfigured_areas_; | 49 | std::set<std::string> malconfigured_areas_; |
74 | 50 | ||
@@ -84,7 +60,7 @@ struct GameData { | |||
84 | ids_config["special_items"][color_name]) { | 60 | ids_config["special_items"][color_name]) { |
85 | std::string input_name = color_name; | 61 | std::string input_name = color_name; |
86 | input_name[0] = std::tolower(input_name[0]); | 62 | input_name[0] = std::tolower(input_name[0]); |
87 | ap_id_by_color_[GetColorForString(input_name)] = | 63 | ap_id_by_color_[GetLingoColorForString(input_name)] = |
88 | ids_config["special_items"][color_name].as<int>(); | 64 | ids_config["special_items"][color_name].as<int>(); |
89 | } else { | 65 | } else { |
90 | TrackerLog(fmt::format("Missing AP item ID for color {}", color_name)); | 66 | TrackerLog(fmt::format("Missing AP item ID for color {}", color_name)); |
@@ -101,11 +77,20 @@ struct GameData { | |||
101 | init_color_id("Brown"); | 77 | init_color_id("Brown"); |
102 | init_color_id("Gray"); | 78 | init_color_id("Gray"); |
103 | 79 | ||
80 | if (ids_config["special_items"]) { | ||
81 | for (const auto& special_item_it : ids_config["special_items"]) | ||
82 | { | ||
83 | item_by_ap_id_[special_item_it.second.as<int>()] = | ||
84 | special_item_it.first.as<std::string>(); | ||
85 | } | ||
86 | } | ||
87 | |||
104 | rooms_.reserve(lingo_config.size() * 2); | 88 | rooms_.reserve(lingo_config.size() * 2); |
105 | 89 | ||
90 | std::vector<int> panel_location_ids; | ||
91 | |||
106 | for (const auto &room_it : lingo_config) { | 92 | for (const auto &room_it : lingo_config) { |
107 | int room_id = AddOrGetRoom(room_it.first.as<std::string>()); | 93 | int room_id = AddOrGetRoom(room_it.first.as<std::string>()); |
108 | room_definition_order_.push_back(room_id); | ||
109 | 94 | ||
110 | for (const auto &entrance_it : room_it.second["entrances"]) { | 95 | for (const auto &entrance_it : room_it.second["entrances"]) { |
111 | int from_room_id = AddOrGetRoom(entrance_it.first.as<std::string>()); | 96 | int from_room_id = AddOrGetRoom(entrance_it.first.as<std::string>()); |
@@ -142,6 +127,10 @@ struct GameData { | |||
142 | exit_obj.type = EntranceType::kCrossroadsRoofAccess; | 127 | exit_obj.type = EntranceType::kCrossroadsRoofAccess; |
143 | } | 128 | } |
144 | 129 | ||
130 | if (option["static_painting"] && option["static_painting"].as<bool>()) { | ||
131 | exit_obj.type = EntranceType::kStaticPainting; | ||
132 | } | ||
133 | |||
145 | rooms_[from_room_id].exits.push_back(exit_obj); | 134 | rooms_[from_room_id].exits.push_back(exit_obj); |
146 | }; | 135 | }; |
147 | 136 | ||
@@ -181,12 +170,12 @@ struct GameData { | |||
181 | 170 | ||
182 | if (panel_it.second["colors"]) { | 171 | if (panel_it.second["colors"]) { |
183 | if (panel_it.second["colors"].IsScalar()) { | 172 | if (panel_it.second["colors"].IsScalar()) { |
184 | panels_[panel_id].colors.push_back(GetColorForString( | 173 | panels_[panel_id].colors.push_back(GetLingoColorForString( |
185 | panel_it.second["colors"].as<std::string>())); | 174 | panel_it.second["colors"].as<std::string>())); |
186 | } else { | 175 | } else { |
187 | for (const auto &color_node : panel_it.second["colors"]) { | 176 | for (const auto &color_node : panel_it.second["colors"]) { |
188 | panels_[panel_id].colors.push_back( | 177 | panels_[panel_id].colors.push_back( |
189 | GetColorForString(color_node.as<std::string>())); | 178 | GetLingoColorForString(color_node.as<std::string>())); |
190 | } | 179 | } |
191 | } | 180 | } |
192 | } | 181 | } |
@@ -292,10 +281,11 @@ struct GameData { | |||
292 | ids_config["panels"][rooms_[room_id].name] && | 281 | ids_config["panels"][rooms_[room_id].name] && |
293 | ids_config["panels"][rooms_[room_id].name] | 282 | ids_config["panels"][rooms_[room_id].name] |
294 | [panels_[panel_id].name]) { | 283 | [panels_[panel_id].name]) { |
295 | panels_[panel_id].ap_location_id = | 284 | int location_id = ids_config["panels"][rooms_[room_id].name] |
296 | ids_config["panels"][rooms_[room_id].name] | 285 | [panels_[panel_id].name] |
297 | [panels_[panel_id].name] | 286 | .as<int>(); |
298 | .as<int>(); | 287 | panels_[panel_id].ap_location_id = location_id; |
288 | panel_location_ids.push_back(location_id); | ||
299 | } else { | 289 | } else { |
300 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", | 290 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", |
301 | rooms_[room_id].name, | 291 | rooms_[room_id].name, |
@@ -361,6 +351,9 @@ struct GameData { | |||
361 | ids_config["doors"][rooms_[room_id].name] | 351 | ids_config["doors"][rooms_[room_id].name] |
362 | [doors_[door_id].name]["item"] | 352 | [doors_[door_id].name]["item"] |
363 | .as<int>(); | 353 | .as<int>(); |
354 | |||
355 | item_by_ap_id_[doors_[door_id].ap_item_id] = | ||
356 | doors_[door_id].item_name; | ||
364 | } else { | 357 | } else { |
365 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", | 358 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", |
366 | rooms_[room_id].name, | 359 | rooms_[room_id].name, |
@@ -377,6 +370,9 @@ struct GameData { | |||
377 | doors_[door_id].group_ap_item_id = | 370 | doors_[door_id].group_ap_item_id = |
378 | ids_config["door_groups"][doors_[door_id].group_name] | 371 | ids_config["door_groups"][doors_[door_id].group_name] |
379 | .as<int>(); | 372 | .as<int>(); |
373 | |||
374 | item_by_ap_id_[doors_[door_id].group_ap_item_id] = | ||
375 | doors_[door_id].group_name; | ||
380 | } else { | 376 | } else { |
381 | TrackerLog(fmt::format("Missing AP item ID for door group {}", | 377 | TrackerLog(fmt::format("Missing AP item ID for door group {}", |
382 | doors_[door_id].group_name)); | 378 | doors_[door_id].group_name)); |
@@ -440,21 +436,50 @@ struct GameData { | |||
440 | int panel_door_id = | 436 | int panel_door_id = |
441 | AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name); | 437 | AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name); |
442 | 438 | ||
439 | std::map<std::string, std::vector<std::string>> panel_per_room; | ||
440 | int num_panels = 0; | ||
443 | for (const auto &panel_node : panel_door_it.second["panels"]) { | 441 | for (const auto &panel_node : panel_door_it.second["panels"]) { |
442 | num_panels++; | ||
443 | |||
444 | int panel_id = -1; | 444 | int panel_id = -1; |
445 | 445 | ||
446 | if (panel_node.IsScalar()) { | 446 | if (panel_node.IsScalar()) { |
447 | panel_id = AddOrGetPanel(rooms_[room_id].name, | 447 | panel_id = AddOrGetPanel(rooms_[room_id].name, |
448 | panel_node.as<std::string>()); | 448 | panel_node.as<std::string>()); |
449 | |||
450 | panel_per_room[rooms_[room_id].name].push_back( | ||
451 | panel_node.as<std::string>()); | ||
449 | } else { | 452 | } else { |
450 | panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(), | 453 | panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(), |
451 | panel_node["panel"].as<std::string>()); | 454 | panel_node["panel"].as<std::string>()); |
455 | |||
456 | panel_per_room[panel_node["room"].as<std::string>()].push_back( | ||
457 | panel_node["panel"].as<std::string>()); | ||
452 | } | 458 | } |
453 | 459 | ||
454 | Panel &panel = panels_[panel_id]; | 460 | Panel &panel = panels_[panel_id]; |
455 | panel.panel_door = panel_door_id; | 461 | panel.panel_door = panel_door_id; |
456 | } | 462 | } |
457 | 463 | ||
464 | if (panel_door_it.second["item_name"]) { | ||
465 | panel_doors_[panel_door_id].item_name = | ||
466 | panel_door_it.second["item_name"].as<std::string>(); | ||
467 | } else { | ||
468 | std::vector<std::string> room_strs; | ||
469 | for (const auto &[room_str, panels_str] : panel_per_room) { | ||
470 | room_strs.push_back(fmt::format( | ||
471 | "{} - {}", room_str, hatkirby::implode(panels_str, ", "))); | ||
472 | } | ||
473 | |||
474 | if (num_panels == 1) { | ||
475 | panel_doors_[panel_door_id].item_name = | ||
476 | fmt::format("{} (Panel)", room_strs[0]); | ||
477 | } else { | ||
478 | panel_doors_[panel_door_id].item_name = fmt::format( | ||
479 | "{} (Panels)", hatkirby::implode(room_strs, " and ")); | ||
480 | } | ||
481 | } | ||
482 | |||
458 | if (ids_config["panel_doors"] && | 483 | if (ids_config["panel_doors"] && |
459 | ids_config["panel_doors"][rooms_[room_id].name] && | 484 | ids_config["panel_doors"][rooms_[room_id].name] && |
460 | ids_config["panel_doors"][rooms_[room_id].name] | 485 | ids_config["panel_doors"][rooms_[room_id].name] |
@@ -462,6 +487,9 @@ struct GameData { | |||
462 | panel_doors_[panel_door_id].ap_item_id = | 487 | panel_doors_[panel_door_id].ap_item_id = |
463 | ids_config["panel_doors"][rooms_[room_id].name][panel_door_name] | 488 | ids_config["panel_doors"][rooms_[room_id].name][panel_door_name] |
464 | .as<int>(); | 489 | .as<int>(); |
490 | |||
491 | item_by_ap_id_[panel_doors_[panel_door_id].ap_item_id] = | ||
492 | panel_doors_[panel_door_id].item_name; | ||
465 | } else { | 493 | } else { |
466 | TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}", | 494 | TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}", |
467 | rooms_[room_id].name, panel_door_name)); | 495 | rooms_[room_id].name, panel_door_name)); |
@@ -475,6 +503,9 @@ struct GameData { | |||
475 | ids_config["panel_groups"][panel_group]) { | 503 | ids_config["panel_groups"][panel_group]) { |
476 | panel_doors_[panel_door_id].group_ap_item_id = | 504 | panel_doors_[panel_door_id].group_ap_item_id = |
477 | ids_config["panel_groups"][panel_group].as<int>(); | 505 | ids_config["panel_groups"][panel_group].as<int>(); |
506 | |||
507 | item_by_ap_id_[panel_doors_[panel_door_id].group_ap_item_id] = | ||
508 | panel_group; | ||
478 | } else { | 509 | } else { |
479 | TrackerLog(fmt::format( | 510 | TrackerLog(fmt::format( |
480 | "Missing AP item ID for panel door group {}", panel_group)); | 511 | "Missing AP item ID for panel door group {}", panel_group)); |
@@ -538,6 +569,8 @@ struct GameData { | |||
538 | ids_config["progression"][progressive_item_name]) { | 569 | ids_config["progression"][progressive_item_name]) { |
539 | progressive_item_id = | 570 | progressive_item_id = |
540 | ids_config["progression"][progressive_item_name].as<int>(); | 571 | ids_config["progression"][progressive_item_name].as<int>(); |
572 | |||
573 | item_by_ap_id_[progressive_item_id] = progressive_item_name; | ||
541 | } else { | 574 | } else { |
542 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", | 575 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", |
543 | progressive_item_name)); | 576 | progressive_item_name)); |
@@ -589,6 +622,21 @@ struct GameData { | |||
589 | } | 622 | } |
590 | } | 623 | } |
591 | 624 | ||
625 | // Determine the panel solve indices from the sorted location IDs. | ||
626 | std::sort(panel_location_ids.begin(), panel_location_ids.end()); | ||
627 | |||
628 | std::map<int, int> solve_index_by_location_id; | ||
629 | for (int i = 0; i < panel_location_ids.size(); i++) { | ||
630 | solve_index_by_location_id[panel_location_ids[i]] = i; | ||
631 | } | ||
632 | |||
633 | for (Panel &panel : panels_) { | ||
634 | if (panel.ap_location_id != -1) { | ||
635 | panel.solve_index = solve_index_by_location_id[panel.ap_location_id]; | ||
636 | panel_by_solve_index_[panel.solve_index] = panel.id; | ||
637 | } | ||
638 | } | ||
639 | |||
592 | map_areas_.reserve(areas_config.size()); | 640 | map_areas_.reserve(areas_config.size()); |
593 | 641 | ||
594 | std::map<std::string, int> fold_areas; | 642 | std::map<std::string, int> fold_areas; |
@@ -734,31 +782,6 @@ struct GameData { | |||
734 | } | 782 | } |
735 | } | 783 | } |
736 | 784 | ||
737 | // As a workaround for a generator bug in 0.5.1, we are going to remove the | ||
738 | // panel door requirement on panels that are defined earlier in the file than | ||
739 | // the panel door is. This results in logic that matches the generator, even | ||
740 | // if it is not true to how the game should work. This will be reverted once | ||
741 | // the logic bug is fixed and released. | ||
742 | // See: https://github.com/ArchipelagoMW/Archipelago/pull/4342 | ||
743 | for (Panel& panel : panels_) { | ||
744 | if (panel.panel_door == -1) { | ||
745 | continue; | ||
746 | } | ||
747 | const PanelDoor &panel_door = panel_doors_[panel.panel_door]; | ||
748 | for (int room_id : room_definition_order_) { | ||
749 | if (room_id == panel_door.room) { | ||
750 | // The panel door was defined first (or at the same time as the panel), | ||
751 | // so we're good. | ||
752 | break; | ||
753 | } else if (room_id == panel.room) { | ||
754 | // The panel was defined first, so we have to pretend the panel door is | ||
755 | // not required for this panel. | ||
756 | panel.panel_door = -1; | ||
757 | break; | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | |||
762 | // Report errors. | 785 | // Report errors. |
763 | for (const std::string &area : malconfigured_areas_) { | 786 | for (const std::string &area : malconfigured_areas_) { |
764 | TrackerLog(fmt::format("Area data not found for: {}", area)); | 787 | TrackerLog(fmt::format("Area data not found for: {}", area)); |
@@ -891,7 +914,7 @@ struct GameData { | |||
891 | if (!panel_doors_by_id_.count(full_name)) { | 914 | if (!panel_doors_by_id_.count(full_name)) { |
892 | int panel_door_id = panel_doors_.size(); | 915 | int panel_door_id = panel_doors_.size(); |
893 | panel_doors_by_id_[full_name] = panel_door_id; | 916 | panel_doors_by_id_[full_name] = panel_door_id; |
894 | panel_doors_.push_back({.room = AddOrGetRoom(room)}); | 917 | panel_doors_.push_back({}); |
895 | } | 918 | } |
896 | 919 | ||
897 | return panel_doors_by_id_[full_name]; | 920 | return panel_doors_by_id_[full_name]; |
@@ -964,6 +987,10 @@ const Panel &GD_GetPanel(int panel_id) { | |||
964 | return GetState().panels_.at(panel_id); | 987 | return GetState().panels_.at(panel_id); |
965 | } | 988 | } |
966 | 989 | ||
990 | int GD_GetPanelBySolveIndex(int solve_index) { | ||
991 | return GetState().panel_by_solve_index_.at(solve_index); | ||
992 | } | ||
993 | |||
967 | const std::vector<PaintingExit> &GD_GetPaintings() { | 994 | const std::vector<PaintingExit> &GD_GetPaintings() { |
968 | return GetState().paintings_; | 995 | return GetState().paintings_; |
969 | } | 996 | } |
@@ -1010,3 +1037,38 @@ std::optional<int> GD_GetSubwayItemForPainting(const std::string &painting_id) { | |||
1010 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { | 1037 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { |
1011 | return GetState().subway_item_by_sunwarp_.at(sunwarp); | 1038 | return GetState().subway_item_by_sunwarp_.at(sunwarp); |
1012 | } | 1039 | } |
1040 | |||
1041 | std::string GD_GetItemName(int id) { | ||
1042 | auto it = GetState().item_by_ap_id_.find(id); | ||
1043 | if (it != GetState().item_by_ap_id_.end()) { | ||
1044 | return it->second; | ||
1045 | } else { | ||
1046 | return "Unknown"; | ||
1047 | } | ||
1048 | } | ||
1049 | |||
1050 | LingoColor GetLingoColorForString(const std::string &str) { | ||
1051 | if (str == "black") { | ||
1052 | return LingoColor::kBlack; | ||
1053 | } else if (str == "red") { | ||
1054 | return LingoColor::kRed; | ||
1055 | } else if (str == "blue") { | ||
1056 | return LingoColor::kBlue; | ||
1057 | } else if (str == "yellow") { | ||
1058 | return LingoColor::kYellow; | ||
1059 | } else if (str == "orange") { | ||
1060 | return LingoColor::kOrange; | ||
1061 | } else if (str == "green") { | ||
1062 | return LingoColor::kGreen; | ||
1063 | } else if (str == "gray") { | ||
1064 | return LingoColor::kGray; | ||
1065 | } else if (str == "brown") { | ||
1066 | return LingoColor::kBrown; | ||
1067 | } else if (str == "purple") { | ||
1068 | return LingoColor::kPurple; | ||
1069 | } else { | ||
1070 | TrackerLog(fmt::format("Invalid color: {}", str)); | ||
1071 | |||
1072 | return LingoColor::kNone; | ||
1073 | } | ||
1074 | } | ||