From 67a2efe7be6f4872adca8d944ebf403046472a98 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 6 Jun 2024 13:53:20 -0400 Subject: Proper painting reachability detection --- src/area_popup.cpp | 16 ++++++------ src/game_data.cpp | 34 +++++++++++++++++++------- src/game_data.h | 11 ++++++--- src/tracker_panel.cpp | 7 +++--- src/tracker_state.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++--------- src/tracker_state.h | 2 ++ 6 files changed, 103 insertions(+), 34 deletions(-) diff --git a/src/area_popup.cpp b/src/area_popup.cpp index b5c1ccb..58d8897 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp @@ -66,8 +66,9 @@ void AreaPopup::UpdateIndicators() { } if (AP_IsPaintingShuffle()) { - for (const PaintingExit& painting : map_area.paintings) { - wxSize item_extent = mem_dc.GetTextExtent(painting.id); + for (int painting_id : map_area.paintings) { + const PaintingExit& painting = GD_GetPaintingExit(painting_id); + wxSize item_extent = mem_dc.GetTextExtent(painting.internal_id); // TODO: Replace with a friendly name. int item_height = std::max(32, item_extent.GetHeight()) + 10; acc_height += item_height; @@ -123,18 +124,19 @@ void AreaPopup::UpdateIndicators() { } if (AP_IsPaintingShuffle()) { - for (const PaintingExit& painting : map_area.paintings) { - bool checked = AP_IsPaintingChecked(painting.id); + for (int painting_id : map_area.paintings) { + const PaintingExit& painting = GD_GetPaintingExit(painting_id); + bool checked = AP_IsPaintingChecked(painting.internal_id); wxBitmap* eye_ptr = checked ? &checked_eye_ : &unchecked_eye_; mem_dc.DrawBitmap(*eye_ptr, {10, cur_height}); - bool reachable = painting.door ? IsDoorOpen(*painting.door) : true; + bool reachable = IsPaintingReachable(painting_id); const wxColour* text_color = reachable ? wxWHITE : wxRED; mem_dc.SetTextForeground(*text_color); - wxSize item_extent = mem_dc.GetTextExtent(painting.id); - mem_dc.DrawText(painting.id, + wxSize item_extent = mem_dc.GetTextExtent(painting.internal_id); // TODO: Replace with friendly name. + mem_dc.DrawText(painting.internal_id, {10 + 32 + 10, cur_height + (32 - mem_dc.GetFontMetrics().height) / 2}); diff --git a/src/game_data.cpp b/src/game_data.cpp index 4dd69e2..71b8629 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp @@ -48,11 +48,13 @@ struct GameData { std::vector panels_; std::vector map_areas_; std::vector subway_items_; + std::vector paintings_; std::map room_by_id_; std::map door_by_id_; std::map panel_by_id_; std::map area_by_id_; + std::map painting_by_id_; std::vector door_definition_order_; @@ -419,13 +421,13 @@ struct GameData { 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; + std::string internal_id = painting["id"].as(); if ((!painting["exit_only"] || !painting["exit_only"].as()) && (!painting["disable"] || !painting["disable"].as())) { - PaintingExit painting_exit; - painting_exit.id = painting_id; + int painting_id = AddOrGetPainting(internal_id); + PaintingExit &painting_exit = paintings_[painting_id]; + painting_exit.room = room_id; if (painting["required_door"]) { std::string rd_room = rooms_[room_id].name; @@ -437,7 +439,7 @@ struct GameData { rd_room, painting["required_door"]["door"].as()); } - rooms_[room_id].paintings.push_back(painting_exit); + rooms_[room_id].paintings.push_back(painting_exit.id); } } } @@ -606,8 +608,8 @@ struct GameData { int area_id = AddOrGetArea(area_name); MapArea &map_area = map_areas_[area_id]; - for (const PaintingExit &painting : room.paintings) { - map_area.paintings.push_back(painting); + for (int painting_id : room.paintings) { + map_area.paintings.push_back(painting_id); } } } @@ -738,6 +740,16 @@ struct GameData { return area_by_id_[area]; } + + int AddOrGetPainting(std::string internal_id) { + if (!painting_by_id_.count(internal_id)) { + int painting_id = paintings_.size(); + painting_by_id_[internal_id] = painting_id; + paintings_.push_back({.id = painting_id, .internal_id = internal_id}); + } + + return painting_by_id_[internal_id]; + } }; GameData &GetState() { @@ -773,8 +785,12 @@ const Panel &GD_GetPanel(int panel_id) { return GetState().panels_.at(panel_id); } -int GD_GetRoomForPainting(const std::string &painting_id) { - return GetState().room_by_painting_.at(painting_id); +const PaintingExit &GD_GetPaintingExit(int painting_id) { + return GetState().paintings_.at(painting_id); +} + +int GD_GetPaintingByName(const std::string &name) { + return GetState().painting_by_id_.at(name); } const std::vector &GD_GetAchievementPanels() { diff --git a/src/game_data.h b/src/game_data.h index 68ba5e4..e0942f7 100644 --- a/src/game_data.h +++ b/src/game_data.h @@ -87,14 +87,16 @@ struct Exit { }; struct PaintingExit { - std::string id; + int id; + int room; + std::string internal_id; std::optional door; }; struct Room { std::string name; std::vector exits; - std::vector paintings; + std::vector paintings; std::vector sunwarps; std::vector panels; }; @@ -113,7 +115,7 @@ struct MapArea { int id; std::string name; std::vector locations; - std::vector paintings; + std::vector paintings; int map_x; int map_y; int classification = 0; @@ -152,7 +154,8 @@ const std::vector& GD_GetDoors(); const Door& GD_GetDoor(int door_id); int GD_GetDoorByName(const std::string& name); const Panel& GD_GetPanel(int panel_id); -int GD_GetRoomForPainting(const std::string& painting_id); +const PaintingExit& GD_GetPaintingExit(int painting_id); +int GD_GetPaintingByName(const std::string& name); const std::vector& GD_GetAchievementPanels(); int GD_GetItemIdForColor(LingoColor color); const std::vector& GD_GetSunwarpDoors(); diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index 0385f89..f0810c9 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp @@ -172,9 +172,10 @@ void TrackerPanel::Redraw() { } if (AP_IsPaintingShuffle()) { - for (const PaintingExit &painting : map_area.paintings) { - if (!AP_IsPaintingChecked(painting.id)) { - bool reachable = painting.door ? IsDoorOpen(*painting.door) : true; + for (int painting_id : map_area.paintings) { + const PaintingExit &painting = GD_GetPaintingExit(painting_id); + if (!AP_IsPaintingChecked(painting.internal_id)) { + bool reachable = IsPaintingReachable(painting_id); if (reachable) { has_reachable_unchecked = true; diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 187a4a8..46bdbec 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp @@ -141,6 +141,7 @@ class RequirementCalculator { struct TrackerState { std::map reachability; std::set reachable_doors; + std::set reachable_paintings; std::mutex reachability_mutex; RequirementCalculator requirements; std::map> door_reports; @@ -170,6 +171,7 @@ class StateCalculator { void Calculate() { std::list panel_boundary; + std::list painting_boundary; std::list flood_boundary; flood_boundary.push_back({.destination_room = options_.start}); @@ -177,6 +179,8 @@ class StateCalculator { while (reachable_changed) { reachable_changed = false; + std::list new_boundary; + std::list new_panel_boundary; for (int panel_id : panel_boundary) { if (solveable_panels_.count(panel_id)) { @@ -192,7 +196,33 @@ class StateCalculator { } } - std::list new_boundary; + std::list new_painting_boundary; + for (int painting_id : painting_boundary) { + if (reachable_paintings_.count(painting_id)) { + continue; + } + + Decision painting_reachable = IsPaintingReachable(painting_id); + if (painting_reachable == kYes) { + reachable_paintings_.insert(painting_id); + reachable_changed = true; + + PaintingExit cur_painting = GD_GetPaintingExit(painting_id); + if (AP_GetPaintingMapping().count(cur_painting.internal_id) && + AP_GetCheckedPaintings().count(cur_painting.internal_id)) { + Exit painting_exit; + PaintingExit target_painting = + GD_GetPaintingExit(GD_GetPaintingByName( + AP_GetPaintingMapping().at(cur_painting.internal_id))); + painting_exit.destination_room = target_painting.room; + + new_boundary.push_back(painting_exit); + } + } else if (painting_reachable == kMaybe) { + new_painting_boundary.push_back(painting_id); + } + } + for (const Exit& room_exit : flood_boundary) { if (reachable_rooms_.count(room_exit.destination_room)) { continue; @@ -227,16 +257,8 @@ class StateCalculator { } if (AP_IsPaintingShuffle()) { - for (const PaintingExit& out_edge : room_obj.paintings) { - if (AP_GetPaintingMapping().count(out_edge.id) && - AP_GetCheckedPaintings().count(out_edge.id)) { - Exit painting_exit; - painting_exit.destination_room = GD_GetRoomForPainting( - AP_GetPaintingMapping().at(out_edge.id)); - painting_exit.door = out_edge.door; - - new_boundary.push_back(painting_exit); - } + for (int out_edge : room_obj.paintings) { + new_painting_boundary.push_back(out_edge); } } @@ -285,6 +307,7 @@ class StateCalculator { flood_boundary = new_boundary; panel_boundary = new_panel_boundary; + painting_boundary = new_painting_boundary; } // Now that we know the full reachable area, let's make sure all doors are @@ -302,6 +325,10 @@ class StateCalculator { const std::set& GetSolveablePanels() const { return solveable_panels_; } + const std::set& GetReachablePaintings() const { + return reachable_paintings_; + } + const std::map>& GetDoorReports() const { return door_report_; } @@ -450,6 +477,15 @@ class StateCalculator { return AreRequirementsSatisfied(GetState().requirements.GetPanel(panel_id)); } + Decision IsPaintingReachable(int painting_id) { + const PaintingExit& painting = GD_GetPaintingExit(painting_id); + if (painting.door) { + return IsDoorReachable(*painting.door); + } + + return kYes; + } + Decision IsExitUsable(const Exit& room_exit) { if (room_exit.type == EntranceType::kPilgrimage) { if (options_.pilgrimage || !AP_IsPilgrimageEnabled()) { @@ -533,6 +569,7 @@ class StateCalculator { std::set reachable_rooms_; std::map door_decisions_; std::set solveable_panels_; + std::set reachable_paintings_; std::map> door_report_; }; @@ -573,6 +610,7 @@ void RecalculateReachability() { } } + std::set reachable_paintings = state_calculator.GetReachablePaintings(); std::map> door_reports = state_calculator.GetDoorReports(); @@ -580,6 +618,7 @@ void RecalculateReachability() { std::lock_guard reachability_guard(GetState().reachability_mutex); std::swap(GetState().reachability, new_reachability); std::swap(GetState().reachable_doors, new_reachable_doors); + std::swap(GetState().reachable_paintings, reachable_paintings); std::swap(GetState().door_reports, door_reports); } } @@ -600,6 +639,12 @@ bool IsDoorOpen(int door_id) { return GetState().reachable_doors.count(door_id); } +bool IsPaintingReachable(int painting_id) { + std::lock_guard reachability_guard(GetState().reachability_mutex); + + return GetState().reachable_paintings.count(painting_id); +} + const std::map& GetDoorRequirements(int door_id) { std::lock_guard reachability_guard(GetState().reachability_mutex); diff --git a/src/tracker_state.h b/src/tracker_state.h index 7acb0f2..c7857a0 100644 --- a/src/tracker_state.h +++ b/src/tracker_state.h @@ -12,6 +12,8 @@ bool IsLocationReachable(int location_id); bool IsDoorOpen(int door_id); +bool IsPaintingReachable(int painting_id); + const std::map& GetDoorRequirements(int door_id); #endif /* end of include guard: TRACKER_STATE_H_8639BC90 */ -- cgit 1.4.1