From d7212d755dca7f4fd99cf4b775cd0d372d7bcbb2 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 5 May 2023 16:35:09 -0400 Subject: Refactored away singletons (Except TrackerConfig, for now at least) --- src/ap_state.cpp | 476 ++++++++++++++++++++------------------- src/area_popup.cpp | 7 +- src/game_data.cpp | 604 +++++++++++++++++++++++++++----------------------- src/game_data.h | 59 +---- src/tracker_panel.cpp | 6 +- src/tracker_state.cpp | 48 ++-- src/tracker_state.h | 15 +- 7 files changed, 622 insertions(+), 593 deletions(-) diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 69c9e9f..efbca8c 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp @@ -31,307 +31,333 @@ constexpr int ITEM_HANDLING = 7; // <- all namespace { -APClient* apclient = nullptr; +struct APState { + std::unique_ptr apclient; -bool initialized = false; + bool initialized = false; -TrackerFrame* tracker_frame; + TrackerFrame* tracker_frame = nullptr; -bool client_active = false; -std::mutex client_mutex; + bool client_active = false; + std::mutex client_mutex; -bool connected = false; -bool has_connection_result = false; + bool connected = false; + bool has_connection_result = false; -std::map inventory; -std::set checked_locations; + std::map inventory; + std::set checked_locations; -std::map, int64_t> ap_id_by_location_id; -std::map ap_id_by_item_name; -std::map ap_id_by_color; -std::map progressive_item_by_ap_id; + std::map, int64_t> ap_id_by_location_id; + std::map ap_id_by_item_name; + std::map ap_id_by_color; + std::map progressive_item_by_ap_id; -DoorShuffleMode door_shuffle_mode = kNO_DOORS; -bool color_shuffle = false; -bool painting_shuffle = false; -int mastery_requirement = 21; + DoorShuffleMode door_shuffle_mode = kNO_DOORS; + bool color_shuffle = false; + bool painting_shuffle = false; + int mastery_requirement = 21; -std::map painting_mapping; + std::map painting_mapping; -void RefreshTracker() { - GetTrackerState().CalculateState(); - tracker_frame->UpdateIndicators(); -} - -int64_t GetItemId(const std::string& item_name) { - int64_t ap_id = apclient->get_item_id(item_name); - if (ap_id == APClient::INVALID_NAME_ID) { - std::cout << "Could not find AP item ID for " << item_name << std::endl; - } + void Connect(std::string server, std::string player, std::string password) { + if (!initialized) { + std::thread([this]() { + for (;;) { + { + std::lock_guard client_guard(client_mutex); + if (apclient) { + apclient->poll(); + } + } - return ap_id; -} + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + }).detach(); -void DestroyClient() { - client_active = false; - apclient->reset(); - delete apclient; - apclient = nullptr; -} + initialized = true; + } -} // namespace + tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); -void AP_SetTrackerFrame(TrackerFrame* arg) { tracker_frame = arg; } + { + std::lock_guard client_guard(client_mutex); -void AP_Connect(std::string server, std::string player, std::string password) { - if (!initialized) { - std::thread([]() { - for (;;) { - { - std::lock_guard client_guard(client_mutex); - if (apclient) { - apclient->poll(); - } - } + if (apclient) { + DestroyClient(); + } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::string cert_store = ""; + if (std::filesystem::exists(CERT_STORE_PATH)) { + cert_store = CERT_STORE_PATH; } - }).detach(); - initialized = true; - } + apclient = std::make_unique(ap_get_uuid(""), "Lingo", server, + cert_store); + } - tracker_frame->SetStatusMessage("Connecting to Archipelago server...."); + inventory.clear(); + checked_locations.clear(); + door_shuffle_mode = kNO_DOORS; + color_shuffle = false; + painting_shuffle = false; + painting_mapping.clear(); + mastery_requirement = 21; - { - std::lock_guard client_guard(client_mutex); + connected = false; + has_connection_result = false; - if (apclient) { - DestroyClient(); - } + apclient->set_room_info_handler([this, player, password]() { + inventory.clear(); - std::string cert_store = ""; - if (std::filesystem::exists(CERT_STORE_PATH)) { - cert_store = CERT_STORE_PATH; - } + tracker_frame->SetStatusMessage( + "Connected to Archipelago server. Authenticating..."); - apclient = new APClient(ap_get_uuid(""), "Lingo", server, cert_store); - } + apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"}, + {AP_MAJOR, AP_MINOR, AP_REVISION}); + }); - inventory.clear(); - checked_locations.clear(); - door_shuffle_mode = kNO_DOORS; - color_shuffle = false; - painting_shuffle = false; - painting_mapping.clear(); - mastery_requirement = 21; + apclient->set_location_checked_handler( + [this](const std::list& locations) { + for (const int64_t location_id : locations) { + checked_locations.insert(location_id); + std::cout << "Location: " << location_id << std::endl; + } - connected = false; - has_connection_result = false; + RefreshTracker(); + }); - apclient->set_room_info_handler([player, password]() { - inventory.clear(); + apclient->set_slot_disconnected_handler([this]() { + tracker_frame->SetStatusMessage( + "Disconnected from Archipelago. Attempting to reconnect..."); + }); - tracker_frame->SetStatusMessage( - "Connected to Archipelago server. Authenticating..."); + apclient->set_socket_disconnected_handler([this]() { + tracker_frame->SetStatusMessage( + "Disconnected from Archipelago. Attempting to reconnect..."); + }); - apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"}, - {AP_MAJOR, AP_MINOR, AP_REVISION}); - }); + apclient->set_items_received_handler( + [this](const std::list& items) { + for (const APClient::NetworkItem& item : items) { + inventory[item.item]++; + std::cout << "Item: " << item.item << std::endl; + } - apclient->set_location_checked_handler( - [](const std::list& locations) { - for (const int64_t location_id : locations) { - checked_locations.insert(location_id); - std::cout << "Location: " << location_id << std::endl; - } + RefreshTracker(); + }); - RefreshTracker(); - }); + apclient->set_slot_connected_handler([this]( + const nlohmann::json& slot_data) { + tracker_frame->SetStatusMessage("Connected to Archipelago!"); - apclient->set_slot_disconnected_handler([]() { - tracker_frame->SetStatusMessage( - "Disconnected from Archipelago. Attempting to reconnect..."); - }); + door_shuffle_mode = slot_data["shuffle_doors"].get(); + color_shuffle = slot_data["shuffle_colors"].get(); + painting_shuffle = slot_data["shuffle_paintings"].get(); + mastery_requirement = slot_data["mastery_achievements"].get(); - apclient->set_socket_disconnected_handler([]() { - tracker_frame->SetStatusMessage( - "Disconnected from Archipelago. Attempting to reconnect..."); - }); + if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { + painting_mapping.clear(); - apclient->set_items_received_handler( - [](const std::list& items) { - for (const APClient::NetworkItem& item : items) { - inventory[item.item]++; - std::cout << "Item: " << item.item << std::endl; + for (const auto& mapping_it : + slot_data["painting_entrance_to_exit"].items()) { + painting_mapping[mapping_it.key()] = mapping_it.value(); } + } - RefreshTracker(); - }); + connected = true; + has_connection_result = true; - apclient->set_slot_connected_handler([](const nlohmann::json& slot_data) { - tracker_frame->SetStatusMessage("Connected to Archipelago!"); + RefreshTracker(); + }); + + apclient->set_slot_refused_handler( + [this](const std::list& errors) { + connected = false; + has_connection_result = true; + + tracker_frame->SetStatusMessage("Disconnected from Archipelago."); + + std::vector error_messages; + error_messages.push_back("Could not connect to Archipelago."); + + for (const std::string& error : errors) { + if (error == "InvalidSlot") { + error_messages.push_back("Invalid player name."); + } else if (error == "InvalidGame") { + error_messages.push_back( + "The specified player is not playing Lingo."); + } else if (error == "IncompatibleVersion") { + error_messages.push_back( + "The Archipelago server is not the correct version for this " + "client."); + } else if (error == "InvalidPassword") { + error_messages.push_back("Incorrect password."); + } else if (error == "InvalidItemsHandling") { + error_messages.push_back( + "Invalid item handling flag. This is a bug with the tracker. " + "Please report it to the lingo-ap-tracker GitHub."); + } else { + error_messages.push_back("Unknown error."); + } + } - door_shuffle_mode = slot_data["shuffle_doors"].get(); - color_shuffle = slot_data["shuffle_colors"].get(); - painting_shuffle = slot_data["shuffle_paintings"].get(); - mastery_requirement = slot_data["mastery_achievements"].get(); + std::string full_message = hatkirby::implode(error_messages, " "); - if (painting_shuffle && slot_data.contains("painting_entrance_to_exit")) { - painting_mapping.clear(); + wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); + }); - for (const auto& mapping_it : - slot_data["painting_entrance_to_exit"].items()) { - painting_mapping[mapping_it.key()] = mapping_it.value(); - } - } + client_active = true; - connected = true; - has_connection_result = true; + int timeout = 5000; // 5 seconds + int interval = 100; + int remaining_loops = timeout / interval; + while (!has_connection_result) { + if (interval == 0) { + connected = false; + has_connection_result = true; - RefreshTracker(); - }); + DestroyClient(); - apclient->set_slot_refused_handler([](const std::list& errors) { - connected = false; - has_connection_result = true; - - tracker_frame->SetStatusMessage("Disconnected from Archipelago."); - - std::vector error_messages; - error_messages.push_back("Could not connect to Archipelago."); - - for (const std::string& error : errors) { - if (error == "InvalidSlot") { - error_messages.push_back("Invalid player name."); - } else if (error == "InvalidGame") { - error_messages.push_back("The specified player is not playing Lingo."); - } else if (error == "IncompatibleVersion") { - error_messages.push_back( - "The Archipelago server is not the correct version for this " - "client."); - } else if (error == "InvalidPassword") { - error_messages.push_back("Incorrect password."); - } else if (error == "InvalidItemsHandling") { - error_messages.push_back( - "Invalid item handling flag. This is a bug with the tracker. " - "Please report it to the lingo-ap-tracker GitHub."); - } else { - error_messages.push_back("Unknown error."); + tracker_frame->SetStatusMessage("Disconnected from Archipelago."); + + wxMessageBox("Timeout while connecting to Archipelago server.", + "Connection failed", wxOK | wxICON_ERROR); } - } - std::string full_message = hatkirby::implode(error_messages, " "); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); - wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); - }); + interval--; + } - client_active = true; + if (connected) { + for (const MapArea& map_area : GD_GetMapAreas()) { + for (int section_id = 0; section_id < map_area.locations.size(); + section_id++) { + const Location& location = map_area.locations.at(section_id); + + int64_t ap_id = apclient->get_location_id(location.ap_location_name); + if (ap_id == APClient::INVALID_NAME_ID) { + std::cout << "Could not find AP location ID for " + << location.ap_location_name << std::endl; + } else { + ap_id_by_location_id[{map_area.id, section_id}] = ap_id; + } + } + } - int timeout = 5000; // 5 seconds - int interval = 100; - int remaining_loops = timeout / interval; - while (!has_connection_result) { - if (interval == 0) { - connected = false; - has_connection_result = true; + for (const Door& door : GD_GetDoors()) { + if (!door.skip_item) { + ap_id_by_item_name[door.item_name] = GetItemId(door.item_name); - DestroyClient(); + if (!door.group_name.empty() && + !ap_id_by_item_name.count(door.group_name)) { + ap_id_by_item_name[door.group_name] = GetItemId(door.group_name); + } - tracker_frame->SetStatusMessage("Disconnected from Archipelago."); + for (const ProgressiveRequirement& prog_req : door.progressives) { + ap_id_by_item_name[prog_req.item_name] = + GetItemId(prog_req.item_name); + } + } + } - wxMessageBox("Timeout while connecting to Archipelago server.", - "Connection failed", wxOK | wxICON_ERROR); + ap_id_by_color[LingoColor::kBlack] = GetItemId("Black"); + ap_id_by_color[LingoColor::kRed] = GetItemId("Red"); + ap_id_by_color[LingoColor::kBlue] = GetItemId("Blue"); + ap_id_by_color[LingoColor::kYellow] = GetItemId("Yellow"); + ap_id_by_color[LingoColor::kPurple] = GetItemId("Purple"); + ap_id_by_color[LingoColor::kOrange] = GetItemId("Orange"); + ap_id_by_color[LingoColor::kGreen] = GetItemId("Green"); + ap_id_by_color[LingoColor::kBrown] = GetItemId("Brown"); + ap_id_by_color[LingoColor::kGray] = GetItemId("Gray"); + + RefreshTracker(); + } else { + client_active = false; } + } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + bool HasCheckedGameLocation(int area_id, int section_id) { + std::tuple location_key = {area_id, section_id}; - interval--; + if (ap_id_by_location_id.count(location_key)) { + return checked_locations.count(ap_id_by_location_id.at(location_key)); + } else { + return false; + } } - if (connected) { - for (const MapArea& map_area : GetGameData().GetMapAreas()) { - for (int section_id = 0; section_id < map_area.locations.size(); - section_id++) { - const Location& location = map_area.locations.at(section_id); - - int64_t ap_id = apclient->get_location_id(location.ap_location_name); - if (ap_id == APClient::INVALID_NAME_ID) { - std::cout << "Could not find AP location ID for " - << location.ap_location_name << std::endl; - } else { - ap_id_by_location_id[{map_area.id, section_id}] = ap_id; - } - } + bool HasColorItem(LingoColor color) { + if (ap_id_by_color.count(color)) { + return inventory.count(ap_id_by_color.at(color)); + } else { + return false; } + } - for (const Door& door : GetGameData().GetDoors()) { - if (!door.skip_item) { - ap_id_by_item_name[door.item_name] = GetItemId(door.item_name); + bool HasItem(const std::string& item, int quantity) { + if (ap_id_by_item_name.count(item)) { + int64_t ap_id = ap_id_by_item_name.at(item); + return inventory.count(ap_id) && inventory.at(ap_id) >= quantity; + } else { + return false; + } + } - if (!door.group_name.empty() && - !ap_id_by_item_name.count(door.group_name)) { - ap_id_by_item_name[door.group_name] = GetItemId(door.group_name); - } + void RefreshTracker() { + RecalculateReachability(); + tracker_frame->UpdateIndicators(); + } - for (const ProgressiveRequirement& prog_req : door.progressives) { - ap_id_by_item_name[prog_req.item_name] = - GetItemId(prog_req.item_name); - } - } + int64_t GetItemId(const std::string& item_name) { + int64_t ap_id = apclient->get_item_id(item_name); + if (ap_id == APClient::INVALID_NAME_ID) { + std::cout << "Could not find AP item ID for " << item_name << std::endl; } - ap_id_by_color[LingoColor::kBlack] = GetItemId("Black"); - ap_id_by_color[LingoColor::kRed] = GetItemId("Red"); - ap_id_by_color[LingoColor::kBlue] = GetItemId("Blue"); - ap_id_by_color[LingoColor::kYellow] = GetItemId("Yellow"); - ap_id_by_color[LingoColor::kPurple] = GetItemId("Purple"); - ap_id_by_color[LingoColor::kOrange] = GetItemId("Orange"); - ap_id_by_color[LingoColor::kGreen] = GetItemId("Green"); - ap_id_by_color[LingoColor::kBrown] = GetItemId("Brown"); - ap_id_by_color[LingoColor::kGray] = GetItemId("Gray"); - - RefreshTracker(); - } else { + return ap_id; + } + + void DestroyClient() { client_active = false; + apclient->reset(); + apclient.reset(); } +}; + +APState& GetState() { + static APState* instance = new APState(); + return *instance; } -bool AP_HasCheckedGameLocation(int area_id, int section_id) { - std::tuple location_key = {area_id, section_id}; +} // namespace - if (ap_id_by_location_id.count(location_key)) { - return checked_locations.count(ap_id_by_location_id.at(location_key)); - } else { - return false; - } +void AP_SetTrackerFrame(TrackerFrame* arg) { GetState().tracker_frame = arg; } + +void AP_Connect(std::string server, std::string player, std::string password) { + GetState().Connect(server, player, password); +} + +bool AP_HasCheckedGameLocation(int area_id, int section_id) { + return GetState().HasCheckedGameLocation(area_id, section_id); } bool AP_HasColorItem(LingoColor color) { - if (ap_id_by_color.count(color)) { - return inventory.count(ap_id_by_color.at(color)); - } else { - return false; - } + return GetState().HasColorItem(color); } bool AP_HasItem(const std::string& item, int quantity) { - if (ap_id_by_item_name.count(item)) { - int64_t ap_id = ap_id_by_item_name.at(item); - return inventory.count(ap_id) && inventory.at(ap_id) >= quantity; - } else { - return false; - } + return GetState().HasItem(item, quantity); } -DoorShuffleMode AP_GetDoorShuffleMode() { return door_shuffle_mode; } +DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } -bool AP_IsColorShuffle() { return color_shuffle; } +bool AP_IsColorShuffle() { return GetState().color_shuffle; } -bool AP_IsPaintingShuffle() { return painting_shuffle; } +bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } const std::map AP_GetPaintingMapping() { - return painting_mapping; + return GetState().painting_mapping; } -int AP_GetMasteryRequirement() { return mastery_requirement; } +int AP_GetMasteryRequirement() { return GetState().mastery_requirement; } diff --git a/src/area_popup.cpp b/src/area_popup.cpp index a8ff612..88dffe0 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp @@ -6,7 +6,7 @@ AreaPopup::AreaPopup(wxWindow* parent, int area_id) : wxPanel(parent, wxID_ANY), area_id_(area_id) { - const MapArea& map_area = GetGameData().GetMapArea(area_id); + const MapArea& map_area = GD_GetMapArea(area_id); wxFlexGridSizer* section_sizer = new wxFlexGridSizer(2, 10, 10); @@ -40,12 +40,11 @@ AreaPopup::AreaPopup(wxWindow* parent, int area_id) } void AreaPopup::UpdateIndicators() { - const MapArea& map_area = GetGameData().GetMapArea(area_id_); + const MapArea& map_area = GD_GetMapArea(area_id_); for (int section_id = 0; section_id < map_area.locations.size(); section_id++) { bool checked = AP_HasCheckedGameLocation(area_id_, section_id); - bool reachable = - GetTrackerState().IsLocationReachable(area_id_, section_id); + bool reachable = IsLocationReachable(area_id_, section_id); const wxColour* text_color = reachable ? wxWHITE : wxRED; section_labels_[section_id]->SetForegroundColour(*text_color); diff --git a/src/game_data.cpp b/src/game_data.cpp index e15847e..8c6dd26 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp @@ -5,6 +5,8 @@ #include +namespace { + LingoColor GetColorForString(const std::string &str) { if (str == "black") { return LingoColor::kBlack; @@ -30,377 +32,423 @@ LingoColor GetColorForString(const std::string &str) { } } -GameData::GameData() { - YAML::Node lingo_config = YAML::LoadFile("assets/LL1.yaml"); - YAML::Node areas_config = YAML::LoadFile("assets/areas.yaml"); +struct GameData { + std::vector rooms_; + std::vector doors_; + std::vector panels_; + std::vector map_areas_; - rooms_.reserve(lingo_config.size() * 2); + std::map room_by_id_; + std::map door_by_id_; + std::map panel_by_id_; + std::map area_by_id_; - for (const auto &room_it : lingo_config) { - int room_id = AddOrGetRoom(room_it.first.as()); - Room &room_obj = rooms_[room_id]; + std::map room_by_painting_; - for (const auto &entrance_it : room_it.second["entrances"]) { - int from_room_id = AddOrGetRoom(entrance_it.first.as()); - Room &from_room_obj = rooms_[from_room_id]; + std::vector achievement_panels_; - switch (entrance_it.second.Type()) { - case YAML::NodeType::Scalar: { - // This is just "true". - from_room_obj.exits.push_back({.destination_room = room_id}); - break; - } - case YAML::NodeType::Map: { - Exit exit_obj; - exit_obj.destination_room = room_id; - - if (entrance_it.second["door"]) { - std::string door_room = room_obj.name; - if (entrance_it.second["room"]) { - door_room = entrance_it.second["room"].as(); - } - exit_obj.door = AddOrGetDoor( - door_room, entrance_it.second["door"].as()); - } + GameData() { + YAML::Node lingo_config = YAML::LoadFile("assets/LL1.yaml"); + YAML::Node areas_config = YAML::LoadFile("assets/areas.yaml"); - if (entrance_it.second["painting"]) { - exit_obj.painting = entrance_it.second["painting"].as(); - } + rooms_.reserve(lingo_config.size() * 2); - from_room_obj.exits.push_back(exit_obj); - break; - } - case YAML::NodeType::Sequence: { - for (const auto &option : entrance_it.second) { + for (const auto &room_it : lingo_config) { + int room_id = AddOrGetRoom(room_it.first.as()); + Room &room_obj = rooms_[room_id]; + + for (const auto &entrance_it : room_it.second["entrances"]) { + int from_room_id = AddOrGetRoom(entrance_it.first.as()); + Room &from_room_obj = rooms_[from_room_id]; + + switch (entrance_it.second.Type()) { + case YAML::NodeType::Scalar: { + // This is just "true". + from_room_obj.exits.push_back({.destination_room = room_id}); + break; + } + case YAML::NodeType::Map: { Exit exit_obj; exit_obj.destination_room = room_id; - std::string door_room = room_obj.name; - if (option["room"]) { - door_room = option["room"].as(); + if (entrance_it.second["door"]) { + std::string door_room = room_obj.name; + if (entrance_it.second["room"]) { + door_room = entrance_it.second["room"].as(); + } + exit_obj.door = AddOrGetDoor( + door_room, entrance_it.second["door"].as()); } - exit_obj.door = - AddOrGetDoor(door_room, option["door"].as()); - if (option["painting"]) { - exit_obj.painting = option["painting"].as(); + if (entrance_it.second["painting"]) { + exit_obj.painting = entrance_it.second["painting"].as(); } from_room_obj.exits.push_back(exit_obj); + break; } + case YAML::NodeType::Sequence: { + for (const auto &option : entrance_it.second) { + Exit exit_obj; + exit_obj.destination_room = room_id; + + std::string door_room = room_obj.name; + if (option["room"]) { + door_room = option["room"].as(); + } + exit_obj.door = + AddOrGetDoor(door_room, option["door"].as()); - break; - } - default: { - // This shouldn't happen. - std::cout << "Error reading game data: " << entrance_it << std::endl; - break; - } - } - } + if (option["painting"]) { + exit_obj.painting = option["painting"].as(); + } - if (room_it.second["panels"]) { - for (const auto &panel_it : room_it.second["panels"]) { - int panel_id = - AddOrGetPanel(room_obj.name, panel_it.first.as()); - Panel &panel_obj = panels_[panel_id]; - - if (panel_it.second["colors"]) { - if (panel_it.second["colors"].IsScalar()) { - panel_obj.colors.push_back( - GetColorForString(panel_it.second["colors"].as())); - } else { - for (const auto &color_node : panel_it.second["colors"]) { - panel_obj.colors.push_back( - GetColorForString(color_node.as())); + from_room_obj.exits.push_back(exit_obj); } + + break; + } + default: { + // This shouldn't happen. + std::cout << "Error reading game data: " << entrance_it + << std::endl; + break; } } + } - if (panel_it.second["required_room"]) { - if (panel_it.second["required_room"].IsScalar()) { - panel_obj.required_rooms.push_back(AddOrGetRoom( - panel_it.second["required_room"].as())); - } else { - for (const auto &rr_node : panel_it.second["required_room"]) { - panel_obj.required_rooms.push_back( - AddOrGetRoom(rr_node.as())); + if (room_it.second["panels"]) { + for (const auto &panel_it : room_it.second["panels"]) { + int panel_id = + AddOrGetPanel(room_obj.name, panel_it.first.as()); + Panel &panel_obj = panels_[panel_id]; + + if (panel_it.second["colors"]) { + if (panel_it.second["colors"].IsScalar()) { + panel_obj.colors.push_back(GetColorForString( + panel_it.second["colors"].as())); + } else { + for (const auto &color_node : panel_it.second["colors"]) { + panel_obj.colors.push_back( + GetColorForString(color_node.as())); + } } } - } - if (panel_it.second["required_door"]) { - if (panel_it.second["required_door"].IsMap()) { - std::string rd_room = room_obj.name; - if (panel_it.second["required_door"]["room"]) { - rd_room = - panel_it.second["required_door"]["room"].as(); + if (panel_it.second["required_room"]) { + if (panel_it.second["required_room"].IsScalar()) { + panel_obj.required_rooms.push_back(AddOrGetRoom( + panel_it.second["required_room"].as())); + } else { + for (const auto &rr_node : panel_it.second["required_room"]) { + panel_obj.required_rooms.push_back( + AddOrGetRoom(rr_node.as())); + } } + } - panel_obj.required_doors.push_back(AddOrGetDoor( - rd_room, - panel_it.second["required_door"]["door"].as())); - } else { - for (const auto &rr_node : panel_it.second["required_door"]) { + if (panel_it.second["required_door"]) { + if (panel_it.second["required_door"].IsMap()) { std::string rd_room = room_obj.name; - if (rr_node["room"]) { - rd_room = rr_node["room"].as(); + if (panel_it.second["required_door"]["room"]) { + rd_room = + panel_it.second["required_door"]["room"].as(); } - panel_obj.required_doors.push_back( - AddOrGetDoor(rd_room, rr_node["door"].as())); + panel_obj.required_doors.push_back(AddOrGetDoor( + rd_room, + panel_it.second["required_door"]["door"].as())); + } else { + for (const auto &rr_node : panel_it.second["required_door"]) { + std::string rd_room = room_obj.name; + if (rr_node["room"]) { + rd_room = rr_node["room"].as(); + } + + panel_obj.required_doors.push_back( + AddOrGetDoor(rd_room, rr_node["door"].as())); + } } } - } - if (panel_it.second["check"]) { - panel_obj.check = panel_it.second["check"].as(); - } + if (panel_it.second["check"]) { + panel_obj.check = panel_it.second["check"].as(); + } - if (panel_it.second["achievement"]) { - panel_obj.achievement = panel_it.second["achievement"].as(); + if (panel_it.second["achievement"]) { + panel_obj.achievement = panel_it.second["achievement"].as(); - if (panel_obj.achievement) { - achievement_panels_.push_back(panel_id); + if (panel_obj.achievement) { + achievement_panels_.push_back(panel_id); + } } - } - if (panel_it.second["exclude_reduce"]) { - panel_obj.exclude_reduce = - panel_it.second["exclude_reduce"].as(); + if (panel_it.second["exclude_reduce"]) { + panel_obj.exclude_reduce = + panel_it.second["exclude_reduce"].as(); + } } } - } - if (room_it.second["doors"]) { - for (const auto &door_it : room_it.second["doors"]) { - int door_id = - AddOrGetDoor(room_obj.name, door_it.first.as()); - Door &door_obj = doors_[door_id]; - - bool has_external_panels = false; - std::vector panel_names; - - for (const auto &panel_node : door_it.second["panels"]) { - if (panel_node.IsScalar()) { - panel_names.push_back(panel_node.as()); - door_obj.panels.push_back( - AddOrGetPanel(room_obj.name, panel_node.as())); - } else { - has_external_panels = true; - panel_names.push_back(panel_node["panel"].as()); - door_obj.panels.push_back( - AddOrGetPanel(panel_node["room"].as(), - panel_node["panel"].as())); + if (room_it.second["doors"]) { + for (const auto &door_it : room_it.second["doors"]) { + int door_id = + AddOrGetDoor(room_obj.name, door_it.first.as()); + Door &door_obj = doors_[door_id]; + + bool has_external_panels = false; + std::vector panel_names; + + for (const auto &panel_node : door_it.second["panels"]) { + if (panel_node.IsScalar()) { + panel_names.push_back(panel_node.as()); + door_obj.panels.push_back( + AddOrGetPanel(room_obj.name, panel_node.as())); + } else { + has_external_panels = true; + panel_names.push_back(panel_node["panel"].as()); + door_obj.panels.push_back( + AddOrGetPanel(panel_node["room"].as(), + panel_node["panel"].as())); + } } - } - if (door_it.second["skip_location"]) { - door_obj.skip_location = door_it.second["skip_location"].as(); - } + if (door_it.second["skip_location"]) { + door_obj.skip_location = door_it.second["skip_location"].as(); + } - if (door_it.second["skip_item"]) { - door_obj.skip_item = door_it.second["skip_item"].as(); - } + if (door_it.second["skip_item"]) { + door_obj.skip_item = door_it.second["skip_item"].as(); + } - if (door_it.second["event"]) { - door_obj.skip_location = door_it.second["event"].as(); - door_obj.skip_item = door_it.second["event"].as(); - } + if (door_it.second["event"]) { + door_obj.skip_location = door_it.second["event"].as(); + door_obj.skip_item = door_it.second["event"].as(); + } - if (door_it.second["item_name"]) { - door_obj.item_name = door_it.second["item_name"].as(); - } else if (!door_it.second["skip_item"] && !door_it.second["event"]) { - door_obj.item_name = room_obj.name + " - " + door_obj.name; - } + if (door_it.second["item_name"]) { + door_obj.item_name = door_it.second["item_name"].as(); + } else if (!door_it.second["skip_item"] && !door_it.second["event"]) { + door_obj.item_name = room_obj.name + " - " + door_obj.name; + } - if (door_it.second["group"]) { - door_obj.group_name = door_it.second["group"].as(); - } + if (door_it.second["group"]) { + door_obj.group_name = door_it.second["group"].as(); + } + + if (door_it.second["location_name"]) { + door_obj.location_name = + door_it.second["location_name"].as(); + } else if (!door_it.second["skip_location"] && + !door_it.second["event"]) { + if (has_external_panels) { + std::cout + << room_obj.name << " - " << door_obj.name + << " has panels from other rooms but does not have an " + "explicit " + "location name and is not marked skip_location or event" + << std::endl; + } - if (door_it.second["location_name"]) { - door_obj.location_name = - door_it.second["location_name"].as(); - } else if (!door_it.second["skip_location"] && - !door_it.second["event"]) { - if (has_external_panels) { - std::cout - << room_obj.name << " - " << door_obj.name - << " has panels from other rooms but does not have an explicit " - "location name and is not marked skip_location or event" - << std::endl; + door_obj.location_name = + room_obj.name + " - " + hatkirby::implode(panel_names, ", "); } - door_obj.location_name = - room_obj.name + " - " + hatkirby::implode(panel_names, ", "); + if (door_it.second["include_reduce"]) { + door_obj.exclude_reduce = + !door_it.second["include_reduce"].as(); + } } + } - if (door_it.second["include_reduce"]) { - door_obj.exclude_reduce = - !door_it.second["include_reduce"].as(); + if (room_it.second["paintings"]) { + for (const auto &painting : room_it.second["paintings"]) { + std::string painting_id = painting["id"].as(); + room_by_painting_[painting_id] = room_id; + + if (!painting["exit_only"] || !painting["exit_only"].as()) { + PaintingExit painting_exit; + painting_exit.id = painting_id; + + if (painting["required_door"]) { + std::string rd_room = room_obj.name; + if (painting["required_door"]["room"]) { + rd_room = painting["required_door"]["room"].as(); + } + + painting_exit.door = AddOrGetDoor( + rd_room, painting["required_door"]["door"].as()); + } + + room_obj.paintings.push_back(painting_exit); + } } } - } - if (room_it.second["paintings"]) { - for (const auto &painting : room_it.second["paintings"]) { - std::string painting_id = painting["id"].as(); - room_by_painting_[painting_id] = room_id; + if (room_it.second["progression"]) { + for (const auto &progression_it : room_it.second["progression"]) { + std::string progressive_item_name = + progression_it.first.as(); - if (!painting["exit_only"] || !painting["exit_only"].as()) { - PaintingExit painting_exit; - painting_exit.id = painting_id; + int index = 1; + for (const auto &stage : progression_it.second) { + int door_id = -1; - if (painting["required_door"]) { - std::string rd_room = room_obj.name; - if (painting["required_door"]["room"]) { - rd_room = painting["required_door"]["room"].as(); + if (stage.IsScalar()) { + door_id = AddOrGetDoor(room_obj.name, stage.as()); + } else { + door_id = AddOrGetDoor(stage["room"].as(), + stage["door"].as()); } - painting_exit.door = AddOrGetDoor( - rd_room, painting["required_door"]["door"].as()); + doors_[door_id].progressives.push_back( + {.item_name = progressive_item_name, .quantity = index}); + index++; } - - room_obj.paintings.push_back(painting_exit); } } } - if (room_it.second["progression"]) { - for (const auto &progression_it : room_it.second["progression"]) { - std::string progressive_item_name = - progression_it.first.as(); + map_areas_.reserve(areas_config.size()); + + std::map fold_areas; + for (const auto &area_it : areas_config) { + if (area_it.second["map"]) { + int area_id = AddOrGetArea(area_it.first.as()); + MapArea &area_obj = map_areas_[area_id]; + area_obj.map_x = area_it.second["map"][0].as(); + area_obj.map_y = area_it.second["map"][1].as(); + } else if (area_it.second["fold_into"]) { + fold_areas[area_it.first.as()] = + AddOrGetArea(area_it.second["fold_into"].as()); + } + } - int index = 1; - for (const auto &stage : progression_it.second) { - int door_id = -1; + for (const Panel &panel : panels_) { + if (panel.check) { + int room_id = panel.room; + std::string room_name = rooms_[room_id].name; - if (stage.IsScalar()) { - door_id = AddOrGetDoor(room_obj.name, stage.as()); - } else { - door_id = AddOrGetDoor(stage["room"].as(), - stage["door"].as()); - } + std::string area_name = room_name; + if (fold_areas.count(room_name)) { + int fold_area_id = fold_areas[room_name]; + area_name = map_areas_[fold_area_id].name; + } + + int area_id = AddOrGetArea(area_name); + MapArea &map_area = map_areas_[area_id]; + // room field should be the original room ID + map_area.locations.push_back( + {.name = panel.name, + .ap_location_name = room_name + " - " + panel.name, + .room = panel.room, + .panels = {panel.id}}); + } + } + + for (const Door &door : doors_) { + if (!door.skip_location) { + int room_id = door.room; + std::string area_name = rooms_[room_id].name; + std::string section_name; + + size_t divider_pos = door.location_name.find(" - "); + if (divider_pos == std::string::npos) { + section_name = door.location_name; + } else { + area_name = door.location_name.substr(0, divider_pos); + section_name = door.location_name.substr(divider_pos + 3); + } - doors_[door_id].progressives.push_back( - {.item_name = progressive_item_name, .quantity = index}); - index++; + if (fold_areas.count(area_name)) { + int fold_area_id = fold_areas[area_name]; + area_name = map_areas_[fold_area_id].name; } + + int area_id = AddOrGetArea(area_name); + MapArea &map_area = map_areas_[area_id]; + // room field should be the original room ID + map_area.locations.push_back({.name = section_name, + .ap_location_name = door.location_name, + .room = door.room, + .panels = door.panels}); } } } - map_areas_.reserve(areas_config.size()); - - std::map fold_areas; - for (const auto &area_it : areas_config) { - if (area_it.second["map"]) { - int area_id = AddOrGetArea(area_it.first.as()); - MapArea &area_obj = map_areas_[area_id]; - area_obj.map_x = area_it.second["map"][0].as(); - area_obj.map_y = area_it.second["map"][1].as(); - } else if (area_it.second["fold_into"]) { - fold_areas[area_it.first.as()] = - AddOrGetArea(area_it.second["fold_into"].as()); + int AddOrGetRoom(std::string room) { + if (!room_by_id_.count(room)) { + room_by_id_[room] = rooms_.size(); + rooms_.push_back({.name = room}); } - } - for (const Panel &panel : panels_) { - if (panel.check) { - int room_id = panel.room; - std::string room_name = rooms_[room_id].name; + return room_by_id_[room]; + } - std::string area_name = room_name; - if (fold_areas.count(room_name)) { - int fold_area_id = fold_areas[room_name]; - area_name = map_areas_[fold_area_id].name; - } + int AddOrGetDoor(std::string room, std::string door) { + std::string full_name = room + " - " + door; - int area_id = AddOrGetArea(area_name); - MapArea &map_area = map_areas_[area_id]; - // room field should be the original room ID - map_area.locations.push_back( - {.name = panel.name, - .ap_location_name = room_name + " - " + panel.name, - .room = panel.room, - .panels = {panel.id}}); + if (!door_by_id_.count(full_name)) { + door_by_id_[full_name] = doors_.size(); + doors_.push_back({.room = AddOrGetRoom(room), .name = door}); } - } - for (const Door &door : doors_) { - if (!door.skip_location) { - int room_id = door.room; - std::string area_name = rooms_[room_id].name; - std::string section_name; - - size_t divider_pos = door.location_name.find(" - "); - if (divider_pos == std::string::npos) { - section_name = door.location_name; - } else { - area_name = door.location_name.substr(0, divider_pos); - section_name = door.location_name.substr(divider_pos + 3); - } + return door_by_id_[full_name]; + } - if (fold_areas.count(area_name)) { - int fold_area_id = fold_areas[area_name]; - area_name = map_areas_[fold_area_id].name; - } + int AddOrGetPanel(std::string room, std::string panel) { + std::string full_name = room + " - " + panel; - int area_id = AddOrGetArea(area_name); - MapArea &map_area = map_areas_[area_id]; - // room field should be the original room ID - map_area.locations.push_back({.name = section_name, - .ap_location_name = door.location_name, - .room = door.room, - .panels = door.panels}); + if (!panel_by_id_.count(full_name)) { + int panel_id = panels_.size(); + panel_by_id_[full_name] = panel_id; + panels_.push_back( + {.id = panel_id, .room = AddOrGetRoom(room), .name = panel}); } + + return panel_by_id_[full_name]; } -} -int GameData::AddOrGetRoom(std::string room) { - if (!room_by_id_.count(room)) { - room_by_id_[room] = rooms_.size(); - rooms_.push_back({.name = room}); + int AddOrGetArea(std::string area) { + if (!area_by_id_.count(area)) { + int area_id = map_areas_.size(); + area_by_id_[area] = area_id; + map_areas_.push_back({.id = area_id, .name = area}); + } + + return area_by_id_[area]; } +}; - return room_by_id_[room]; +GameData &GetState() { + static GameData *instance = new GameData(); + return *instance; } -int GameData::AddOrGetDoor(std::string room, std::string door) { - std::string full_name = room + " - " + door; +} // namespace - if (!door_by_id_.count(full_name)) { - door_by_id_[full_name] = doors_.size(); - doors_.push_back({.room = AddOrGetRoom(room), .name = door}); - } +const std::vector &GD_GetMapAreas() { return GetState().map_areas_; } + +const MapArea &GD_GetMapArea(int id) { return GetState().map_areas_.at(id); } - return door_by_id_[full_name]; +int GD_GetRoomByName(const std::string &name) { + return GetState().room_by_id_.at(name); } -int GameData::AddOrGetPanel(std::string room, std::string panel) { - std::string full_name = room + " - " + panel; +const Room &GD_GetRoom(int room_id) { return GetState().rooms_.at(room_id); } - if (!panel_by_id_.count(full_name)) { - int panel_id = panels_.size(); - panel_by_id_[full_name] = panel_id; - panels_.push_back( - {.id = panel_id, .room = AddOrGetRoom(room), .name = panel}); - } +const std::vector &GD_GetDoors() { return GetState().doors_; } - return panel_by_id_[full_name]; -} +const Door &GD_GetDoor(int door_id) { return GetState().doors_.at(door_id); } -int GameData::AddOrGetArea(std::string area) { - if (!area_by_id_.count(area)) { - int area_id = map_areas_.size(); - area_by_id_[area] = area_id; - map_areas_.push_back({.id = area_id, .name = area}); - } +const Panel &GD_GetPanel(int panel_id) { + return GetState().panels_.at(panel_id); +} - return area_by_id_[area]; +int GD_GetRoomForPainting(const std::string &painting_id) { + return GetState().room_by_painting_.at(painting_id); } -const GameData &GetGameData() { - static GameData *instance = new GameData(); - return *instance; +const std::vector &GD_GetAchievementPanels() { + return GetState().achievement_panels_; } diff --git a/src/game_data.h b/src/game_data.h index 0cc7a7b..75eede3 100644 --- a/src/game_data.h +++ b/src/game_data.h @@ -81,55 +81,14 @@ struct MapArea { int map_y; }; -class GameData { - public: - GameData(); - - const std::vector& GetMapAreas() const { return map_areas_; } - - const MapArea& GetMapArea(int id) const { return map_areas_.at(id); } - - int GetRoomByName(const std::string& name) const { - return room_by_id_.at(name); - } - - const Room& GetRoom(int room_id) const { return rooms_.at(room_id); } - - const std::vector& GetDoors() const { return doors_; } - - const Door& GetDoor(int door_id) const { return doors_.at(door_id); } - - const Panel& GetPanel(int panel_id) const { return panels_.at(panel_id); } - - int GetRoomForPainting(const std::string& painting_id) const { - return room_by_painting_.at(painting_id); - } - - const std::vector& GetAchievementPanels() const { - return achievement_panels_; - } - - private: - int AddOrGetRoom(std::string room); - int AddOrGetDoor(std::string room, std::string door); - int AddOrGetPanel(std::string room, std::string panel); - int AddOrGetArea(std::string area); - - std::vector rooms_; - std::vector doors_; - std::vector panels_; - std::vector map_areas_; - - std::map room_by_id_; - std::map door_by_id_; - std::map panel_by_id_; - std::map area_by_id_; - - std::map room_by_painting_; - - std::vector achievement_panels_; -}; - -const GameData& GetGameData(); +const std::vector& GD_GetMapAreas(); +const MapArea& GD_GetMapArea(int id); +int GD_GetRoomByName(const std::string& name); +const Room& GD_GetRoom(int room_id); +const std::vector& GD_GetDoors(); +const Door& GD_GetDoor(int door_id); +const Panel& GD_GetPanel(int panel_id); +int GD_GetRoomForPainting(const std::string& painting_id); +const std::vector& GD_GetAchievementPanels(); #endif /* end of include guard: GAME_DATA_H_9C42AC51 */ diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 0e0569b..736db82 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp @@ -15,7 +15,7 @@ TrackerPanel::TrackerPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) { return; } - for (const MapArea &map_area : GetGameData().GetMapAreas()) { + for (const MapArea &map_area : GD_GetMapAreas()) { AreaIndicator area; area.area_id = map_area.id; @@ -93,13 +93,13 @@ void TrackerPanel::Redraw() { for (AreaIndicator &area : areas_) { const wxBrush *brush_color = wxGREY_BRUSH; - const MapArea &map_area = GetGameData().GetMapArea(area.area_id); + const MapArea &map_area = GD_GetMapArea(area.area_id); bool has_reachable_unchecked = false; bool has_unreachable_unchecked = false; for (int section_id = 0; section_id < map_area.locations.size(); section_id++) { if (!AP_HasCheckedGameLocation(area.area_id, section_id)) { - if (GetTrackerState().IsLocationReachable(area.area_id, section_id)) { + if (IsLocationReachable(area.area_id, section_id)) { has_reachable_unchecked = true; } else { has_unreachable_unchecked = true; diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 858ec3e..37a7da8 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp @@ -1,16 +1,29 @@ #include "tracker_state.h" #include +#include #include +#include #include "ap_state.h" #include "game_data.h" +namespace { + +struct TrackerState { + std::map, bool> reachability; +}; + +TrackerState& GetState() { + static TrackerState* instance = new TrackerState(); + return *instance; +} + bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms); bool IsPanelReachable_Helper(int panel_id, const std::set& reachable_rooms) { - const Panel& panel_obj = GetGameData().GetPanel(panel_id); + const Panel& panel_obj = GD_GetPanel(panel_id); if (!reachable_rooms.count(panel_obj.room)) { return false; @@ -19,7 +32,7 @@ bool IsPanelReachable_Helper(int panel_id, if (panel_obj.name == "THE MASTER") { int achievements_accessible = 0; - for (int achieve_id : GetGameData().GetAchievementPanels()) { + for (int achieve_id : GD_GetAchievementPanels()) { if (IsPanelReachable_Helper(achieve_id, reachable_rooms)) { achievements_accessible++; @@ -56,7 +69,7 @@ bool IsPanelReachable_Helper(int panel_id, } bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms) { - const Door& door_obj = GetGameData().GetDoor(door_id); + const Door& door_obj = GD_GetDoor(door_id); if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { if (!reachable_rooms.count(door_obj.room)) { @@ -89,14 +102,15 @@ bool IsDoorReachable_Helper(int door_id, const std::set& reachable_rooms) { } } -void TrackerState::CalculateState() { - reachability_.clear(); +} // namespace + +void RecalculateReachability() { + GetState().reachability.clear(); std::set reachable_rooms; std::list flood_boundary; - flood_boundary.push_back( - {.destination_room = GetGameData().GetRoomByName("Menu")}); + flood_boundary.push_back({.destination_room = GD_GetRoomByName("Menu")}); bool reachable_changed = true; while (reachable_changed) { @@ -123,8 +137,7 @@ void TrackerState::CalculateState() { reachable_rooms.insert(room_exit.destination_room); reachable_changed = true; - const Room& room_obj = - GetGameData().GetRoom(room_exit.destination_room); + const Room& room_obj = GD_GetRoom(room_exit.destination_room); for (const Exit& out_edge : room_obj.exits) { if (!out_edge.painting || !AP_IsPaintingShuffle()) { new_boundary.push_back(out_edge); @@ -135,7 +148,7 @@ void TrackerState::CalculateState() { for (const PaintingExit& out_edge : room_obj.paintings) { if (AP_GetPaintingMapping().count(out_edge.id)) { Exit painting_exit; - painting_exit.destination_room = GetGameData().GetRoomForPainting( + painting_exit.destination_room = GD_GetRoomForPainting( AP_GetPaintingMapping().at(out_edge.id)); painting_exit.door = out_edge.door; @@ -149,7 +162,7 @@ void TrackerState::CalculateState() { flood_boundary = new_boundary; } - for (const MapArea& map_area : GetGameData().GetMapAreas()) { + for (const MapArea& map_area : GD_GetMapAreas()) { for (int section_id = 0; section_id < map_area.locations.size(); section_id++) { const Location& location_section = map_area.locations.at(section_id); @@ -160,22 +173,17 @@ void TrackerState::CalculateState() { } } - reachability_[{map_area.id, section_id}] = reachable; + GetState().reachability[{map_area.id, section_id}] = reachable; } } } -bool TrackerState::IsLocationReachable(int area_id, int section_id) { +bool IsLocationReachable(int area_id, int section_id) { std::tuple key = {area_id, section_id}; - if (reachability_.count(key)) { - return reachability_.at(key); + if (GetState().reachability.count(key)) { + return GetState().reachability.at(key); } else { return false; } } - -TrackerState& GetTrackerState() { - static TrackerState* instance = new TrackerState(); - return *instance; -} diff --git a/src/tracker_state.h b/src/tracker_state.h index 879e6f2..d8256e2 100644 --- a/src/tracker_state.h +++ b/src/tracker_state.h @@ -1,19 +1,8 @@ #ifndef TRACKER_STATE_H_8639BC90 #define TRACKER_STATE_H_8639BC90 -#include -#include +void RecalculateReachability(); -class TrackerState { - public: - void CalculateState(); - - bool IsLocationReachable(int area_id, int section_id); - - private: - std::map, bool> reachability_; -}; - -TrackerState& GetTrackerState(); +bool IsLocationReachable(int area_id, int section_id); #endif /* end of include guard: TRACKER_STATE_H_8639BC90 */ -- cgit 1.4.1