From 534e0aae81261990c1160378a085e2aeac9a6b7a Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 16 Jul 2024 15:21:59 -0400 Subject: Fixed undefined behavior in GetRealSubwayDoor --- src/subway_map.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/subway_map.cpp b/src/subway_map.cpp index 044e6fa..9bfedf9 100644 --- a/src/subway_map.cpp +++ b/src/subway_map.cpp @@ -19,7 +19,6 @@ enum class ItemDrawType { kNone, kBox, kOwl }; namespace { std::optional GetRealSubwayDoor(const SubwayItem subway_item) { - std::optional subway_door = subway_item.door; if (AP_IsSunwarpShuffle() && subway_item.sunwarp && subway_item.sunwarp->type != SubwaySunwarpType::kFinal) { int sunwarp_index = subway_item.sunwarp->dots - 1; @@ -29,12 +28,12 @@ std::optional GetRealSubwayDoor(const SubwayItem subway_item) { for (const auto &[start_index, mapping] : AP_GetSunwarpMapping()) { if (start_index == sunwarp_index || mapping.exit_index == sunwarp_index) { - subway_door = GD_GetSunwarpDoors().at(mapping.dots - 1); + return GD_GetSunwarpDoors().at(mapping.dots - 1); } } - - return subway_door; } + + return subway_item.door; } } // namespace -- cgit 1.4.1 From 52657e9eaa7520a841f0eb384472dbde6522e748 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 17 Jul 2024 14:07:35 -0400 Subject: Fix pilgrimage detection allowing sunwarps when shuffled --- src/tracker_state.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index ba615d1..bd63076 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp @@ -180,7 +180,8 @@ class StateCalculator { std::list panel_boundary; std::list painting_boundary; std::list flood_boundary; - flood_boundary.push_back({.destination_room = options_.start}); + flood_boundary.push_back( + {.source_room = -1, .destination_room = options_.start}); bool reachable_changed = true; while (reachable_changed) { @@ -282,12 +283,11 @@ class StateCalculator { if (AP_GetSunwarpMapping().count(index)) { const SunwarpMapping& sm = AP_GetSunwarpMapping().at(index); - Exit sunwarp_exit; - sunwarp_exit.destination_room = - GD_GetRoomForSunwarp(sm.exit_index); - sunwarp_exit.door = GD_GetSunwarpDoors().at(sm.dots - 1); - - new_boundary.push_back(sunwarp_exit); + new_boundary.push_back( + {.source_room = room_exit.destination_room, + .destination_room = GD_GetRoomForSunwarp(sm.exit_index), + .door = GD_GetSunwarpDoors().at(sm.dots - 1), + .type = EntranceType::kSunwarp}); } } } -- cgit 1.4.1 From 3cea704b86a60be2178d1faf5e1be0f927a8c56d Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 17 Jul 2024 14:11:08 -0400 Subject: Don't show unreachable paintings as checked --- src/area_popup.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/area_popup.cpp b/src/area_popup.cpp index 58d8897..ca3b352 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp @@ -126,15 +126,15 @@ void AreaPopup::UpdateIndicators() { if (AP_IsPaintingShuffle()) { 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 = IsPaintingReachable(painting_id); const wxColour* text_color = reachable ? wxWHITE : wxRED; mem_dc.SetTextForeground(*text_color); + bool checked = reachable && AP_IsPaintingChecked(painting.internal_id); + wxBitmap* eye_ptr = checked ? &checked_eye_ : &unchecked_eye_; + mem_dc.DrawBitmap(*eye_ptr, {10, cur_height}); + wxSize item_extent = mem_dc.GetTextExtent(painting.internal_id); // TODO: Replace with friendly name. mem_dc.DrawText(painting.internal_id, {10 + 32 + 10, -- cgit 1.4.1 From deaa92cd5f6f83afaac608f22992163fb3a1a662 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 17 Jul 2024 14:11:31 -0400 Subject: Bump version --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 8f93e18..f89a32d 100644 --- a/src/version.h +++ b/src/version.h @@ -36,6 +36,6 @@ struct Version { } }; -constexpr const Version kTrackerVersion = Version(0, 10, 6); +constexpr const Version kTrackerVersion = Version(0, 10, 7); #endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file -- cgit 1.4.1 From b80e1b888a7203312119e5bfad9e26c2c17d9b9f Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 17 Jul 2024 14:17:33 -0400 Subject: Released v0.10.7 --- CHANGELOG.md | 10 ++++++++++ VERSION | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67e32ac..716e12d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # lingo-ap-tracker Releases +## v0.10.7 - 2024-07-17 + +- Fixed issue with pilgrimage detection when sunwarps are shuffled where it + would expect you to use sunwarps mid-pilgrimage. +- Fixed unreachable paintings sometimes being shown as already checked. + +Download: +[lingo-ap-tracker-v0.10.7-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.10.7-win64.zip)
+Source: [v0.10.7](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.10.7) + ## v0.10.6 - 2024-07-16 - The status bar now shows the name and server for the connected slot. diff --git a/VERSION b/VERSION index 7b63f4b..46832df 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.10.6 \ No newline at end of file +v0.10.7 \ No newline at end of file -- cgit 1.4.1 From 8c5b719469bc61e33a451d9b3aeb66c7b0a6d68e Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 19 Jul 2024 03:51:23 -0400 Subject: Added savedata analyzer --- CMakeLists.txt | 1 + src/ap_state.cpp | 7 +++++ src/ap_state.h | 2 ++ src/area_popup.cpp | 40 +++++++++++++++++++------ src/game_data.cpp | 9 +++++- src/game_data.h | 3 ++ src/godot_variant.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/godot_variant.h | 28 ++++++++++++++++++ src/tracker_frame.cpp | 35 ++++++++++++++++++++++ src/tracker_frame.h | 2 ++ src/tracker_panel.cpp | 54 +++++++++++++++++++++++++++++++-- src/tracker_panel.h | 19 ++++++++++++ 12 files changed, 269 insertions(+), 13 deletions(-) create mode 100644 src/godot_variant.cpp create mode 100644 src/godot_variant.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f9f1117..e1cb7f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ add_executable(lingo_ap_tracker "src/subway_map.cpp" "src/network_set.cpp" "src/logger.cpp" + "src/godot_variant.cpp" "vendor/whereami/whereami.c" ) set_property(TARGET lingo_ap_tracker PROPERTY CXX_STANDARD 20) diff --git a/src/ap_state.cpp b/src/ap_state.cpp index ebc5fc9..f8d4ee0 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp @@ -52,6 +52,8 @@ struct APState { std::list tracked_data_storage_keys; std::string victory_data_storage_key; + std::string save_name; + std::map inventory; std::set checked_locations; std::map data_storage; @@ -131,6 +133,7 @@ struct APState { cert_store); } + save_name.clear(); inventory.clear(); checked_locations.clear(); data_storage.clear(); @@ -228,6 +231,8 @@ struct APState { fmt::format("Connected to Archipelago! ({}@{})", player, server)); TrackerLog("Connected to Archipelago!"); + save_name = fmt::format("zzAP_{}_{}.save", apclient->get_seed(), + apclient->get_player_number()); data_storage_prefix = fmt::format("Lingo_{}_", apclient->get_player_number()); door_shuffle_mode = slot_data["shuffle_doors"].get(); @@ -509,6 +514,8 @@ void AP_Connect(std::string server, std::string player, std::string password) { GetState().Connect(server, player, password); } +std::string AP_GetSaveName() { return GetState().save_name; } + bool AP_HasCheckedGameLocation(int location_id) { return GetState().HasCheckedGameLocation(location_id); } diff --git a/src/ap_state.h b/src/ap_state.h index 7af7395..f8936e5 100644 --- a/src/ap_state.h +++ b/src/ap_state.h @@ -43,6 +43,8 @@ void AP_SetTrackerFrame(TrackerFrame* tracker_frame); void AP_Connect(std::string server, std::string player, std::string password); +std::string AP_GetSaveName(); + bool AP_HasCheckedGameLocation(int location_id); bool AP_HasCheckedHuntPanel(int location_id); diff --git a/src/area_popup.cpp b/src/area_popup.cpp index ca3b352..b18ba62 100644 --- a/src/area_popup.cpp +++ b/src/area_popup.cpp @@ -2,10 +2,13 @@ #include +#include + #include "ap_state.h" #include "game_data.h" #include "global.h" #include "tracker_config.h" +#include "tracker_panel.h" #include "tracker_state.h" AreaPopup::AreaPopup(wxWindow* parent, int area_id) @@ -43,15 +46,23 @@ void AreaPopup::UpdateIndicators() { mem_dc.SetFont(GetFont()); + TrackerPanel* tracker_panel = dynamic_cast(GetParent()); + std::vector real_locations; for (int section_id = 0; section_id < map_area.locations.size(); section_id++) { const Location& location = map_area.locations.at(section_id); - if (!AP_IsLocationVisible(location.classification) && - !(location.hunt && GetTrackerConfig().show_hunt_panels)) { - continue; + if (tracker_panel->IsPanelsMode()) { + if (!location.panel) { + continue; + } + } else { + if (!AP_IsLocationVisible(location.classification) && + !(location.hunt && GetTrackerConfig().show_hunt_panels)) { + continue; + } } real_locations.push_back(section_id); @@ -65,7 +76,7 @@ void AreaPopup::UpdateIndicators() { } } - if (AP_IsPaintingShuffle()) { + if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { 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. @@ -102,10 +113,21 @@ void AreaPopup::UpdateIndicators() { for (int section_id : real_locations) { const Location& location = map_area.locations.at(section_id); - bool checked = - AP_HasCheckedGameLocation(location.ap_location_id) || - (location.hunt && AP_HasCheckedHuntPanel(location.ap_location_id)) || - (IsLocationWinCondition(location) && AP_HasReachedGoal()); + bool checked = false; + if (IsLocationWinCondition(location)) { + checked = AP_HasReachedGoal(); + } else if (tracker_panel->IsPanelsMode()) { + checked = location.panel && std::any_of( + location.panels.begin(), location.panels.end(), + [tracker_panel](int panel_id) { + const Panel& panel = GD_GetPanel(panel_id); + return tracker_panel->GetSolvedPanels().contains(panel.nodepath); + }); + } else { + checked = + AP_HasCheckedGameLocation(location.ap_location_id) || + (location.hunt && AP_HasCheckedHuntPanel(location.ap_location_id)); + } wxBitmap* eye_ptr = checked ? &checked_eye_ : &unchecked_eye_; @@ -123,7 +145,7 @@ void AreaPopup::UpdateIndicators() { cur_height += 10 + 32; } - if (AP_IsPaintingShuffle()) { + if (AP_IsPaintingShuffle() && !tracker_panel->IsPanelsMode()) { for (int painting_id : map_area.paintings) { const PaintingExit& painting = GD_GetPaintingExit(painting_id); diff --git a/src/game_data.cpp b/src/game_data.cpp index 4c0104f..b8e1386 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp @@ -265,6 +265,11 @@ struct GameData { panel_it.second["location_name"].as(); } + if (panel_it.second["id"]) { + panels_[panel_id].nodepath = + panel_it.second["id"].as(); + } + if (panel_it.second["hunt"]) { panels_[panel_id].hunt = panel_it.second["hunt"].as(); } @@ -564,7 +569,8 @@ struct GameData { .room = panel.room, .panels = {panel.id}, .classification = classification, - .hunt = panel.hunt}); + .hunt = panel.hunt, + .panel = true}); locations_by_name[location_name] = {area_id, map_area.locations.size() - 1}; } @@ -617,6 +623,7 @@ struct GameData { for (const Location &location : map_area.locations) { map_area.classification |= location.classification; map_area.hunt |= location.hunt; + map_area.panel |= location.panel; } } diff --git a/src/game_data.h b/src/game_data.h index 23f7b3a..71bc533 100644 --- a/src/game_data.h +++ b/src/game_data.h @@ -43,6 +43,7 @@ struct Panel { int id; int room; std::string name; + std::string nodepath; std::vector colors; std::vector required_rooms; std::vector required_doors; @@ -113,6 +114,7 @@ struct Location { std::vector panels; int classification = 0; bool hunt = false; + bool panel = false; }; struct MapArea { @@ -124,6 +126,7 @@ struct MapArea { int map_y; int classification = 0; bool hunt = false; + bool panel = false; }; enum class SubwaySunwarpType { diff --git a/src/godot_variant.cpp b/src/godot_variant.cpp new file mode 100644 index 0000000..152408f --- /dev/null +++ b/src/godot_variant.cpp @@ -0,0 +1,82 @@ +// Godot save decoder algorithm by Chris Souvey. + +#include "godot_variant.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +uint16_t ReadUint16(std::basic_istream& stream) { + uint16_t result; + stream.read(reinterpret_cast(&result), 2); + return result; +} + +uint32_t ReadUint32(std::basic_istream& stream) { + uint32_t result; + stream.read(reinterpret_cast(&result), 4); + return result; +} + +GodotVariant ParseVariant(std::basic_istream& stream) { + uint16_t type = ReadUint16(stream); + stream.ignore(2); + + switch (type) { + case 1: { + // bool + bool boolval = (ReadUint32(stream) == 1); + return {boolval}; + } + case 15: { + // nodepath + uint32_t name_length = ReadUint32(stream) & 0x7fffffff; + uint32_t subname_length = ReadUint32(stream) & 0x7fffffff; + uint32_t flags = ReadUint32(stream); + + std::vector result; + for (size_t i = 0; i < name_length + subname_length; i++) { + uint32_t char_length = ReadUint32(stream); + uint32_t padded_length = (char_length % 4 == 0) + ? char_length + : (char_length + 4 - (char_length % 4)); + std::vector next_bytes(padded_length); + stream.read(next_bytes.data(), padded_length); + std::string next_piece; + std::copy(next_bytes.begin(), + std::next(next_bytes.begin(), char_length), + std::back_inserter(next_piece)); + result.push_back(next_piece); + } + + return {result}; + } + case 19: { + // array + uint32_t length = ReadUint32(stream) & 0x7fffffff; + std::vector result; + for (size_t i = 0; i < length; i++) { + result.push_back(ParseVariant(stream)); + } + return {result}; + } + default: { + // eh + } + } +} + +} // namespace + +GodotVariant ParseGodotFile(std::string filename) { + std::ifstream file_stream(filename, std::ios_base::binary); + file_stream.ignore(4); + return ParseVariant(file_stream); +} diff --git a/src/godot_variant.h b/src/godot_variant.h new file mode 100644 index 0000000..620e569 --- /dev/null +++ b/src/godot_variant.h @@ -0,0 +1,28 @@ +#ifndef GODOT_VARIANT_H_ED7F2EB6 +#define GODOT_VARIANT_H_ED7F2EB6 + +#include +#include +#include + +struct GodotVariant { + using value_type = std::variant, std::vector>; + + value_type value; + + GodotVariant(value_type v) : value(v) {} + + bool AsBool() const { return std::get(value); } + + const std::vector& AsNodePath() const { + return std::get>(value); + } + + const std::vector& AsArray() const { + return std::get>(value); + } +}; + +GodotVariant ParseGodotFile(std::string filename); + +#endif /* end of include guard: GODOT_VARIANT_H_ED7F2EB6 */ diff --git a/src/tracker_frame.cpp b/src/tracker_frame.cpp index 80fd137..3b6beda 100644 --- a/src/tracker_frame.cpp +++ b/src/tracker_frame.cpp @@ -2,9 +2,12 @@ #include #include +#include #include +#include #include +#include #include #include @@ -23,6 +26,7 @@ enum TrackerFrameIds { ID_SETTINGS = 3, ID_ZOOM_IN = 4, ID_ZOOM_OUT = 5, + ID_OPEN_SAVE_FILE = 6, }; wxDEFINE_EVENT(STATE_RESET, wxCommandEvent); @@ -38,6 +42,7 @@ TrackerFrame::TrackerFrame() wxMenu *menuFile = new wxMenu(); menuFile->Append(ID_CONNECT, "&Connect"); + menuFile->Append(ID_OPEN_SAVE_FILE, "&Open Save Data\tCtrl-O"); menuFile->Append(ID_SETTINGS, "&Settings"); menuFile->Append(wxID_EXIT); @@ -71,6 +76,7 @@ TrackerFrame::TrackerFrame() Bind(wxEVT_MENU, &TrackerFrame::OnZoomIn, this, ID_ZOOM_IN); Bind(wxEVT_MENU, &TrackerFrame::OnZoomOut, this, ID_ZOOM_OUT); Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &TrackerFrame::OnChangePage, this); + Bind(wxEVT_MENU, &TrackerFrame::OnOpenFile, this, ID_OPEN_SAVE_FILE); Bind(STATE_RESET, &TrackerFrame::OnStateReset, this); Bind(STATE_CHANGED, &TrackerFrame::OnStateChanged, this); Bind(STATUS_CHANGED, &TrackerFrame::OnStatusChanged, this); @@ -204,10 +210,36 @@ void TrackerFrame::OnChangePage(wxBookCtrlEvent &event) { zoom_out_menu_item_->Enable(event.GetSelection() == 1); } +void TrackerFrame::OnOpenFile(wxCommandEvent& event) { + wxFileDialog open_file_dialog( + this, "Open Lingo Save File", + fmt::format("{}\\Godot\\app_userdata\\Lingo\\level1_stable", + wxStandardPaths::Get().GetUserConfigDir().ToStdString()), + AP_GetSaveName(), "Lingo save file (*.save)|*.save", + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (open_file_dialog.ShowModal() == wxID_CANCEL) { + return; + } + + std::string savedata_path = open_file_dialog.GetPath().ToStdString(); + + if (panels_panel_ == nullptr) { + panels_panel_ = new TrackerPanel(notebook_); + notebook_->AddPage(panels_panel_, "Panels"); + } + + notebook_->SetSelection(notebook_->FindPage(panels_panel_)); + panels_panel_->SetSavedataPath(savedata_path); +} + void TrackerFrame::OnStateReset(wxCommandEvent& event) { tracker_panel_->UpdateIndicators(); achievements_pane_->UpdateIndicators(); subway_map_->OnConnect(); + if (panels_panel_ != nullptr) { + notebook_->DeletePage(notebook_->FindPage(panels_panel_)); + panels_panel_ = nullptr; + } Refresh(); } @@ -215,6 +247,9 @@ void TrackerFrame::OnStateChanged(wxCommandEvent &event) { tracker_panel_->UpdateIndicators(); achievements_pane_->UpdateIndicators(); subway_map_->UpdateIndicators(); + if (panels_panel_ != nullptr) { + panels_panel_->UpdateIndicators(); + } Refresh(); } diff --git a/src/tracker_frame.h b/src/tracker_frame.h index f7cb3f2..19bd0b3 100644 --- a/src/tracker_frame.h +++ b/src/tracker_frame.h @@ -35,6 +35,7 @@ class TrackerFrame : public wxFrame { void OnZoomIn(wxCommandEvent &event); void OnZoomOut(wxCommandEvent &event); void OnChangePage(wxBookCtrlEvent &event); + void OnOpenFile(wxCommandEvent &event); void OnStateReset(wxCommandEvent &event); void OnStateChanged(wxCommandEvent &event); @@ -46,6 +47,7 @@ class TrackerFrame : public wxFrame { TrackerPanel *tracker_panel_; AchievementsPane *achievements_pane_; SubwayMap *subway_map_; + TrackerPanel *panels_panel_ = nullptr; wxMenuItem *zoom_in_menu_item_; wxMenuItem *zoom_out_menu_item_; diff --git a/src/tracker_panel.cpp b/src/tracker_panel.cpp index d60c1b6..2e1497b 100644 --- a/src/tracker_panel.cpp +++ b/src/tracker_panel.cpp @@ -1,11 +1,15 @@ #include "tracker_panel.h" +#include #include +#include + #include "ap_state.h" #include "area_popup.h" #include "game_data.h" #include "global.h" +#include "godot_variant.h" #include "tracker_config.h" #include "tracker_state.h" @@ -53,6 +57,35 @@ void TrackerPanel::UpdateIndicators() { Redraw(); } +void TrackerPanel::SetSavedataPath(std::string savedata_path) { + if (!panels_mode_) { + wxButton *refresh_button = new wxButton(this, wxID_ANY, "Refresh", {15, 15}); + refresh_button->Bind(wxEVT_BUTTON, &TrackerPanel::OnRefreshSavedata, this); + } + + savedata_path_ = savedata_path; + panels_mode_ = true; + + RefreshSavedata(); +} + +void TrackerPanel::RefreshSavedata() { + solved_panels_.clear(); + + GodotVariant godot_variant = ParseGodotFile(*savedata_path_); + for (const GodotVariant &panel_node : godot_variant.AsArray()) { + const std::vector &fields = panel_node.AsArray(); + if (fields[1].AsBool()) { + const std::vector &nodepath = fields[0].AsNodePath(); + std::string key = fmt::format("{}/{}", nodepath[3], nodepath[4]); + solved_panels_.insert(key); + } + } + + UpdateIndicators(); + Refresh(); +} + void TrackerPanel::OnPaint(wxPaintEvent &event) { if (GetSize() != rendered_.GetSize()) { Redraw(); @@ -92,6 +125,10 @@ void TrackerPanel::OnMouseMove(wxMouseEvent &event) { event.Skip(); } +void TrackerPanel::OnRefreshSavedata(wxCommandEvent &event) { + RefreshSavedata(); +} + void TrackerPanel::Redraw() { wxSize panel_size = GetSize(); wxSize image_size = map_image_.GetSize(); @@ -142,21 +179,32 @@ void TrackerPanel::Redraw() { for (AreaIndicator &area : areas_) { const MapArea &map_area = GD_GetMapArea(area.area_id); - if (!AP_IsLocationVisible(map_area.classification) && + if (panels_mode_) { + area.active = map_area.panel; + } else if (!AP_IsLocationVisible(map_area.classification) && !(map_area.hunt && GetTrackerConfig().show_hunt_panels) && !(AP_IsPaintingShuffle() && !map_area.paintings.empty())) { area.active = false; - continue; } else { area.active = true; } + if (!area.active) { + continue; + } + bool has_reachable_unchecked = false; bool has_unreachable_unchecked = false; for (const Location §ion : map_area.locations) { bool has_unchecked = false; if (IsLocationWinCondition(section)) { has_unchecked = !AP_HasReachedGoal(); + } else if (panels_mode_) { + has_unchecked = section.panel && std::any_of( + section.panels.begin(), section.panels.end(), [this](int panel_id) { + const Panel &panel = GD_GetPanel(panel_id); + return !solved_panels_.contains(panel.nodepath); + }); } else if (AP_IsLocationVisible(section.classification)) { has_unchecked = !AP_HasCheckedGameLocation(section.ap_location_id); } else if (section.hunt && GetTrackerConfig().show_hunt_panels) { @@ -172,7 +220,7 @@ void TrackerPanel::Redraw() { } } - if (AP_IsPaintingShuffle()) { + if (AP_IsPaintingShuffle() && !panels_mode_) { for (int painting_id : map_area.paintings) { const PaintingExit &painting = GD_GetPaintingExit(painting_id); if (!AP_IsPaintingChecked(painting.internal_id)) { diff --git a/src/tracker_panel.h b/src/tracker_panel.h index 06ec7a0..e1f515d 100644 --- a/src/tracker_panel.h +++ b/src/tracker_panel.h @@ -7,6 +7,10 @@ #include #endif +#include +#include +#include + class AreaPopup; class TrackerPanel : public wxPanel { @@ -15,6 +19,14 @@ class TrackerPanel : public wxPanel { void UpdateIndicators(); + void SetSavedataPath(std::string savedata_path); + + bool IsPanelsMode() const { return panels_mode_; } + + const std::set &GetSolvedPanels() const { + return solved_panels_; + } + private: struct AreaIndicator { int area_id = -1; @@ -28,9 +40,12 @@ class TrackerPanel : public wxPanel { void OnPaint(wxPaintEvent &event); void OnMouseMove(wxMouseEvent &event); + void OnRefreshSavedata(wxCommandEvent &event); void Redraw(); + void RefreshSavedata(); + wxImage map_image_; wxImage player_image_; wxBitmap rendered_; @@ -42,6 +57,10 @@ class TrackerPanel : public wxPanel { double scale_y_ = 0; std::vector areas_; + + bool panels_mode_ = false; + std::optional savedata_path_; + std::set solved_panels_; }; #endif /* end of include guard: TRACKER_PANEL_H_D675A54D */ -- cgit 1.4.1 From 5709482f51d16ee93838deee990091f2c9f21818 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 19 Jul 2024 03:51:41 -0400 Subject: Bump version --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index f89a32d..ed5e97c 100644 --- a/src/version.h +++ b/src/version.h @@ -36,6 +36,6 @@ struct Version { } }; -constexpr const Version kTrackerVersion = Version(0, 10, 7); +constexpr const Version kTrackerVersion = Version(0, 11, 0); #endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file -- cgit 1.4.1 From 6507e05f4029985137ad8deef21142bec90cd65c Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 19 Jul 2024 03:55:14 -0400 Subject: Added monostate return for ParseVariant --- src/godot_variant.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/godot_variant.cpp b/src/godot_variant.cpp index 152408f..1bc906f 100644 --- a/src/godot_variant.cpp +++ b/src/godot_variant.cpp @@ -69,6 +69,7 @@ GodotVariant ParseVariant(std::basic_istream& stream) { } default: { // eh + return {std::monostate{}}; } } } -- cgit 1.4.1 From ab5206255603f6401d9c216ffce26607da16ad33 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Fri, 19 Jul 2024 04:05:18 -0400 Subject: Released v0.11.0 --- CHANGELOG.md | 11 +++++++++++ VERSION | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 716e12d..a9ce76d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # lingo-ap-tracker Releases +## v0.11.0 - 2024-07-19 + +- Added a savedata analyzer. When connected to a world, the user can open up the + Lingo save file associated with the connected world, and a new tab will open + up showing unsolved panels that are accessible, even if the world is not a + panelsanity world. + +Download: +[lingo-ap-tracker-v0.11.0-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.11.0-win64.zip)
+Source: [v0.11.0](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.11.0) + ## v0.10.7 - 2024-07-17 - Fixed issue with pilgrimage detection when sunwarps are shuffled where it diff --git a/VERSION b/VERSION index 46832df..e88c34f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.10.7 \ No newline at end of file +v0.11.0 \ No newline at end of file -- cgit 1.4.1