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 | } | ||
