add required dungeons to links house sign

This commit is contained in:
gymnast86
2026-05-27 10:54:35 -07:00
parent 32e553e38d
commit 3aa7abe73f
12 changed files with 137 additions and 14 deletions
+2
View File
@@ -1560,6 +1560,8 @@ set(DUSK_FILES
src/dusk/randomizer/generator/logic/flatten/flatten.hpp
src/dusk/randomizer/generator/logic/flatten/simplify_algebraic.cpp
src/dusk/randomizer/generator/logic/flatten/simplify_algebraic.hpp
src/dusk/randomizer/generator/logic/hints.cpp
src/dusk/randomizer/generator/logic/hints.hpp
src/dusk/randomizer/generator/logic/item.cpp
src/dusk/randomizer/generator/logic/item.hpp
src/dusk/randomizer/generator/logic/item_pool.cpp
@@ -1270,7 +1270,12 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
const auto& name = overrideNode["Name"].as<std::string>();
// TODO: Handle multiple languages
auto language = randomizer::Text::ENGLISH;
auto text = randomizer::getTextStr(name);
std::string text;
if (world->GetTextDatabase().contains(name)) {
text = world->GetDynamicTextStr(name);
} else {
text = randomizer::getTextStr(name);
}
u8 group = overrideNode["Group"].as<u8>();
u16 messageId = overrideNode["Message Id"].as<u16>();
u32 key = (group << 16) | messageId;
@@ -243,6 +243,10 @@
Group: 0
Message Id: 2041
- Name: Links House Sign
Group: 1
Message Id: 9005
- Name: Charlo Donation Choice Text
Group: 4
Message Id: 6403
@@ -119,6 +119,14 @@ namespace randomizer::logic::dungeon
return this->_required;
}
void Dungeon::AddOutsideDependentLocation(location::Location* location) {
this->_outsideDependentLocations.push_back(location);
}
std::list<location::Location*> Dungeon::GetOutsideDependentLocations() {
return this->_outsideDependentLocations;
}
bool Dungeon::ShouldBeBarren() const
{
return !this->_required && this->_world->Setting("Unrequired Dungeons Are Barren") == "On";
@@ -53,6 +53,8 @@ namespace randomizer::logic::dungeon
location::Location* GetGoalLocation();
void SetRequired(const bool& required);
bool IsRequired() const;
void AddOutsideDependentLocation(location::Location* location);
std::list<location::Location*> GetOutsideDependentLocations();
/**
* @brief Returns whether or not the dungeon should be barren given the current settings and placement of dungeon
@@ -71,6 +73,8 @@ namespace randomizer::logic::dungeon
std::unordered_set<entrance::Entrance*> _startingEntrances;
location::Location* _goalLocation;
location::LocationPool _locations = {};
// Locations which depend on beating this dungeon
std::list<location::Location*> _outsideDependentLocations = {};
bool _required = false;
};
} // namespace randomizer::logic::dungeon
@@ -0,0 +1,35 @@
#include "hints.hpp"
#include "dusk/randomizer/generator/utility/text.hpp"
#include "world.hpp"
namespace randomizer::logic::hints {
// Tell the player which dungeons are required on the sign in front of Link's House
static void GenerateRequiredDungeonsHint(world::WorldPool& worlds) {
static const std::unordered_map<std::string, std::string> dungeonColors = {
{"Forest Temple", "<green>"},
{"Goron Mines", "<red>"},
{"Lakebed Temple", "<blue>"},
{"Arbiters Grounds", "<orange>"},
{"Snowpeak Ruins", "<light blue>"},
{"Temple of Time", "<dark green>"},
{"City in the Sky", "<yellow>"},
{"Palace of Twilight", "<purple>"},
// {"Hyrule Castle", "<silver>"}
};
for (const auto& world : worlds) {
auto& requiredDungeonText = world->AddDynamicTextStr("Links House Sign");
for (const auto& [name, dungeon] : world->GetDungeonTable()) {
if (dungeon->IsRequired()) {
requiredDungeonText += dungeonColors.at(name) + name + "\n";
}
}
}
}
void GenerateAllHints(world::WorldPool& worlds) {
GenerateRequiredDungeonsHint(worlds);
}
}
@@ -0,0 +1,7 @@
#pragma once
namespace randomizer::logic::hints {
void GenerateAllHints(world::WorldPool& worldPool);
}
+51 -7
View File
@@ -12,9 +12,10 @@
#include "../utility/string.hpp"
#include "../utility/yaml.hpp"
#include <iostream>
#include <unordered_set>
#include <filesystem>
#include <iostream>
#include <ranges>
#include <unordered_set>
namespace randomizer::logic::world
{
@@ -705,7 +706,7 @@ namespace randomizer::logic::world
{
this->AssignAreaProperties();
this->AssignGoalLocations();
this->ChooseRequiredDungeons();
this->DetermineDungeonDependentLocations();
this->SetForbiddenItems();
}
@@ -816,9 +817,43 @@ namespace randomizer::logic::world
}
}
void World::ChooseRequiredDungeons()
void World::DetermineDungeonDependentLocations()
{
// STUB
for (const auto& [dungeonName, dungeon] : this->_dungeons)
{
// Hyrule Castle is implicitly required
if (dungeonName == "Hyrule Castle") {
continue;
}
// Disable the dungeon's starting entrances
for (auto& entrance : dungeon->GetStartingEntrances())
{
entrance->SetDisbled(true);
}
// Run an accessibility search to see which locations inherently require accessing this dungeon
auto completeItemPool = item_pool::GetCompleteItemPool(this->_randomizer->GetWorlds());
auto search = search::Search::Accessible(&this->_randomizer->GetWorlds(), completeItemPool);
search.SearchWorlds();
for (auto& location : this->_locationTable | std::ranges::views::values) {
// Don't check locations which are part of this dungeon
if (utility::container::ElementInContainer(dungeon->GetLocations(), location.get())) {
continue;
}
// If the search does not contain this location, then the location is dependent on accessing this dungeon
if (!search._visitedLocations.contains(location.get())) {
dungeon->AddOutsideDependentLocation(location.get());
}
}
// Re-enable the dungeon's entrances
for (auto& entrance : dungeon->GetStartingEntrances())
{
entrance->SetDisbled(false);
}
}
}
void World::DetermineRequiredDungeons()
@@ -826,9 +861,14 @@ namespace randomizer::logic::world
for (const auto& [dungeonName, dungeon] : this->_dungeons)
{
// To determine if a dungeon is required, we're going to disable all of its entrances and then check to see
// that the game is still beatble. If the game is not beatable with the dungeon entrances disabled, then the
// that the game is still beatable. If the game is not beatable with the dungeon entrances disabled, then the
// dungeon is required.
// Hyrule Castle is implicitly required
if (dungeonName == "Hyrule Castle") {
continue;
}
// Disable the dungeon's starting entrances
for (auto& entrance : dungeon->GetStartingEntrances())
{
@@ -838,7 +878,7 @@ namespace randomizer::logic::world
// Check if the game is beatable, set dungeon as required if so. If the dungeon is not required and barren
// unrequired dungeons is on, then set all the locations in the unrequired dungeon as nonprogress.
auto completeItemPool = item_pool::GetCompleteItemPool(this->_randomizer->GetWorlds());
if (!search::GameBeatable(&(this->_randomizer->GetWorlds()), completeItemPool))
if (!search::GameBeatable(&this->_randomizer->GetWorlds(), completeItemPool))
{
dungeon->SetRequired(true);
}
@@ -848,6 +888,10 @@ namespace randomizer::logic::world
{
location->SetProgression(false);
}
for (auto& location : dungeon->GetOutsideDependentLocations())
{
location->SetProgression(false);
}
}
// Re-enable the dungeon's entrances
+13 -1
View File
@@ -9,6 +9,7 @@
#include "../seedgen/settings.hpp"
#include "../utility/log.hpp"
#include "../utility/text.hpp"
#include <unordered_map>
#include <map>
@@ -99,7 +100,7 @@ namespace randomizer::logic::world
* @brief STUB: Would choose required dungeons ahead of placing any non-vanilla and non-plandomized items. Not really
* required unless we let users choose a specific amount of directly required dungeons
*/
void ChooseRequiredDungeons();
void DetermineDungeonDependentLocations();
/**
* @brief Determines which dungeons are required based on placed items. Sets required dungeons as such in their
@@ -149,6 +150,15 @@ namespace randomizer::logic::world
seedgen::settings::Setting& Setting(const std::string& settingName);
TextDatabase& GetTextDatabase() { return this->_textDatabase; }
const std::string& GetDynamicTextStr(const std::string& name) {
return this->_textDatabase.at(name).at(Text::Type::STANDARD).mText.at(Text::Language::ENGLISH);
}
// Make a new custom text entry for this world specifically
std::string& AddDynamicTextStr(const std::string& name, Text::Type type = Text::STANDARD, Text::Language language = Text::ENGLISH) {
return this->_textDatabase[name][type].mText[language];
}
private:
int _id = -1;
int _entranceIdCounter = 0;
@@ -168,6 +178,8 @@ namespace randomizer::logic::world
item_pool::ItemPool _itemPool = {};
item_pool::ItemPool _startingItemPool = {};
std::unordered_map<entrance::Entrance*, int> _exitTimeFormCache = {};
// Custom text for this world specifically
TextDatabase _textDatabase = {};
// Plandomizer Data
std::unordered_map<location::Location*, item::Item*> _plandomizerLocations = {};
+3 -1
View File
@@ -3,6 +3,7 @@
#include "logic/entrance_shuffle.hpp"
#include "logic/fill.hpp"
#include "logic/flatten/flatten.hpp"
#include "logic/hints.hpp"
#include "logic/plandomizer.hpp"
#include "logic/search.hpp"
#include "logic/spoiler_log.hpp"
@@ -147,7 +148,8 @@ namespace randomizer
// Generate Playthrough
logic::search::GeneratePlaythrough(this);
// TODO: Generate Hints
// Generate Hints
logic::hints::GenerateAllHints(this->_worlds);
// Write Logs
if (this->_config.IsGeneratingSpoilerLog())
@@ -224,7 +224,7 @@ namespace randomizer {
auto type = string_to_type(typeNode.first.as<std::string>());
auto typeData = typeNode.second;
const auto& text = typeData["Text"].as<std::string>();
tb[name][type].mtext[language] = text;
tb[name][type].mText[language] = text;
if (typeData["Gender"]) {
tb[name][type].mGender[language] = string_to_gender(typeData["Gender"].as<std::string>());
}
@@ -264,7 +264,7 @@ namespace randomizer {
if (!tb.contains(name)) {
throw std::runtime_error("Text name \"" + name + "\" is not recognized.");
}
return tb.at(name).at(type).mtext.at(language);
return tb.at(name).at(type).mText.at(language);
}
void applyMessageCodes(std::string& str) {
@@ -49,7 +49,7 @@ namespace randomizer {
PLURALITY_MAX,
};
std::array<std::string, LANGUAGE_MAX> mtext{};
std::array<std::string, LANGUAGE_MAX> mText{};
std::array<Gender, LANGUAGE_MAX> mGender{};
std::array<Plurality, LANGUAGE_MAX> mPlurality{};
};
@@ -66,7 +66,7 @@ namespace randomizer {
Text::Gender string_to_gender(const std::string& str);
Text::Plurality string_to_plurality(const std::string& str);
// Retrieval of Text objects keyed by name and type (standard, pretty, criptic)
// Retrieval of Text objects keyed by name and type (standard, pretty, cryptic)
using TextDatabase = std::unordered_map<std::string, std::array<Text, Text::TYPE_MAX>>;
const TextDatabase& getTextDatabase();