mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-02 09:39:48 -04:00
add required dungeons to links house sign
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,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();
|
||||
|
||||
Reference in New Issue
Block a user