mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-16 04:23:53 -04:00
better plando error messaging
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
|
||||
namespace randomizer::logic::entrance
|
||||
{
|
||||
std::unordered_set<Type> NON_ASSUMED_TYPES = {Type::SPAWN, Type::WARP_PORTAL};
|
||||
|
||||
Type TypeFromStr(const std::string& str)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace randomizer::logic::entrance
|
||||
ALL,
|
||||
};
|
||||
|
||||
extern std::unordered_set<Type> NON_ASSUMED_TYPES;
|
||||
static const std::unordered_set NON_ASSUMED_TYPES = {SPAWN, WARP_PORTAL};
|
||||
|
||||
/**
|
||||
* @brief Takes a string representation of a Type and returns the
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
#include "item_pool.hpp"
|
||||
#include "search.hpp"
|
||||
|
||||
#include "../utility/file.hpp"
|
||||
#include "../utility/random.hpp"
|
||||
#include "../utility/yaml.hpp"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
using namespace randomizer::logic::entrance;
|
||||
|
||||
namespace randomizer::logic::entrance_shuffle
|
||||
@@ -19,7 +19,11 @@ namespace randomizer::logic::entrance_shuffle
|
||||
auto targetEntrancePools = CreateTargetPools(entrancePools);
|
||||
|
||||
// Set plando entrances first
|
||||
SetPlandomizedEntrances(world, entrancePools, targetEntrancePools);
|
||||
try {
|
||||
SetPlandomizedEntrances(world, entrancePools, targetEntrancePools);
|
||||
} catch (std::runtime_error& e) {
|
||||
throw std::runtime_error("Plandomizer Error: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
// Then shuffle non-assumed types (currently this is just spawn)
|
||||
ShuffleNonAssumedEntrancesPools(world, entrancePools, targetEntrancePools);
|
||||
@@ -360,20 +364,20 @@ namespace randomizer::logic::entrance_shuffle
|
||||
// Throw error if entrance/target types are not shuffleable
|
||||
if (entranceType == Type::INVALID)
|
||||
{
|
||||
throw std::runtime_error("Plandomizer Error: " + entranceToConnect->GetOriginalName() +
|
||||
throw std::runtime_error(entranceToConnect->GetOriginalName() +
|
||||
" is not an entrance that can be shuffled");
|
||||
}
|
||||
if (plandoTarget->GetType() == Type::INVALID)
|
||||
{
|
||||
throw std::runtime_error("Plandomizer Error: " + plandoTarget->GetOriginalName() +
|
||||
throw std::runtime_error(plandoTarget->GetOriginalName() +
|
||||
" is not an entrance that can be shuffled");
|
||||
}
|
||||
|
||||
// Throw error if entrance type is shuffleable, but the type itself is not randomized currently
|
||||
if (!entrancePools.contains(entranceType))
|
||||
{
|
||||
throw std::runtime_error("Plandomizer Error: " + entranceToConnect->GetOriginalName() + "'s type " +
|
||||
TypeToStr(entranceType) + " is not being shuffled and thus can't be plandomized.");
|
||||
throw std::runtime_error("Entrance type " + TypeToStr(entranceType) + " for " +
|
||||
entranceToConnect->GetOriginalName() + " is not being shuffled and thus can't be plandomized.");
|
||||
}
|
||||
|
||||
// Get the appropriate pools
|
||||
@@ -383,13 +387,13 @@ namespace randomizer::logic::entrance_shuffle
|
||||
// If entrances are coupled, but the user tries to plandomize a non-primary connection, get the primary connection
|
||||
// instead
|
||||
if (world->Setting("Decouple Entrances") == "Off" &&
|
||||
randomizer::utility::container::ElementInContainer(entrancePool, entranceToConnect->GetReverse()))
|
||||
utility::container::ElementInContainer(entrancePool, entranceToConnect->GetReverse()))
|
||||
{
|
||||
entranceToConnect = entranceToConnect->GetReverse();
|
||||
targetToConnect = targetToConnect->GetReverse();
|
||||
}
|
||||
|
||||
if (randomizer::utility::container::ElementInContainer(entrancePool, entranceToConnect))
|
||||
if (utility::container::ElementInContainer(entrancePool, entranceToConnect))
|
||||
{
|
||||
bool validTargetFound = false;
|
||||
for (auto& target : targetPool)
|
||||
@@ -411,7 +415,7 @@ namespace randomizer::logic::entrance_shuffle
|
||||
}
|
||||
catch(const EntranceShuffleError& e)
|
||||
{
|
||||
throw std::runtime_error("Could not connect plandomized entrance " +
|
||||
throw std::runtime_error("Could not connect entrance " +
|
||||
entranceToConnect->GetOriginalName() + " to " + target->GetOriginalName() +
|
||||
" Reason:\n" + e.what());
|
||||
}
|
||||
@@ -425,8 +429,8 @@ namespace randomizer::logic::entrance_shuffle
|
||||
// If we found our target, delete the entrance and it's now connected target from their respective pools
|
||||
if (validTargetFound)
|
||||
{
|
||||
randomizer::utility::container::Erase(entrancePool, entranceToConnect);
|
||||
randomizer::utility::container::Erase(targetPool, targetToConnect->GetAssumed());
|
||||
utility::container::Erase(entrancePool, entranceToConnect);
|
||||
utility::container::Erase(targetPool, targetToConnect->GetAssumed());
|
||||
}
|
||||
// Otherwise, the target is invalid
|
||||
else
|
||||
@@ -438,7 +442,7 @@ namespace randomizer::logic::entrance_shuffle
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Plandomizer Error: " + entranceToConnect->GetOriginalName() +
|
||||
" for some reason could not be found.");
|
||||
" could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,15 +453,22 @@ namespace randomizer::logic::entrance_shuffle
|
||||
EntrancePools& entrancePools,
|
||||
EntrancePools& targetEntrancePools)
|
||||
{
|
||||
// If we aren't shuffling any non-assumed types, return early
|
||||
if (std::ranges::none_of(entrancePools | std::ranges::views::keys, [](const auto& type) {
|
||||
return NON_ASSUMED_TYPES.contains(type);
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& worlds = world->GetRandomizer()->GetWorlds();
|
||||
auto completeItemPool = item_pool::GetCompleteItemPool(worlds);
|
||||
|
||||
// The idea here is we want to try shuffling all the non-assumed entrances
|
||||
// at the same time since we can't validate the world after each one individually
|
||||
// (That would require assuming access to entrances which we can't guarantee access to)
|
||||
// at the same time since we can't validate the world after each one individually.
|
||||
// (That would require assuming access to entrances which we can't guarantee access to.)
|
||||
// Realistically, this should never take more than 1 or 2 tries unless there's some wacky
|
||||
// plandomizer stuff going on. Currently the only non-assumed entrance we're shuffling is the randomized spawn
|
||||
// but if we ever shuffle warp portals, they'll go here too.
|
||||
// plandomizer stuff going on. Currently, the only non-assumed entrance we're shuffling
|
||||
// is the randomized spawn, but if we ever shuffle warp portals, they'll go here too.
|
||||
|
||||
int retries = 20;
|
||||
while (retries > 0)
|
||||
@@ -501,7 +512,7 @@ namespace randomizer::logic::entrance_shuffle
|
||||
for (auto& [entrance, target] : rollbacks)
|
||||
{
|
||||
ConfirmReplacement(entrance, target);
|
||||
randomizer::utility::container::Erase(targetEntrancePools[entrance->GetType()], target);
|
||||
utility::container::Erase(targetEntrancePools[entrance->GetType()], target);
|
||||
}
|
||||
// Once we've made a valid world, delete all other targets that didn't get used
|
||||
for (auto& [entranceType, targetPool] : targetEntrancePools)
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
|
||||
#include "../utility/yaml.hpp"
|
||||
#include "../utility/file.hpp"
|
||||
#include "../utility/log.hpp"
|
||||
|
||||
namespace randomizer::logic::plandomizer
|
||||
{
|
||||
void LoadPlandomizerData(world::WorldPool& worlds, const fspath& filepath, const bool& ignoreErrors /*false*/)
|
||||
{
|
||||
// Verify the file exists before trying to open it
|
||||
// TODO: TRY CATCH HERE
|
||||
utility::file::Verify(filepath);
|
||||
|
||||
auto plandoTree = LoadYAML(filepath);
|
||||
@@ -33,8 +31,8 @@ namespace randomizer::logic::plandomizer
|
||||
return;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Plandomizer file locations for " + worldStr +
|
||||
" is not a map. Please check your syntax before trying again.");
|
||||
throw std::runtime_error("Locations for " + worldStr +
|
||||
" is not a map. Please check your plandomizer file syntax.");
|
||||
}
|
||||
|
||||
for (const auto& locationNode : locations)
|
||||
@@ -50,7 +48,7 @@ namespace randomizer::logic::plandomizer
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Plandomizer Error: Missing key \"item\" in node:\n" +
|
||||
throw std::runtime_error("Missing key \"item\" in node:\n" +
|
||||
YAML::Dump(locationNode));
|
||||
}
|
||||
|
||||
@@ -59,7 +57,7 @@ namespace randomizer::logic::plandomizer
|
||||
worldId = locationNode.second["World"].as<int>();
|
||||
if (worldId < 1 || worldId > worlds.size())
|
||||
{
|
||||
std::string errorMsg = "Plandomizer Error: Bad World ID \"" + std::to_string(worldId) +
|
||||
std::string errorMsg = "Bad World ID \"" + std::to_string(worldId) +
|
||||
"\"\nOnly " + std::to_string(worlds.size()) +
|
||||
" worlds are being generated.";
|
||||
throw std::runtime_error(errorMsg);
|
||||
|
||||
@@ -633,6 +633,21 @@ namespace randomizer::logic::world
|
||||
location->SetCurrentItem(item);
|
||||
utility::container::Erase(this->_itemPool, item);
|
||||
}
|
||||
|
||||
// If no world has entrance randomizer enabled, check to see if our plandomized item placements work
|
||||
if (std::ranges::none_of(this->GetRandomizer()->GetWorlds(), [](const auto& world) {
|
||||
return world->AnyEntranceRandomizerEnabled();
|
||||
})) {
|
||||
if (!this->_plandomizerLocations.empty() && Setting("Logic Rules") != "No Logic") {
|
||||
auto& worlds = this->GetRandomizer()->GetWorlds();
|
||||
auto completeItemPool = item_pool::GetCompleteItemPool(worlds);
|
||||
auto verifyLogicError = search::VerifyLogic(&worlds, completeItemPool);
|
||||
if (verifyLogicError.has_value())
|
||||
{
|
||||
throw std::runtime_error("Plandomizer item placements do not work! Reason:\n" + verifyLogicError.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void World::SetNonProgressLocations()
|
||||
@@ -1194,7 +1209,7 @@ namespace randomizer::logic::world
|
||||
}
|
||||
|
||||
entrance::EntrancePool World::GetShuffleableEntrances(const entrance::Type& type,
|
||||
const bool& onlyPrimary /* = false */)
|
||||
bool onlyPrimary /* = false */)
|
||||
{
|
||||
entrance::EntrancePool shuffleableEntrances = {};
|
||||
for (const auto& [areaName, area] : this->GetAreaTable())
|
||||
@@ -1213,7 +1228,7 @@ namespace randomizer::logic::world
|
||||
|
||||
entrance::EntrancePool World::GetShuffledEntrances(
|
||||
const entrance::Type& type /* = entrance::Type::ALL */,
|
||||
const bool& onlyPrimary /* = false */)
|
||||
bool onlyPrimary /* = false */)
|
||||
{
|
||||
auto entrances = this->GetShuffleableEntrances(type, onlyPrimary);
|
||||
|
||||
@@ -1282,4 +1297,14 @@ namespace randomizer::logic::world
|
||||
}
|
||||
return settings.GetMap().at(settingName);
|
||||
}
|
||||
|
||||
bool World::AnyEntranceRandomizerEnabled() {
|
||||
return Setting("Randomize Starting Spawn") != "Off" ||
|
||||
Setting("Randomize Dungeon Entrances") != "Off" ||
|
||||
Setting("Randomize Boss Entrances") != "Off" ||
|
||||
Setting("Randomize Grotto Entrances") != "Off" ||
|
||||
Setting("Randomize Cave Entrances") != "Off" ||
|
||||
Setting("Randomize Interior Entrances") != "Off" ||
|
||||
Setting("Randomize Overworld Entrances") != "Off";
|
||||
}
|
||||
} // namespace randomizer::logic::world
|
||||
|
||||
@@ -137,10 +137,10 @@ namespace randomizer::logic::world
|
||||
entrance::Entrance* GetEntrance(const std::string& originalName);
|
||||
int GetNewEntranceID();
|
||||
entrance::EntrancePool GetShuffleableEntrances(const entrance::Type& type,
|
||||
const bool& onlyPrimary = false);
|
||||
bool onlyPrimary = false);
|
||||
entrance::EntrancePool GetShuffledEntrances(
|
||||
const entrance::Type& type = entrance::Type::ALL,
|
||||
const bool& onlyPrimary = false);
|
||||
bool onlyPrimary = false);
|
||||
std::unordered_map<entrance::Entrance*, int>& GetExitTimeFormCache();
|
||||
|
||||
int GetMacroIndex(const std::string& macroName) const;
|
||||
@@ -149,6 +149,7 @@ namespace randomizer::logic::world
|
||||
std::string GetEventName(const int& eventIndex);
|
||||
|
||||
seedgen::settings::Setting& Setting(const std::string& settingName);
|
||||
bool AnyEntranceRandomizerEnabled();
|
||||
|
||||
TextDatabase& GetTextDatabase() { return this->_textDatabase; }
|
||||
const std::string& GetText(const std::string& name, Text::Type type = Text::STANDARD, Text::Language language = Text::ENGLISH) {
|
||||
|
||||
@@ -106,7 +106,11 @@ namespace randomizer
|
||||
// Process Plando Data for all worlds
|
||||
if (this->_config.IsUsingPlandomizer())
|
||||
{
|
||||
logic::plandomizer::LoadPlandomizerData(this->_worlds, this->_config.GetPlandomizerPath());
|
||||
try {
|
||||
logic::plandomizer::LoadPlandomizerData(this->_worlds, this->_config.GetPlandomizerPath());
|
||||
} catch (const std::runtime_error& e) {
|
||||
throw std::runtime_error("Plandomizer Error: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
// Pre Entrance Shuffle Tasks
|
||||
@@ -118,7 +122,7 @@ namespace randomizer
|
||||
utility::platform::Log("Shuffling Entrances...");
|
||||
for (auto& world : this->_worlds)
|
||||
{
|
||||
logic::entrance_shuffle::ShuffleWorldEntrances(world.get(), this->_worlds);
|
||||
logic::entrance_shuffle::ShuffleWorldEntrances(world.get());
|
||||
}
|
||||
|
||||
// Post Entrance Shuffle Tasks
|
||||
|
||||
@@ -421,6 +421,8 @@ Modal* RandomizerWindow::show_seed_gen_modal(std::string_view message) {
|
||||
},
|
||||
.icon = "verifying",
|
||||
})));
|
||||
// Allow manual line breaks in this modal for error messages
|
||||
modal->root()->SetProperty("white-space", "pre-line");
|
||||
|
||||
if (auto* doc = top_document()) {
|
||||
doc->focus();
|
||||
@@ -1189,7 +1191,7 @@ void RandomizerWindow::update() {
|
||||
m_genSeedModal->set_icon("error");
|
||||
}
|
||||
|
||||
m_genSeedModal->set_body(generationStatusMsg);
|
||||
m_genSeedModal->set_body(escape(generationStatusMsg));
|
||||
m_genSeedModal->add_action({
|
||||
.label = "OK",
|
||||
.onPressed = [this](Modal& modal) {
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
void show() override;
|
||||
void hide(bool close) override;
|
||||
bool visible() const override;
|
||||
Rml::Element* root() { return mRoot;}
|
||||
|
||||
protected:
|
||||
Rml::Element* mRoot = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user