From b74603c883bef1961fc2f1acfc00faf4cb0b25ba Mon Sep 17 00:00:00 2001 From: gymnast86 Date: Tue, 19 May 2026 22:13:06 -0700 Subject: [PATCH] basic backend for tracking --- src/dusk/imgui/ImGuiConsole.cpp | 1 + src/dusk/imgui/ImGuiMenuRandomizer.cpp | 83 +++++++++++++++++++ src/dusk/imgui/ImGuiMenuRandomizer.hpp | 17 +++- .../randomizer/generator/logic/search.cpp | 4 + .../randomizer/generator/logic/search.hpp | 2 + src/dusk/randomizer/generator/randomizer.cpp | 37 ++++++++- src/dusk/randomizer/generator/randomizer.hpp | 1 + .../randomizer/generator/seedgen/config.cpp | 4 +- .../randomizer/generator/seedgen/config.hpp | 6 +- 9 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index f8002321cd..f1c7e1f10e 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -378,6 +378,7 @@ namespace dusk { } m_menuRandomizer.windowRandoStats(); m_menuRandomizer.windowRandoGeneration(); + m_menuRandomizer.windowRandoTracker(); // Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds. if (dusk::getSettings().game.gyroMode.getValue() != GyroMode::Mouse) diff --git a/src/dusk/imgui/ImGuiMenuRandomizer.cpp b/src/dusk/imgui/ImGuiMenuRandomizer.cpp index a73fc4c899..1d9160bc12 100644 --- a/src/dusk/imgui/ImGuiMenuRandomizer.cpp +++ b/src/dusk/imgui/ImGuiMenuRandomizer.cpp @@ -15,6 +15,7 @@ #include #include "dusk/data.hpp" +#include "dusk/randomizer/generator/logic/search.hpp" namespace dusk { @@ -142,6 +143,8 @@ namespace dusk { } ImGui::Checkbox("Show Rando Stats", &m_showRandoStats); + ImGui::Checkbox("Show Rando Tracker", &m_showRandoTracker); + ImGui::EndMenu(); } } @@ -207,4 +210,84 @@ namespace dusk { ImGui::End(); } + + void ImGuiMenuRandomizer::windowRandoTracker() { + if (!m_showRandoTracker) { + return; + } + + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + + if (ImGui::Begin("Rando Tracker", nullptr, windowFlags)) { + auto trackerRando = getTrackerRando(); + ImGui::Text("Here's where the tracker will be"); + + if (ImGui::Button("Update Tracker")) { + auto trackerRando = getTrackerRando(); + + // Generate tracker world if it doesn't exist + auto contextHash = randomizer_GetContext().mHash; + auto trackerHash = trackerRando->GetConfig().GetHash(false); + // If no hash, or seeds switched, try to create tracker world from currently active seed + if (trackerHash.empty() || (trackerHash != contextHash && !contextHash.empty())) { + *trackerRando = randomizer::Randomizer(data::configured_data_path()); + trackerRando->GenerateTrackerWorld(); + auto trackerWorld = trackerRando->GetWorlds()[0].get(); + auto currentItems = trackerWorld->GetStartingItemPool(); + + m_currentSearch = randomizer::logic::search::Search::Accessible(&trackerRando->GetWorlds(), currentItems); + m_currentSearch.SearchWorlds(); + } else { + // Don't try to update inventory when on the title screen + if (!playerIsOnTitleScreen()) { + // TODO: Translate game save inventory into ItemPool that can be used for searching + randomizer::logic::item_pool::ItemPool currentItems = {}; + m_currentSearch = randomizer::logic::search::Search::Accessible(&trackerRando->GetWorlds(), currentItems); + } + + m_currentSearch.SearchWorlds(); + } + } + + if (trackerRando->GetConfig().GetHash(false).empty()) { + ImGui::Text("There is currently no tracker world"); + } else { + ImGui::Text("Tracker world loaded from seed %s", trackerRando->GetConfig().GetHash().c_str()); + + // Show total number of available locations + auto locations = trackerRando->GetWorlds()[0]->GetAllLocations(); + auto numAvailableLocations = m_currentSearch._visitedLocations.size(); + ImGui::Text("Locations Available: %zu / %zu", numAvailableLocations, locations.size()); + + if (ImGui::BeginChild("ScrollRegion", ImVec2(500, 500), true)) + { + // Show all locations. Green for accessible. Red for Unaccessible + for (auto location : locations) { + // Color red + auto color = ImVec4(1.f, 0.f, 0.f, 1.f); + + // If the search found this location, change color to green + if (m_currentSearch._visitedLocations.contains(location)) { + color = ImVec4(0.f, 1.f, 0.f, 1.f); + } + + ImGui::TextColored(color, "%s", location->GetName().c_str()); + + // Show requirements for the location below it (formatting isn't pretty right now) + ImGui::Text(" %s", location->GetComputedRequirement().to_string().c_str()); + } + } + ImGui::EndChild(); + } + } + + ImGui::End(); + } + + randomizer::Randomizer* ImGuiMenuRandomizer::getTrackerRando() { + static randomizer::Randomizer trackerRando{data::configured_data_path()}; + return &trackerRando; + } + } // namespace dusk \ No newline at end of file diff --git a/src/dusk/imgui/ImGuiMenuRandomizer.hpp b/src/dusk/imgui/ImGuiMenuRandomizer.hpp index b9e3b051e8..0b983a5bc6 100644 --- a/src/dusk/imgui/ImGuiMenuRandomizer.hpp +++ b/src/dusk/imgui/ImGuiMenuRandomizer.hpp @@ -1,7 +1,14 @@ - - #ifndef DUSK_IMGUI_MENU_RANDOMIZER_HPP #define DUSK_IMGUI_MENU_RANDOMIZER_HPP +#include "dusk/randomizer/generator/logic/search.hpp" + +namespace randomizer { +class Randomizer; + +namespace logic::search { +class Search; +} +} namespace dusk { class ImGuiMenuRandomizer { @@ -11,10 +18,16 @@ public: void windowRandoStats(); void windowRandoGeneration(); + void windowRandoTracker(); + + randomizer::Randomizer* getTrackerRando(); private: bool m_showRandoStats{false}; bool m_showRandoGeneration{false}; + bool m_showRandoTracker{false}; + + randomizer::logic::search::Search m_currentSearch = randomizer::logic::search::Search(); }; } diff --git a/src/dusk/randomizer/generator/logic/search.cpp b/src/dusk/randomizer/generator/logic/search.cpp index e2c026fa88..6807a0ae07 100644 --- a/src/dusk/randomizer/generator/logic/search.cpp +++ b/src/dusk/randomizer/generator/logic/search.cpp @@ -10,6 +10,10 @@ namespace randomizer::logic::search { + Search::Search(): _searchMode(SearchMode::NO_SEARCH) { + + } + Search::Search(const SearchMode& searchMode, world::WorldPool* worlds, const item_pool::ItemPool& items /* = {} */, diff --git a/src/dusk/randomizer/generator/logic/search.hpp b/src/dusk/randomizer/generator/logic/search.hpp index 66a3c96616..784fbf4395 100644 --- a/src/dusk/randomizer/generator/logic/search.hpp +++ b/src/dusk/randomizer/generator/logic/search.hpp @@ -48,6 +48,7 @@ namespace randomizer::logic::search { enum class SearchMode { + NO_SEARCH, ACCESSIBLE_LOCATIONS, GAME_BEATABLE, ALL_LOCATIONS_REACHABLE, @@ -59,6 +60,7 @@ namespace randomizer::logic::search class Search { public: + Search(); Search(const SearchMode& searchMode, world::WorldPool* worlds, const item_pool::ItemPool& items = {}, diff --git a/src/dusk/randomizer/generator/randomizer.cpp b/src/dusk/randomizer/generator/randomizer.cpp index 3b341d8ba4..b49239b74b 100644 --- a/src/dusk/randomizer/generator/randomizer.cpp +++ b/src/dusk/randomizer/generator/randomizer.cpp @@ -13,10 +13,9 @@ #include -#include "seedgen/seed.hpp" -#include "SDL3/SDL_filesystem.h" -#include "dusk/app_info.hpp" +#include "dusk/data.hpp" #include "dusk/logging.h" +#include "dusk/randomizer/game/randomizer_context.hpp" namespace randomizer { @@ -36,6 +35,38 @@ namespace randomizer return std::nullopt; } + void Randomizer::GenerateTrackerWorld() { + auto contextHash = randomizer_GetContext().mHash; + + if (contextHash.empty()) { + return; + } + + std::filesystem::path seedSettings = dusk::data::configured_data_path() / "randomizer" / "seeds" / + contextHash / (contextHash + " Anti-Spoiler Log.txt"); + + this->_config.LoadFromFile(seedSettings, GetPrefPath()); + this->_config.SetHash(contextHash); + + std::unique_ptr world = std::make_unique(1, this); + world->SetSettings(this->_config.GetSettingsList().front()); + world->Build(); + this->_worlds.emplace_back(std::move(world)); + + auto trackerWorld = this->_worlds.at(0).get(); + trackerWorld->SetNonProgressLocations(); + trackerWorld->AssignAreaProperties(); + trackerWorld->AssignGoalLocations(); + + // Cache exit form times. This *must* run before conducting the flattening search, otherwise + // the flattening search will pollute the exit timeform cache with a bunch of zeros + logic::fill::CacheExitTimeForms(this->_worlds); + + // Set raw requirements for each location + FlattenSearch search = FlattenSearch(trackerWorld); + search.doSearch(); + } + void Randomizer::GenerateWorlds() { utility::time::ScopedTimer<"Seed generation took ", std::chrono::milliseconds> timer; diff --git a/src/dusk/randomizer/generator/randomizer.hpp b/src/dusk/randomizer/generator/randomizer.hpp index 89009e55f4..3f1d75eefc 100644 --- a/src/dusk/randomizer/generator/randomizer.hpp +++ b/src/dusk/randomizer/generator/randomizer.hpp @@ -19,6 +19,7 @@ namespace randomizer */ std::optional Generate(); void GenerateWorlds(); + void GenerateTrackerWorld(); auto& GetConfig() { return this->_config; } auto& GetWorlds() { return this->_worlds; } diff --git a/src/dusk/randomizer/generator/seedgen/config.cpp b/src/dusk/randomizer/generator/seedgen/config.cpp index 85fb3748dc..2818d13627 100644 --- a/src/dusk/randomizer/generator/seedgen/config.cpp +++ b/src/dusk/randomizer/generator/seedgen/config.cpp @@ -320,9 +320,9 @@ namespace randomizer::seedgen::config WritePreferencesToFile(preferencesPath); } - std::string Config::GetHash() + std::string Config::GetHash(bool generateIfEmpty) { - if (this->_hash.empty()) + if (this->_hash.empty() && generateIfEmpty) { this->_hash = seed::GenerateHash(); } diff --git a/src/dusk/randomizer/generator/seedgen/config.hpp b/src/dusk/randomizer/generator/seedgen/config.hpp index a0227da9d5..78f66a1517 100644 --- a/src/dusk/randomizer/generator/seedgen/config.hpp +++ b/src/dusk/randomizer/generator/seedgen/config.hpp @@ -71,11 +71,13 @@ namespace randomizer::seedgen::config // std::string getPermalink(const bool& internal = false) const; /** - * @brief Returns the hash for the config. If the hash is an empty string, then a new one will be generated. + * @brief Returns the hash for the config. + * @param generateIfEmpty Generates a new hash if the current hash is empty * * @return The hash as a string */ - std::string GetHash(); + std::string GetHash(bool generateIfEmpty = true); + void SetHash(const std::string& newHash) { this->_hash = newHash; } private: fspath _plandomizerPath;