better plando error messaging

This commit is contained in:
gymnast86
2026-06-06 20:17:51 -07:00
parent 7e367c1e15
commit 3fb86dbac8
9 changed files with 74 additions and 33 deletions
@@ -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);
+27 -2
View File
@@ -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) {
+6 -2
View File
@@ -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
+3 -1
View File
@@ -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) {
+1
View File
@@ -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;