diff options
Diffstat (limited to 'src/game_data.cpp')
| -rw-r--r-- | src/game_data.cpp | 238 |
1 files changed, 182 insertions, 56 deletions
| diff --git a/src/game_data.cpp b/src/game_data.cpp index bc4f41b..e75170e 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | #include "game_data.h" | 1 | #include "game_data.h" |
| 2 | 2 | ||
| 3 | #include <fmt/core.h> | ||
| 3 | #include <hkutil/string.h> | 4 | #include <hkutil/string.h> |
| 4 | #include <yaml-cpp/yaml.h> | 5 | #include <yaml-cpp/yaml.h> |
| 5 | 6 | ||
| @@ -31,9 +32,7 @@ LingoColor GetColorForString(const std::string &str) { | |||
| 31 | } else if (str == "purple") { | 32 | } else if (str == "purple") { |
| 32 | return LingoColor::kPurple; | 33 | return LingoColor::kPurple; |
| 33 | } else { | 34 | } else { |
| 34 | std::ostringstream errmsg; | 35 | TrackerLog(fmt::format("Invalid color: {}", str)); |
| 35 | errmsg << "Invalid color: " << str; | ||
| 36 | TrackerLog(errmsg.str()); | ||
| 37 | 36 | ||
| 38 | return LingoColor::kNone; | 37 | return LingoColor::kNone; |
| 39 | } | 38 | } |
| @@ -44,11 +43,14 @@ struct GameData { | |||
| 44 | std::vector<Door> doors_; | 43 | std::vector<Door> doors_; |
| 45 | std::vector<Panel> panels_; | 44 | std::vector<Panel> panels_; |
| 46 | std::vector<MapArea> map_areas_; | 45 | std::vector<MapArea> map_areas_; |
| 46 | std::vector<SubwayItem> subway_items_; | ||
| 47 | std::vector<PaintingExit> paintings_; | ||
| 47 | 48 | ||
| 48 | std::map<std::string, int> room_by_id_; | 49 | std::map<std::string, int> room_by_id_; |
| 49 | std::map<std::string, int> door_by_id_; | 50 | std::map<std::string, int> door_by_id_; |
| 50 | std::map<std::string, int> panel_by_id_; | 51 | std::map<std::string, int> panel_by_id_; |
| 51 | std::map<std::string, int> area_by_id_; | 52 | std::map<std::string, int> area_by_id_; |
| 53 | std::map<std::string, int> painting_by_id_; | ||
| 52 | 54 | ||
| 53 | std::vector<int> door_definition_order_; | 55 | std::vector<int> door_definition_order_; |
| 54 | 56 | ||
| @@ -61,6 +63,9 @@ struct GameData { | |||
| 61 | 63 | ||
| 62 | std::vector<int> sunwarp_doors_; | 64 | std::vector<int> sunwarp_doors_; |
| 63 | 65 | ||
| 66 | std::map<std::string, int> subway_item_by_painting_; | ||
| 67 | std::map<SubwaySunwarp, int> subway_item_by_sunwarp_; | ||
| 68 | |||
| 64 | bool loaded_area_data_ = false; | 69 | bool loaded_area_data_ = false; |
| 65 | std::set<std::string> malconfigured_areas_; | 70 | std::set<std::string> malconfigured_areas_; |
| 66 | 71 | ||
| @@ -79,9 +84,7 @@ struct GameData { | |||
| 79 | ap_id_by_color_[GetColorForString(input_name)] = | 84 | ap_id_by_color_[GetColorForString(input_name)] = |
| 80 | ids_config["special_items"][color_name].as<int>(); | 85 | ids_config["special_items"][color_name].as<int>(); |
| 81 | } else { | 86 | } else { |
| 82 | std::ostringstream errmsg; | 87 | TrackerLog(fmt::format("Missing AP item ID for color {}", color_name)); |
| 83 | errmsg << "Missing AP item ID for color " << color_name; | ||
| 84 | TrackerLog(errmsg.str()); | ||
| 85 | } | 88 | } |
| 86 | }; | 89 | }; |
| 87 | 90 | ||
| @@ -156,8 +159,10 @@ struct GameData { | |||
| 156 | } | 159 | } |
| 157 | default: { | 160 | default: { |
| 158 | // This shouldn't happen. | 161 | // This shouldn't happen. |
| 159 | std::cout << "Error reading game data: " << entrance_it | 162 | std::ostringstream formatted; |
| 160 | << std::endl; | 163 | formatted << entrance_it; |
| 164 | TrackerLog( | ||
| 165 | fmt::format("Error reading game data: {}", formatted.str())); | ||
| 161 | break; | 166 | break; |
| 162 | } | 167 | } |
| 163 | } | 168 | } |
| @@ -282,10 +287,9 @@ struct GameData { | |||
| 282 | [panels_[panel_id].name] | 287 | [panels_[panel_id].name] |
| 283 | .as<int>(); | 288 | .as<int>(); |
| 284 | } else { | 289 | } else { |
| 285 | std::ostringstream errmsg; | 290 | TrackerLog(fmt::format("Missing AP location ID for panel {} - {}", |
| 286 | errmsg << "Missing AP location ID for panel " | 291 | rooms_[room_id].name, |
| 287 | << rooms_[room_id].name << " - " << panels_[panel_id].name; | 292 | panels_[panel_id].name)); |
| 288 | TrackerLog(errmsg.str()); | ||
| 289 | } | 293 | } |
| 290 | } | 294 | } |
| 291 | } | 295 | } |
| @@ -348,10 +352,9 @@ struct GameData { | |||
| 348 | [doors_[door_id].name]["item"] | 352 | [doors_[door_id].name]["item"] |
| 349 | .as<int>(); | 353 | .as<int>(); |
| 350 | } else { | 354 | } else { |
| 351 | std::ostringstream errmsg; | 355 | TrackerLog(fmt::format("Missing AP item ID for door {} - {}", |
| 352 | errmsg << "Missing AP item ID for door " << rooms_[room_id].name | 356 | rooms_[room_id].name, |
| 353 | << " - " << doors_[door_id].name; | 357 | doors_[door_id].name)); |
| 354 | TrackerLog(errmsg.str()); | ||
| 355 | } | 358 | } |
| 356 | } | 359 | } |
| 357 | 360 | ||
| @@ -365,10 +368,8 @@ struct GameData { | |||
| 365 | ids_config["door_groups"][doors_[door_id].group_name] | 368 | ids_config["door_groups"][doors_[door_id].group_name] |
| 366 | .as<int>(); | 369 | .as<int>(); |
| 367 | } else { | 370 | } else { |
| 368 | std::ostringstream errmsg; | 371 | TrackerLog(fmt::format("Missing AP item ID for door group {}", |
| 369 | errmsg << "Missing AP item ID for door group " | 372 | doors_[door_id].group_name)); |
| 370 | << doors_[door_id].group_name; | ||
| 371 | TrackerLog(errmsg.str()); | ||
| 372 | } | 373 | } |
| 373 | } | 374 | } |
| 374 | 375 | ||
| @@ -378,13 +379,11 @@ struct GameData { | |||
| 378 | } else if (!door_it.second["skip_location"] && | 379 | } else if (!door_it.second["skip_location"] && |
| 379 | !door_it.second["event"]) { | 380 | !door_it.second["event"]) { |
| 380 | if (has_external_panels) { | 381 | if (has_external_panels) { |
| 381 | std::ostringstream errmsg; | 382 | TrackerLog(fmt::format( |
| 382 | errmsg | 383 | "{} - {} has panels from other rooms but does not have an " |
| 383 | << rooms_[room_id].name << " - " << doors_[door_id].name | 384 | "explicit location name and is not marked skip_location or " |
| 384 | << " has panels from other rooms but does not have an " | 385 | "event", |
| 385 | "explicit " | 386 | rooms_[room_id].name, doors_[door_id].name)); |
| 386 | "location name and is not marked skip_location or event"; | ||
| 387 | TrackerLog(errmsg.str()); | ||
| 388 | } | 387 | } |
| 389 | 388 | ||
| 390 | doors_[door_id].location_name = | 389 | doors_[door_id].location_name = |
| @@ -404,10 +403,9 @@ struct GameData { | |||
| 404 | [doors_[door_id].name]["location"] | 403 | [doors_[door_id].name]["location"] |
| 405 | .as<int>(); | 404 | .as<int>(); |
| 406 | } else { | 405 | } else { |
| 407 | std::ostringstream errmsg; | 406 | TrackerLog(fmt::format("Missing AP location ID for door {} - {}", |
| 408 | errmsg << "Missing AP location ID for door " | 407 | rooms_[room_id].name, |
| 409 | << rooms_[room_id].name << " - " << doors_[door_id].name; | 408 | doors_[door_id].name)); |
| 410 | TrackerLog(errmsg.str()); | ||
| 411 | } | 409 | } |
| 412 | } | 410 | } |
| 413 | 411 | ||
| @@ -428,12 +426,14 @@ struct GameData { | |||
| 428 | 426 | ||
| 429 | if (room_it.second["paintings"]) { | 427 | if (room_it.second["paintings"]) { |
| 430 | for (const auto &painting : room_it.second["paintings"]) { | 428 | for (const auto &painting : room_it.second["paintings"]) { |
| 431 | std::string painting_id = painting["id"].as<std::string>(); | 429 | std::string internal_id = painting["id"].as<std::string>(); |
| 432 | room_by_painting_[painting_id] = room_id; | 430 | int painting_id = AddOrGetPainting(internal_id); |
| 431 | PaintingExit &painting_exit = paintings_[painting_id]; | ||
| 432 | painting_exit.room = room_id; | ||
| 433 | 433 | ||
| 434 | if (!painting["exit_only"] || !painting["exit_only"].as<bool>()) { | 434 | if ((!painting["exit_only"] || !painting["exit_only"].as<bool>()) && |
| 435 | PaintingExit painting_exit; | 435 | (!painting["disable"] || !painting["disable"].as<bool>())) { |
| 436 | painting_exit.id = painting_id; | 436 | painting_exit.entrance = true; |
| 437 | 437 | ||
| 438 | if (painting["required_door"]) { | 438 | if (painting["required_door"]) { |
| 439 | std::string rd_room = rooms_[room_id].name; | 439 | std::string rd_room = rooms_[room_id].name; |
| @@ -444,9 +444,9 @@ struct GameData { | |||
| 444 | painting_exit.door = AddOrGetDoor( | 444 | painting_exit.door = AddOrGetDoor( |
| 445 | rd_room, painting["required_door"]["door"].as<std::string>()); | 445 | rd_room, painting["required_door"]["door"].as<std::string>()); |
| 446 | } | 446 | } |
| 447 | |||
| 448 | rooms_[room_id].paintings.push_back(painting_exit); | ||
| 449 | } | 447 | } |
| 448 | |||
| 449 | rooms_[room_id].paintings.push_back(painting_exit.id); | ||
| 450 | } | 450 | } |
| 451 | } | 451 | } |
| 452 | 452 | ||
| @@ -473,10 +473,8 @@ struct GameData { | |||
| 473 | progressive_item_id = | 473 | progressive_item_id = |
| 474 | ids_config["progression"][progressive_item_name].as<int>(); | 474 | ids_config["progression"][progressive_item_name].as<int>(); |
| 475 | } else { | 475 | } else { |
| 476 | std::ostringstream errmsg; | 476 | TrackerLog(fmt::format("Missing AP item ID for progressive item {}", |
| 477 | errmsg << "Missing AP item ID for progressive item " | 477 | progressive_item_name)); |
| 478 | << progressive_item_name; | ||
| 479 | TrackerLog(errmsg.str()); | ||
| 480 | } | 478 | } |
| 481 | 479 | ||
| 482 | int index = 1; | 480 | int index = 1; |
| @@ -559,14 +557,13 @@ struct GameData { | |||
| 559 | int area_id = AddOrGetArea(area_name); | 557 | int area_id = AddOrGetArea(area_name); |
| 560 | MapArea &map_area = map_areas_[area_id]; | 558 | MapArea &map_area = map_areas_[area_id]; |
| 561 | // room field should be the original room ID | 559 | // room field should be the original room ID |
| 562 | map_area.locations.push_back( | 560 | map_area.locations.push_back({.name = section_name, |
| 563 | {.name = section_name, | 561 | .ap_location_name = location_name, |
| 564 | .ap_location_name = location_name, | 562 | .ap_location_id = panel.ap_location_id, |
| 565 | .ap_location_id = panel.ap_location_id, | 563 | .room = panel.room, |
| 566 | .room = panel.room, | 564 | .panels = {panel.id}, |
| 567 | .panels = {panel.id}, | 565 | .classification = classification, |
| 568 | .classification = classification, | 566 | .hunt = panel.hunt}); |
| 569 | .hunt = panel.hunt}); | ||
| 570 | locations_by_name[location_name] = {area_id, | 567 | locations_by_name[location_name] = {area_id, |
| 571 | map_area.locations.size() - 1}; | 568 | map_area.locations.size() - 1}; |
| 572 | } | 569 | } |
| @@ -622,11 +619,101 @@ struct GameData { | |||
| 622 | } | 619 | } |
| 623 | } | 620 | } |
| 624 | 621 | ||
| 622 | for (const Room &room : rooms_) { | ||
| 623 | std::string area_name = room.name; | ||
| 624 | if (fold_areas.count(room.name)) { | ||
| 625 | int fold_area_id = fold_areas[room.name]; | ||
| 626 | area_name = map_areas_[fold_area_id].name; | ||
| 627 | } | ||
| 628 | |||
| 629 | if (!room.paintings.empty()) { | ||
| 630 | int area_id = AddOrGetArea(area_name); | ||
| 631 | MapArea &map_area = map_areas_[area_id]; | ||
| 632 | |||
| 633 | for (int painting_id : room.paintings) { | ||
| 634 | const PaintingExit &painting_obj = paintings_.at(painting_id); | ||
| 635 | if (painting_obj.entrance) { | ||
| 636 | map_area.paintings.push_back(painting_id); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 625 | // Report errors. | 642 | // Report errors. |
| 626 | for (const std::string &area : malconfigured_areas_) { | 643 | for (const std::string &area : malconfigured_areas_) { |
| 627 | std::ostringstream errstr; | 644 | TrackerLog(fmt::format("Area data not found for: {}", area)); |
| 628 | errstr << "Area data not found for: " << area; | 645 | } |
| 629 | TrackerLog(errstr.str()); | 646 | |
| 647 | // Read in subway items. | ||
| 648 | YAML::Node subway_config = | ||
| 649 | YAML::LoadFile(GetAbsolutePath("assets/subway.yaml")); | ||
| 650 | for (const auto &subway_it : subway_config) { | ||
| 651 | SubwayItem subway_item; | ||
| 652 | subway_item.id = subway_items_.size(); | ||
| 653 | subway_item.x = subway_it["pos"][0].as<int>(); | ||
| 654 | subway_item.y = subway_it["pos"][1].as<int>(); | ||
| 655 | |||
| 656 | if (subway_it["door"]) { | ||
| 657 | subway_item.door = AddOrGetDoor(subway_it["room"].as<std::string>(), | ||
| 658 | subway_it["door"].as<std::string>()); | ||
| 659 | } | ||
| 660 | |||
| 661 | if (subway_it["paintings"]) { | ||
| 662 | for (const auto &painting_it : subway_it["paintings"]) { | ||
| 663 | std::string painting_id = painting_it.as<std::string>(); | ||
| 664 | |||
| 665 | subway_item.paintings.push_back(painting_id); | ||
| 666 | subway_item_by_painting_[painting_id] = subway_item.id; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | if (subway_it["tags"]) { | ||
| 671 | for (const auto &tag_it : subway_it["tags"]) { | ||
| 672 | subway_item.tags.push_back(tag_it.as<std::string>()); | ||
| 673 | } | ||
| 674 | } | ||
| 675 | |||
| 676 | if (subway_it["sunwarp"]) { | ||
| 677 | SubwaySunwarp sunwarp; | ||
| 678 | sunwarp.dots = subway_it["sunwarp"]["dots"].as<int>(); | ||
| 679 | |||
| 680 | std::string sunwarp_type = | ||
| 681 | subway_it["sunwarp"]["type"].as<std::string>(); | ||
| 682 | if (sunwarp_type == "final") { | ||
| 683 | sunwarp.type = SubwaySunwarpType::kFinal; | ||
| 684 | } else if (sunwarp_type == "exit") { | ||
| 685 | sunwarp.type = SubwaySunwarpType::kExit; | ||
| 686 | } else { | ||
| 687 | sunwarp.type = SubwaySunwarpType::kEnter; | ||
| 688 | } | ||
| 689 | |||
| 690 | subway_item.sunwarp = sunwarp; | ||
| 691 | |||
| 692 | subway_item_by_sunwarp_[sunwarp] = subway_item.id; | ||
| 693 | |||
| 694 | subway_item.door = | ||
| 695 | AddOrGetDoor("Sunwarps", std::to_string(sunwarp.dots) + " Sunwarp"); | ||
| 696 | } | ||
| 697 | |||
| 698 | if (subway_it["special"]) { | ||
| 699 | subway_item.special = subway_it["special"].as<std::string>(); | ||
| 700 | } | ||
| 701 | |||
| 702 | subway_items_.push_back(subway_item); | ||
| 703 | } | ||
| 704 | |||
| 705 | // Find singleton subway tags. | ||
| 706 | std::map<std::string, std::set<int>> subway_tags; | ||
| 707 | for (const SubwayItem &subway_item : subway_items_) { | ||
| 708 | for (const std::string &tag : subway_item.tags) { | ||
| 709 | subway_tags[tag].insert(subway_item.id); | ||
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | for (const auto &[tag, items] : subway_tags) { | ||
| 714 | if (items.size() == 1) { | ||
| 715 | TrackerLog(fmt::format("Singleton subway item tag: {}", tag)); | ||
| 716 | } | ||
| 630 | } | 717 | } |
| 631 | } | 718 | } |
| 632 | 719 | ||
| @@ -643,8 +730,10 @@ struct GameData { | |||
| 643 | std::string full_name = room + " - " + door; | 730 | std::string full_name = room + " - " + door; |
| 644 | 731 | ||
| 645 | if (!door_by_id_.count(full_name)) { | 732 | if (!door_by_id_.count(full_name)) { |
| 733 | int door_id = doors_.size(); | ||
| 646 | door_by_id_[full_name] = doors_.size(); | 734 | door_by_id_[full_name] = doors_.size(); |
| 647 | doors_.push_back({.room = AddOrGetRoom(room), .name = door}); | 735 | doors_.push_back( |
| 736 | {.id = door_id, .room = AddOrGetRoom(room), .name = door}); | ||
| 648 | } | 737 | } |
| 649 | 738 | ||
| 650 | return door_by_id_[full_name]; | 739 | return door_by_id_[full_name]; |
| @@ -676,6 +765,16 @@ struct GameData { | |||
| 676 | 765 | ||
| 677 | return area_by_id_[area]; | 766 | return area_by_id_[area]; |
| 678 | } | 767 | } |
| 768 | |||
| 769 | int AddOrGetPainting(std::string internal_id) { | ||
| 770 | if (!painting_by_id_.count(internal_id)) { | ||
| 771 | int painting_id = paintings_.size(); | ||
| 772 | painting_by_id_[internal_id] = painting_id; | ||
| 773 | paintings_.push_back({.id = painting_id, .internal_id = internal_id}); | ||
| 774 | } | ||
| 775 | |||
| 776 | return painting_by_id_[internal_id]; | ||
| 777 | } | ||
| 679 | }; | 778 | }; |
| 680 | 779 | ||
| 681 | GameData &GetState() { | 780 | GameData &GetState() { |
| @@ -685,6 +784,10 @@ GameData &GetState() { | |||
| 685 | 784 | ||
| 686 | } // namespace | 785 | } // namespace |
| 687 | 786 | ||
| 787 | bool SubwaySunwarp::operator<(const SubwaySunwarp &rhs) const { | ||
| 788 | return std::tie(dots, type) < std::tie(rhs.dots, rhs.type); | ||
| 789 | } | ||
| 790 | |||
| 688 | const std::vector<MapArea> &GD_GetMapAreas() { return GetState().map_areas_; } | 791 | const std::vector<MapArea> &GD_GetMapAreas() { return GetState().map_areas_; } |
| 689 | 792 | ||
| 690 | const MapArea &GD_GetMapArea(int id) { return GetState().map_areas_.at(id); } | 793 | const MapArea &GD_GetMapArea(int id) { return GetState().map_areas_.at(id); } |
| @@ -707,8 +810,12 @@ const Panel &GD_GetPanel(int panel_id) { | |||
| 707 | return GetState().panels_.at(panel_id); | 810 | return GetState().panels_.at(panel_id); |
| 708 | } | 811 | } |
| 709 | 812 | ||
| 710 | int GD_GetRoomForPainting(const std::string &painting_id) { | 813 | const PaintingExit &GD_GetPaintingExit(int painting_id) { |
| 711 | return GetState().room_by_painting_.at(painting_id); | 814 | return GetState().paintings_.at(painting_id); |
| 815 | } | ||
| 816 | |||
| 817 | int GD_GetPaintingByName(const std::string &name) { | ||
| 818 | return GetState().painting_by_id_.at(name); | ||
| 712 | } | 819 | } |
| 713 | 820 | ||
| 714 | const std::vector<int> &GD_GetAchievementPanels() { | 821 | const std::vector<int> &GD_GetAchievementPanels() { |
| @@ -726,3 +833,22 @@ const std::vector<int> &GD_GetSunwarpDoors() { | |||
| 726 | int GD_GetRoomForSunwarp(int index) { | 833 | int GD_GetRoomForSunwarp(int index) { |
| 727 | return GetState().room_by_sunwarp_.at(index); | 834 | return GetState().room_by_sunwarp_.at(index); |
| 728 | } | 835 | } |
| 836 | |||
| 837 | const std::vector<SubwayItem> &GD_GetSubwayItems() { | ||
| 838 | return GetState().subway_items_; | ||
| 839 | } | ||
| 840 | |||
| 841 | const SubwayItem &GD_GetSubwayItem(int id) { | ||
| 842 | return GetState().subway_items_.at(id); | ||
| 843 | } | ||
| 844 | |||
| 845 | std::optional<int> GD_GetSubwayItemForPainting(const std::string &painting_id) { | ||
| 846 | if (GetState().subway_item_by_painting_.count(painting_id)) { | ||
| 847 | return GetState().subway_item_by_painting_.at(painting_id); | ||
| 848 | } | ||
| 849 | return std::nullopt; | ||
| 850 | } | ||
| 851 | |||
| 852 | int GD_GetSubwayItemForSunwarp(const SubwaySunwarp &sunwarp) { | ||
| 853 | return GetState().subway_item_by_sunwarp_.at(sunwarp); | ||
| 854 | } | ||
