diff options
Diffstat (limited to 'src/game_data.cpp')
-rw-r--r-- | src/game_data.cpp | 180 |
1 files changed, 119 insertions, 61 deletions
diff --git a/src/game_data.cpp b/src/game_data.cpp index a4a441d..94b9888 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>()); |
@@ -181,12 +166,12 @@ struct GameData { | |||
181 | 166 | ||
182 | if (panel_it.second["colors"]) { | 167 | if (panel_it.second["colors"]) { |
183 | if (panel_it.second["colors"].IsScalar()) { | 168 | if (panel_it.second["colors"].IsScalar()) { |
184 | panels_[panel_id].colors.push_back(GetColorForString( | 169 | panels_[panel_id].colors.push_back(GetLingoColorForString( |
185 | panel_it.second["colors"].as<std::string>())); | 170 | panel_it.second["colors"].as<std::string>())); |
186 | } else { | 171 | } else { |
187 | for (const auto &color_node : panel_it.second["colors"]) { | 172 | for (const auto &color_node : panel_it.second["colors"]) { |
188 | panels_[panel_id].colors.push_back( | 173 | panels_[panel_id].colors.push_back( |
189 | GetColorForString(color_node.as<std::string>())); | 174 | GetLingoColorForString(color_node.as<std::string>())); |
190 | } | 175 | } |
191 | } | 176 | } |
192 | } | 177 | } |
@@ -292,10 +277,11 @@ struct GameData { | |||
292 | ids_config["panels"][rooms_[room_id].name] && | 277 | ids_config["panels"][rooms_[room_id].name] && |
293 | ids_config["panels"][rooms_[room_id].name] | 278 | ids_config["panels"][rooms_[room_id].name] |
294 | [panels_[panel_id].name]) { | 279 | [panels_[panel_id].name]) { |
295 | panels_[panel_id].ap_location_id = | 280 | int location_id = ids_config["panels"][rooms_[room_id].name] |
296 | ids_config["panels"][rooms_[room_id].name] | 281 | [panels_[panel_id].name] |
297 | [panels_[panel_id].name] | 282 | .as<int>(); |
298 | .as<int>(); | 283 | panels_[panel_id].ap_location_id = location_id; |
284 | panel_location_ids.push_back(location_id); | ||
299 | } else { | 285 | } else { |
300 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", | 286 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", |
301 | rooms_[room_id].name, | 287 | rooms_[room_id].name, |
@@ -361,6 +347,9 @@ struct GameData { | |||
361 | ids_config["doors"][rooms_[room_id].name] | 347 | ids_config["doors"][rooms_[room_id].name] |
362 | [doors_[door_id].name]["item"] | 348 | [doors_[door_id].name]["item"] |
363 | .as<int>(); | 349 | .as<int>(); |
350 | |||
351 | item_by_ap_id_[doors_[door_id].ap_item_id] = | ||
352 | doors_[door_id].item_name; | ||
364 | } else { | 353 | } else { |
365 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", | 354 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", |
366 | rooms_[room_id].name, | 355 | rooms_[room_id].name, |
@@ -377,6 +366,9 @@ struct GameData { | |||
377 | doors_[door_id].group_ap_item_id = | 366 | doors_[door_id].group_ap_item_id = |
378 | ids_config["door_groups"][doors_[door_id].group_name] | 367 | ids_config["door_groups"][doors_[door_id].group_name] |
379 | .as<int>(); | 368 | .as<int>(); |
369 | |||
370 | item_by_ap_id_[doors_[door_id].group_ap_item_id] = | ||
371 | doors_[door_id].group_name; | ||
380 | } else { | 372 | } else { |
381 | TrackerLog(fmt::format("Missing AP item ID for door group {}", | 373 | TrackerLog(fmt::format("Missing AP item ID for door group {}", |
382 | doors_[door_id].group_name)); | 374 | doors_[door_id].group_name)); |
@@ -440,21 +432,50 @@ struct GameData { | |||
440 | int panel_door_id = | 432 | int panel_door_id = |
441 | AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name); | 433 | AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name); |
442 | 434 | ||
435 | std::map<std::string, std::vector<std::string>> panel_per_room; | ||
436 | int num_panels = 0; | ||
443 | for (const auto &panel_node : panel_door_it.second["panels"]) { | 437 | for (const auto &panel_node : panel_door_it.second["panels"]) { |
438 | num_panels++; | ||
439 | |||
444 | int panel_id = -1; | 440 | int panel_id = -1; |
445 | 441 | ||
446 | if (panel_node.IsScalar()) { | 442 | if (panel_node.IsScalar()) { |
447 | panel_id = AddOrGetPanel(rooms_[room_id].name, | 443 | panel_id = AddOrGetPanel(rooms_[room_id].name, |
448 | panel_node.as<std::string>()); | 444 | panel_node.as<std::string>()); |
445 | |||
446 | panel_per_room[rooms_[room_id].name].push_back( | ||
447 | panel_node.as<std::string>()); | ||
449 | } else { | 448 | } else { |
450 | panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(), | 449 | panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(), |
451 | panel_node["panel"].as<std::string>()); | 450 | panel_node["panel"].as<std::string>()); |
451 | |||
452 | panel_per_room[panel_node["room"].as<std::string>()].push_back( | ||
453 | panel_node["panel"].as<std::string>()); | ||
452 | } | 454 | } |
453 | 455 | ||
454 | Panel &panel = panels_[panel_id]; | 456 | Panel &panel = panels_[panel_id]; |
455 | panel.panel_door = panel_door_id; | 457 | panel.panel_door = panel_door_id; |
456 | } | 458 | } |
457 | 459 | ||
460 | if (panel_door_it.second["item_name"]) { | ||
461 | panel_doors_[panel_door_id].item_name = | ||
462 | panel_door_it.second["item_name"].as<std::string>(); | ||
463 | } else { | ||
464 | std::vector<std::string> room_strs; | ||
465 | for (const auto &[room_str, panels_str] : panel_per_room) { | ||
466 | room_strs.push_back(fmt::format( | ||
467 | "{} - {}", room_str, hatkirby::implode(panels_str, ", "))); | ||
468 | } | ||
469 | |||
470 | if (num_panels == 1) { | ||
471 | panel_doors_[panel_door_id].item_name = | ||
472 | fmt::format("{} (Panel)", room_strs[0]); | ||
473 | } else { | ||
474 | panel_doors_[panel_door_id].item_name = fmt::format( | ||
475 | "{} (Panels)", hatkirby::implode(room_strs, " and ")); | ||
476 | } | ||
477 | } | ||
478 | |||
458 | if (ids_config["panel_doors"] && | 479 | if (ids_config["panel_doors"] && |
459 | ids_config["panel_doors"][rooms_[room_id].name] && | 480 | ids_config["panel_doors"][rooms_[room_id].name] && |
460 | ids_config["panel_doors"][rooms_[room_id].name] | 481 | ids_config["panel_doors"][rooms_[room_id].name] |
@@ -462,6 +483,9 @@ struct GameData { | |||
462 | panel_doors_[panel_door_id].ap_item_id = | 483 | panel_doors_[panel_door_id].ap_item_id = |
463 | ids_config["panel_doors"][rooms_[room_id].name][panel_door_name] | 484 | ids_config["panel_doors"][rooms_[room_id].name][panel_door_name] |
464 | .as<int>(); | 485 | .as<int>(); |
486 | |||
487 | item_by_ap_id_[panel_doors_[panel_door_id].ap_item_id] = | ||
488 | panel_doors_[panel_door_id].item_name; | ||
465 | } else { | 489 | } else { |
466 | TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}", | 490 | TrackerLog(fmt::format("Missing AP item ID for panel door {} - {}", |
467 | rooms_[room_id].name, panel_door_name)); | 491 | rooms_[room_id].name, panel_door_name)); |
@@ -475,6 +499,9 @@ struct GameData { | |||
475 | ids_config["panel_groups"][panel_group]) { | 499 | ids_config["panel_groups"][panel_group]) { |
476 | panel_doors_[panel_door_id].group_ap_item_id = | 500 | panel_doors_[panel_door_id].group_ap_item_id = |
477 | ids_config["panel_groups"][panel_group].as<int>(); | 501 | ids_config["panel_groups"][panel_group].as<int>(); |
502 | |||
503 | item_by_ap_id_[panel_doors_[panel_door_id].group_ap_item_id] = | ||
504 | panel_group; | ||
478 | } else { | 505 | } else { |
479 | TrackerLog(fmt::format( | 506 | TrackerLog(fmt::format( |
480 | "Missing AP item ID for panel door group {}", panel_group)); | 507 | "Missing AP item ID for panel door group {}", panel_group)); |
@@ -538,6 +565,8 @@ struct GameData { | |||
538 | ids_config["progression"][progressive_item_name]) { | 565 | ids_config["progression"][progressive_item_name]) { |
539 | progressive_item_id = | 566 | progressive_item_id = |
540 | ids_config["progression"][progressive_item_name].as<int>(); | 567 | ids_config["progression"][progressive_item_name].as<int>(); |
568 | |||
569 | item_by_ap_id_[progressive_item_id] = progressive_item_name; | ||
541 | } else { | 570 | } else { |
542 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", | 571 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", |
543 | progressive_item_name)); | 572 | progressive_item_name)); |
@@ -589,6 +618,21 @@ struct GameData { | |||
589 | } | 618 | } |
590 | } | 619 | } |
591 | 620 | ||
621 | // Determine the panel solve indices from the sorted location IDs. | ||
622 | std::sort(panel_location_ids.begin(), panel_location_ids.end()); | ||
623 | |||
624 | std::map<int, int> solve_index_by_location_id; | ||
625 | for (int i = 0; i < panel_location_ids.size(); i++) { | ||
626 | solve_index_by_location_id[panel_location_ids[i]] = i; | ||
627 | } | ||
628 | |||
629 | for (Panel &panel : panels_) { | ||
630 | if (panel.ap_location_id != -1) { | ||
631 | panel.solve_index = solve_index_by_location_id[panel.ap_location_id]; | ||
632 | panel_by_solve_index_[panel.solve_index] = panel.id; | ||
633 | } | ||
634 | } | ||
635 | |||
592 | map_areas_.reserve(areas_config.size()); | 636 | map_areas_.reserve(areas_config.size()); |
593 | 637 | ||
594 | std::map<std::string, int> fold_areas; | 638 | std::map<std::string, int> fold_areas; |
@@ -734,31 +778,6 @@ struct GameData { | |||
734 | } | 778 | } |
735 | } | 779 | } |
736 | 780 | ||
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. | 781 | // Report errors. |
763 | for (const std::string &area : malconfigured_areas_) { | 782 | for (const std::string &area : malconfigured_areas_) { |
764 | TrackerLog(fmt::format("Area data not found for: {}", area)); | 783 | TrackerLog(fmt::format("Area data not found for: {}", area)); |
@@ -891,7 +910,7 @@ struct GameData { | |||
891 | if (!panel_doors_by_id_.count(full_name)) { | 910 | if (!panel_doors_by_id_.count(full_name)) { |
892 | int panel_door_id = panel_doors_.size(); | 911 | int panel_door_id = panel_doors_.size(); |
893 | panel_doors_by_id_[full_name] = panel_door_id; | 912 | panel_doors_by_id_[full_name] = panel_door_id; |
894 | panel_doors_.push_back({.room = AddOrGetRoom(room)}); | 913 | panel_doors_.push_back({}); |
895 | } | 914 | } |
896 | 915 | ||
897 | return panel_doors_by_id_[full_name]; | 916 | return panel_doors_by_id_[full_name]; |
@@ -964,6 +983,10 @@ const Panel &GD_GetPanel(int panel_id) { | |||
964 | return GetState().panels_.at(panel_id); | 983 | return GetState().panels_.at(panel_id); |
965 | } | 984 | } |
966 | 985 | ||
986 | int GD_GetPanelBySolveIndex(int solve_index) { | ||
987 | return GetState().panel_by_solve_index_.at(solve_index); | ||
988 | } | ||
989 | |||
967 | const std::vector<PaintingExit> &GD_GetPaintings() { | 990 | const std::vector<PaintingExit> &GD_GetPaintings() { |
968 | return GetState().paintings_; | 991 | return GetState().paintings_; |
969 | } | 992 | } |
@@ -1010,3 +1033,38 @@ std::optional<int> GD_GetSubwayItemForPainting(const std::string &painting_id) { | |||
1010 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { | 1033 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { |
1011 | return GetState().subway_item_by_sunwarp_.at(sunwarp); | 1034 | return GetState().subway_item_by_sunwarp_.at(sunwarp); |
1012 | } | 1035 | } |
1036 | |||
1037 | std::string GD_GetItemName(int id) { | ||
1038 | auto it = GetState().item_by_ap_id_.find(id); | ||
1039 | if (it != GetState().item_by_ap_id_.end()) { | ||
1040 | return it->second; | ||
1041 | } else { | ||
1042 | return "Unknown"; | ||
1043 | } | ||
1044 | } | ||
1045 | |||
1046 | LingoColor GetLingoColorForString(const std::string &str) { | ||
1047 | if (str == "black") { | ||
1048 | return LingoColor::kBlack; | ||
1049 | } else if (str == "red") { | ||
1050 | return LingoColor::kRed; | ||
1051 | } else if (str == "blue") { | ||
1052 | return LingoColor::kBlue; | ||
1053 | } else if (str == "yellow") { | ||
1054 | return LingoColor::kYellow; | ||
1055 | } else if (str == "orange") { | ||
1056 | return LingoColor::kOrange; | ||
1057 | } else if (str == "green") { | ||
1058 | return LingoColor::kGreen; | ||
1059 | } else if (str == "gray") { | ||
1060 | return LingoColor::kGray; | ||
1061 | } else if (str == "brown") { | ||
1062 | return LingoColor::kBrown; | ||
1063 | } else if (str == "purple") { | ||
1064 | return LingoColor::kPurple; | ||
1065 | } else { | ||
1066 | TrackerLog(fmt::format("Invalid color: {}", str)); | ||
1067 | |||
1068 | return LingoColor::kNone; | ||
1069 | } | ||
1070 | } | ||