mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-27 07:37:24 -04:00
update hyrule castle settings
This commit is contained in:
@@ -236,11 +236,20 @@ Can Complete All Dungeons: "'Can_Complete_Forest_Temple' and 'Can_Complete_Goron
|
||||
'Can_Complete_Arbiters_Grounds' and 'Can_Complete_Snowpeak_Ruins' and 'Can_Complete_Temple_of_Time' and
|
||||
'Can_Complete_City_in_the_Sky' and 'Can_Complete_Palace_of_Twilight'"
|
||||
|
||||
Can Break Hyrule Castle Barrier: Hyrule_Castle_Requirements == Open or
|
||||
(Hyrule_Castle_Requirements == Vanilla and 'Can_Complete_Palace_of_Twilight') or
|
||||
(Hyrule_Castle_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, 3)) or
|
||||
(Hyrule_Castle_Requirements == Mirror_Shards and count(Progressive_Mirror_Shard, 4)) or
|
||||
(Hyrule_Castle_Requirements == All_Dungeons and Can_Complete_All_Dungeons)
|
||||
Can Break Hyrule Castle Barrier: Hyrule_Barrier_Requirements == Open or
|
||||
(Hyrule_Barrier_Requirements == Vanilla and 'Can_Complete_Palace_of_Twilight') or
|
||||
(Hyrule_Barrier_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, Hyrule_Barrier_Fused_Shadows)) or
|
||||
(Hyrule_Barrier_Requirements == Mirror_Shards and count(Progressive_Mirror_Shard, Hyrule_Barrier_Mirror_Shards)) or
|
||||
(Hyrule_Barrier_Requirements == Dungeons and dungeons_completed(Hyrule_Barrier_Dungeons)) or
|
||||
(Hyrule_Barrier_Requirements == Poe_Souls and count(Poe_Soul, Hyrule_Barrier_Poe_Souls)) or
|
||||
(Hyrule_Barrier_Requirements == Hearts and hearts(Hyrule_Barrier_Hearts))
|
||||
|
||||
Can Open Hyrule Castle Big Key Gate: Hyrule_Castle_Big_Key_Requirements == None or
|
||||
(Hyrule_Castle_Big_Key_Requirements == Fused_Shadows and count(Progressive_Fused_Shadow, Hyrule_Castle_Big_Key_Fused_Shadows)) or
|
||||
(Hyrule_Castle_Big_Key_Requirements == Mirror_Shards and count(Progressive_Fused_Shadow, Hyrule_Castle_Big_Key_Mirror_Shards)) or
|
||||
(Hyrule_Castle_Big_Key_Requirements == Dungeons and dungeons_completed(Hyrule_Castle_Big_Key_Dungeons)) or
|
||||
(Hyrule_Castle_Big_Key_Requirements == Poe_Souls and count(Poe_Soul, Hyrule_Castle_Big_Key_Poe_Souls)) or
|
||||
(Hyrule_Castle_Big_Key_Requirements == Hearts and hearts(Hyrule_Castle_Big_Key_Hearts))
|
||||
|
||||
# WARP PORTALS
|
||||
|
||||
|
||||
@@ -16,14 +16,46 @@
|
||||
## Access Options ##
|
||||
######################
|
||||
|
||||
- Name: Hyrule Castle Requirements
|
||||
- Name: Hyrule Barrier Requirements
|
||||
Default Option: Vanilla
|
||||
Options:
|
||||
- Open: "The barrier around Hyrule Castle is dispelled from the beginning."
|
||||
- Fused Shadows: "The player must collect all 3 Fused Shadows."
|
||||
- Mirror Shards: "The player must collect all 4 Mirror Shards."
|
||||
- All Dungeons: "The player must complete all dungeons."
|
||||
- Vanilla: "The player must complete Palace of Twilight."
|
||||
- Vanilla: "The barrier will be dispelled once Palace of Twilight is cleared."
|
||||
- Fused Shadows: "The barrier will be dispelled once the required number of Fused Shadows have been collected."
|
||||
- Mirror Shards: "The barrier will be dispelled once the required number of Mirror Shards have been collected."
|
||||
- Dungeons: "The barrier will be dispelled once the required number of Dungeons have been cleared."
|
||||
- Poe Souls: "The barrier will be dispelled once the required number of Poe Souls have been collected."
|
||||
- Hearts: "The barrier will be dispelled once the required number of Hearts have been reached."
|
||||
|
||||
- Name: Hyrule Barrier Fused Shadows
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-3: description
|
||||
|
||||
- Name: Hyrule Barrier Mirror Shards
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-4: description
|
||||
|
||||
- Name: Hyrule Barrier Dungeons
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-8: description
|
||||
|
||||
- Name: Hyrule Barrier Poe Souls
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-60: description
|
||||
|
||||
- Name: Hyrule Barrier Hearts
|
||||
Tracker Important: True
|
||||
Default Option: 4
|
||||
Options:
|
||||
- 4-20: description # Hehe 420
|
||||
|
||||
- Name: Palace of Twilight Requirements
|
||||
Default Option: Vanilla
|
||||
@@ -150,6 +182,47 @@
|
||||
- Anywhere: "Maps and Compasses can appear anywhere."
|
||||
- Start With: "The player starts with all Maps and Compasses."
|
||||
|
||||
- Name: Hyrule Castle Big Key Requirements
|
||||
Tracker Important: True
|
||||
Default Option: None
|
||||
Options:
|
||||
- None: "The gate is opened and the key is randomized according to the respective Big Key settings."
|
||||
- Fused Shadows: "The gate will open once the required number of Fused Shadows have been collected."
|
||||
- Mirror Shards: "The gate will open once the required number of Mirror Shards have been collected."
|
||||
- Dungeons: "The gate will open once the required number of Dungeons have been cleared."
|
||||
- Poe Souls: "The gate will open once the required number of Poe Souls have been collected."
|
||||
- Hearts: "The gate will open once the required number of Hearts have been reached."
|
||||
|
||||
- Name: Hyrule Castle Big Key Fused Shadows
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-3: description
|
||||
|
||||
- Name: Hyrule Castle Big Key Mirror Shards
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-4: description
|
||||
|
||||
- Name: Hyrule Castle Big Key Dungeons
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-8: description
|
||||
|
||||
- Name: Hyrule Castle Big Key Poe Souls
|
||||
Tracker Important: True
|
||||
Default Option: 1
|
||||
Options:
|
||||
- 1-60: description
|
||||
|
||||
- Name: Hyrule Castle Big Key Hearts
|
||||
Tracker Important: True
|
||||
Default Option: 4
|
||||
Options:
|
||||
- 4-20: description # Hehe 420
|
||||
|
||||
- Name: Dungeon Rewards Can Be Anywhere
|
||||
Default Option: "Off"
|
||||
Options:
|
||||
|
||||
@@ -85,7 +85,7 @@ EventFlags:
|
||||
- 0x0003 # Yeto put pumpkin and cheese in soup.
|
||||
- 0x1460 # Snowpeak Ruins North and West doors unlocked.
|
||||
- 0x0120 # Told Yeta about cheese
|
||||
- Hyrule_Castle_Requirements == Open:
|
||||
- Hyrule_Barrier_Requirements == Open:
|
||||
- 0x4208 # Remove Castle Barrier
|
||||
- Palace_of_Twilight_Requirements == Open:
|
||||
- 0x2B08 # Mirror of Twilight Repaired.
|
||||
@@ -592,6 +592,9 @@ RegionFlags:
|
||||
- 0x93 # Unlock door outside 3F.
|
||||
- 0xB0 # Unlock treasure room door.
|
||||
- 0xA3 # Unlock door in south garden.
|
||||
- Big_Keys == Keysy and Hyrule_Castle_Big_Key_Requirements == None:
|
||||
- 0xA1 # Unlocked Hyrule Castle Boss Door.
|
||||
- 0xED # Got Hyrule Castle Big Key.
|
||||
- Maps_and_Compasses == Start_With:
|
||||
- 0xEE # Got Hyrule Castle Compass.
|
||||
- 0xEF # Got Hyrule Castle Dungeon Map.
|
||||
@@ -601,4 +604,5 @@ RegionFlags:
|
||||
- 0x85 # watched focus on lowered chandelier cs
|
||||
- 0x9D # lower the main hall chandelier
|
||||
- 0xAF # defeated double Dinalfos (opens gates both sides)
|
||||
|
||||
- Hyrule_Castle_Big_Key_Requirements == None:
|
||||
- 0x94 # Open HC BK gate
|
||||
|
||||
@@ -17,7 +17,7 @@ Gifts From NPCs: Random
|
||||
Golden Bugs: Random
|
||||
Goron Mines Entrance: Random
|
||||
Hidden Skills: Random
|
||||
Hyrule Castle Requirements: Random
|
||||
Hyrule Barrier Requirements: Random
|
||||
Increase Spinner Speed: Random
|
||||
Increase Wallet Capacity: Random
|
||||
Instant Message Text: Random
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Barrier Requirements: Dungeons
|
||||
Hyrule Barrier Dungeons: 8
|
||||
@@ -0,0 +1,2 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Barrier Requirements: Fused Shadows
|
||||
@@ -0,0 +1,3 @@
|
||||
seed: TESTTESTTEST
|
||||
Hyrule Barrier Requirements: Hearts
|
||||
Hyrule Barrier Hearts: 13
|
||||
@@ -0,0 +1,2 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Barrier Requirements: Mirror Shards
|
||||
@@ -0,0 +1,2 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Barrier Requirements: Open
|
||||
@@ -0,0 +1,3 @@
|
||||
seed: TESTTESTTEST
|
||||
Hyrule Barrier Requirements: Poe Souls
|
||||
Hyrule Barrier Poe Souls: 30
|
||||
@@ -1,2 +0,0 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Castle Requirements: All Dungeons
|
||||
@@ -1,2 +0,0 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Castle Requirements: Fused Shadows
|
||||
@@ -1,2 +0,0 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Castle Requirements: Mirror Shards
|
||||
@@ -1,2 +0,0 @@
|
||||
Seed: TESTTESTTEST
|
||||
Hyrule Castle Requirements: Open
|
||||
@@ -1,3 +1,3 @@
|
||||
Seed: TESTTESTTEST
|
||||
Unrequired Dungeons Are Barren: On
|
||||
Hyrule Castle Requirements: Mirror Shards
|
||||
Hyrule Barrier Requirements: Mirror Shards
|
||||
@@ -161,7 +161,7 @@
|
||||
Region: Hyrule Castle
|
||||
Locations:
|
||||
Hyrule Castle Southeast Balcony Tower Chest: Can_Defeat_Aerolfos
|
||||
Hyrule Castle Big Key Chest: Nothing
|
||||
Hyrule Castle Big Key Chest: Can_Open_Hyrule_Castle_Big_Key_Gate
|
||||
Exits:
|
||||
Hyrule Castle Final Climb Near Outside Balcony: Can_Open_Doors and (count(Hyrule_Castle_Small_Key, 2) or Small_Keys == Keysy)
|
||||
Hyrule Castle Double Darknut Room: Can_Open_Doors and 'Can_Defeat_Hyrule_Castle_Double_Darknuts'
|
||||
|
||||
@@ -747,11 +747,11 @@ namespace randomizer::logic::entrance_shuffle
|
||||
foundLocations.end(),
|
||||
[](const auto& location) { return location->IsProgression(); });
|
||||
|
||||
// If there are no sphere zero locations available and we didn't find a disconnected exit, then this world will not
|
||||
// be valid. Often times when many entrances are randomized we won't find any locations, but will find disconnected
|
||||
// If there are no sphere zero locations available and we didn't find an accessible disconnected exit, then this world will not
|
||||
// be valid. Often times when many entrances are randomized we won't find any locations, but will find accessible disconnected
|
||||
// exits that haven't been shuffled yet. In this case we can usually wait until these exits are connected and more often
|
||||
// than not this will lead us to sphere zero locations.
|
||||
if (numSphereZeroLocations == 0 && !sphereZeroSearch._foundDisconnectedExit)
|
||||
if (numSphereZeroLocations == 0 && !sphereZeroSearch.HasAccessibleDisconnectedExit())
|
||||
{
|
||||
throw EntranceShuffleError("No sphere 0 locations reachable at the start!");
|
||||
}
|
||||
|
||||
@@ -243,18 +243,6 @@ int BitIndex::reqBit(const randomizer::logic::requirement::Requirement& req)
|
||||
reverseIndex.push_back(req);
|
||||
return bump();
|
||||
}
|
||||
// case randomizer::logic::requirement::Type::HEALTH:
|
||||
// key = std::to_string(std::get<int>(req._args[0]));
|
||||
// if (heartCount.contains(key))
|
||||
// {
|
||||
// return heartCount[key];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// heartCount[key] = counter;
|
||||
// reverseIndex.push_back(req);
|
||||
// return bump();
|
||||
// }
|
||||
case randomizer::logic::requirement::Type::GOLDEN_BUGS:
|
||||
key = std::to_string(std::get<int>(req._args[0]));
|
||||
if (goldenBugCount.contains(key))
|
||||
@@ -267,6 +255,30 @@ int BitIndex::reqBit(const randomizer::logic::requirement::Requirement& req)
|
||||
reverseIndex.push_back(req);
|
||||
return bump();
|
||||
}
|
||||
case randomizer::logic::requirement::Type::HEARTS:
|
||||
key = std::to_string(std::get<int>(req._args[0]));
|
||||
if (heartCount.contains(key))
|
||||
{
|
||||
return heartCount[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
heartCount[key] = counter;
|
||||
reverseIndex.push_back(req);
|
||||
return bump();
|
||||
}
|
||||
case randomizer::logic::requirement::Type::DUNGEONS_COMPLETED:
|
||||
key = std::to_string(std::get<int>(req._args[0]));
|
||||
if (dungeonCompletedCount.contains(key))
|
||||
{
|
||||
return dungeonCompletedCount[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
dungeonCompletedCount[key] = counter;
|
||||
reverseIndex.push_back(req);
|
||||
return bump();
|
||||
}
|
||||
default:
|
||||
// Not a flattening requirement
|
||||
return -1;
|
||||
|
||||
@@ -64,8 +64,9 @@ class BitIndex
|
||||
int reqBit(const randomizer::logic::requirement::Requirement& req);
|
||||
|
||||
std::unordered_map<std::string, int> itemBits = {};
|
||||
// std::unordered_map<std::string, int> heartCount = {};
|
||||
std::unordered_map<std::string, int> heartCount = {};
|
||||
std::unordered_map<std::string, int> goldenBugCount = {};
|
||||
std::unordered_map<std::string, int> dungeonCompletedCount = {};
|
||||
std::vector<randomizer::logic::requirement::Requirement> reverseIndex = {};
|
||||
int counter = 0;
|
||||
};
|
||||
|
||||
@@ -426,12 +426,14 @@ DNF evaluatePartialRequirement(BitIndex& bitIndex,
|
||||
formTime));
|
||||
}
|
||||
return d;
|
||||
|
||||
|
||||
case randomizer::logic::requirement::Type::ITEM:
|
||||
[[fallthrough]];
|
||||
case randomizer::logic::requirement::Type::GOLDEN_BUGS:
|
||||
[[fallthrough]];
|
||||
case randomizer::logic::requirement::Type::ITEM:
|
||||
// [[fallthrough]];
|
||||
// case randomizer::logic::requirement::Type::HEALTH:
|
||||
case randomizer::logic::requirement::Type::HEARTS:
|
||||
[[fallthrough]];
|
||||
case randomizer::logic::requirement::Type::DUNGEONS_COMPLETED:
|
||||
bits[bitIndex.reqBit(req)] = 1;
|
||||
return DNF({bits});
|
||||
|
||||
|
||||
@@ -54,6 +54,13 @@ namespace randomizer::logic::item
|
||||
{
|
||||
this->_stamp = true;
|
||||
}
|
||||
// Make hearts major items if they're required for anything
|
||||
else if ((name == "Piece of Heart" || name == "Heart Container") &&
|
||||
((world->Setting("Hyrule Barrier Requirements") == "Hearts") ||
|
||||
(world->Setting("Hyrule Castle Big Key Requirements") == "Hearts")))
|
||||
{
|
||||
this->_importance = Importance::MAJOR;
|
||||
}
|
||||
}
|
||||
|
||||
int Item::GetID() const
|
||||
|
||||
@@ -291,8 +291,13 @@ namespace randomizer::logic::item_pool
|
||||
{"Temple of Time Big Key"},
|
||||
{"City in the Sky Big Key"},
|
||||
{"Palace of Twilight Big Key"},
|
||||
{"Hyrule Castle Big Key"},
|
||||
};
|
||||
|
||||
if (world->Setting("Hyrule Castle Big Key Requirements") == "None")
|
||||
{
|
||||
bigKeys.emplace_back("Hyrule Castle Big Key");
|
||||
}
|
||||
|
||||
for (const auto& key : bigKeys)
|
||||
{
|
||||
itemPool.at(key) = 0;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "search.hpp"
|
||||
#include "world.hpp"
|
||||
#include "../utility/container.hpp"
|
||||
#include "../utility/general.hpp"
|
||||
#include "../utility/log.hpp"
|
||||
#include "../utility/string.hpp"
|
||||
|
||||
@@ -135,6 +136,15 @@ namespace randomizer::logic::requirement
|
||||
case Type::GOLDEN_BUGS:
|
||||
count = std::get<int>(this->_args[0]);
|
||||
return "golden_bugs(" + std::to_string(count) + ")";
|
||||
|
||||
case Type::HEARTS:
|
||||
count = std::get<int>(this->_args[0]);
|
||||
return "hearts(" + std::to_string(count) + ")";
|
||||
|
||||
case Type::DUNGEONS_COMPLETED:
|
||||
count = std::get<int>(this->_args[0]);
|
||||
return "dungeons_completed(" + std::to_string(count) + ")";
|
||||
|
||||
default:
|
||||
return reqStr;
|
||||
}
|
||||
@@ -334,9 +344,16 @@ namespace randomizer::logic::requirement
|
||||
}
|
||||
splitLogicStr.push_back(countArgs);
|
||||
|
||||
// For the count, if a setting is passed in, use the setting's value instead
|
||||
auto& countStr = splitLogicStr[1];
|
||||
if (seedgen::settings::GetAllSettingsInfo()->contains(countStr))
|
||||
{
|
||||
countStr = world->Setting(countStr).GetCurrentOption();
|
||||
}
|
||||
|
||||
// Get the arguments
|
||||
auto& itemName = splitLogicStr[0];
|
||||
int count = std::stoi(splitLogicStr[1]);
|
||||
int count = std::stoi(countStr);
|
||||
auto item = world->GetItem(itemName);
|
||||
req._args.emplace_back(count);
|
||||
req._args.emplace_back(item);
|
||||
@@ -357,24 +374,31 @@ namespace randomizer::logic::requirement
|
||||
return req;
|
||||
}
|
||||
|
||||
// And finally a health check
|
||||
// else if (argStr.find("health") != std::string::npos)
|
||||
// {
|
||||
// req._type = randomizer::logic::requirement::Type::HEALTH;
|
||||
// std::string numHeartsStr(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1);
|
||||
// int numHearts = std::stoi(numHeartsStr);
|
||||
// req._args.emplace_back(numHearts);
|
||||
// return req;
|
||||
// }
|
||||
// Then health
|
||||
else if (argStr.find("hearts") != std::string::npos)
|
||||
{
|
||||
req._type = randomizer::logic::requirement::Type::HEARTS;
|
||||
std::string numHeartsStr(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1);
|
||||
|
||||
// If the string for the count is a setting, use the settings current option instead
|
||||
if (seedgen::settings::GetAllSettingsInfo()->contains(numHeartsStr))
|
||||
{
|
||||
numHeartsStr = world->Setting(numHeartsStr).GetCurrentOption();
|
||||
}
|
||||
|
||||
// Check Impossible down here since it's very unlikely
|
||||
int numHearts = std::stoi(numHeartsStr);
|
||||
req._args.emplace_back(numHearts);
|
||||
return req;
|
||||
}
|
||||
|
||||
// Then Impossible...
|
||||
else if (argStr == "Impossible")
|
||||
{
|
||||
req._type = randomizer::logic::requirement::Type::IMPOSSIBLE;
|
||||
return req;
|
||||
}
|
||||
|
||||
// Check golden bugs last since it's least likely
|
||||
// Then golden bugs...
|
||||
else if (argStr.find("golden bugs") != std::string::npos)
|
||||
{
|
||||
req._type = randomizer::logic::requirement::Type::GOLDEN_BUGS;
|
||||
@@ -385,6 +409,24 @@ namespace randomizer::logic::requirement
|
||||
return req;
|
||||
}
|
||||
|
||||
// Then dungeons completed
|
||||
else if (argStr.find("dungeons completed") != std::string::npos)
|
||||
{
|
||||
req._type = Type::DUNGEONS_COMPLETED;
|
||||
// Get rid of parenthesis
|
||||
std::string countStr(argStr.begin() + argStr.find('(') + 1, argStr.end() - 1);
|
||||
|
||||
// For the count, if a setting is passed in, use the setting's value instead
|
||||
if (seedgen::settings::GetAllSettingsInfo()->contains(countStr))
|
||||
{
|
||||
countStr = world->Setting(countStr).GetCurrentOption();
|
||||
}
|
||||
|
||||
int count = std::stoi(countStr);
|
||||
req._args.emplace_back(count);
|
||||
return req;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unrecognized logic symbol: \"" + reqStr + "\"");
|
||||
}
|
||||
|
||||
@@ -445,12 +487,73 @@ namespace randomizer::logic::requirement
|
||||
return req;
|
||||
}
|
||||
|
||||
bool EvaluateSimpleRequirement(const randomizer::logic::requirement::Requirement& req, randomizer::logic::world::World* world)
|
||||
{
|
||||
randomizer::logic::item::Item* item;
|
||||
randomizer::logic::item::Item* heartPiece;
|
||||
randomizer::logic::item::Item* heartContainer;
|
||||
int count;
|
||||
int macroIndex;
|
||||
switch (req._type)
|
||||
{
|
||||
case Type::NOTHING:
|
||||
return true;
|
||||
|
||||
case Type::IMPOSSIBLE:
|
||||
return false;
|
||||
|
||||
case Type::OR:
|
||||
return std::any_of(
|
||||
req._args.begin(),
|
||||
req._args.end(),
|
||||
[&](const auto& arg)
|
||||
{ return EvaluateSimpleRequirement(std::get<Requirement>(arg), world); });
|
||||
|
||||
case Type::AND:
|
||||
return std::all_of(
|
||||
req._args.begin(),
|
||||
req._args.end(),
|
||||
[&](const auto& arg)
|
||||
{ return EvaluateSimpleRequirement(std::get<Requirement>(arg), world); });
|
||||
|
||||
case Type::ITEM:
|
||||
item = std::get<randomizer::logic::item::Item*>(req._args[0]);
|
||||
return randomizer::utility::container::ElementInContainer(world->GetStartingItemPool(), item);
|
||||
|
||||
case Type::COUNT:
|
||||
count = std::get<int>(req._args[0]);
|
||||
item = std::get<randomizer::logic::item::Item*>(req._args[1]);
|
||||
return std::ranges::count(world->GetStartingItemPool(), item) >= count;
|
||||
|
||||
case Type::MACRO:
|
||||
macroIndex = std::get<int>(req._args[0]);
|
||||
return EvaluateSimpleRequirement(world->GetMacro(macroIndex), world);
|
||||
|
||||
case Type::GOLDEN_BUGS:
|
||||
count = std::get<int>(req._args[0]);
|
||||
return std::ranges::count_if(world->GetStartingItemPool(),
|
||||
[](const auto& item) { return item->IsGoldenBug(); }) >= count;
|
||||
|
||||
case Type::HEARTS:
|
||||
count = std::get<int>(req._args[0]);
|
||||
heartPiece = world->GetItem("Piece of Heart");
|
||||
heartContainer = world->GetItem("Heart Container");
|
||||
return std::ranges::count(world->GetStartingItemPool(), heartPiece) +
|
||||
std::ranges::count(world->GetStartingItemPool(), heartContainer) * 5 >= count * 5;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EvaluateRequirementAtFormTime(const randomizer::logic::requirement::Requirement& req,
|
||||
randomizer::logic::search::Search* search,
|
||||
const int& formTime,
|
||||
randomizer::logic::world::World* world)
|
||||
{
|
||||
randomizer::logic::item::Item* item;
|
||||
randomizer::logic::item::Item* heartPiece;
|
||||
randomizer::logic::item::Item* heartContainer;
|
||||
int count;
|
||||
int eventIndex;
|
||||
int macroIndex;
|
||||
@@ -513,6 +616,37 @@ namespace randomizer::logic::requirement
|
||||
return std::count_if(search->_ownedItems.begin(),
|
||||
search->_ownedItems.end(),
|
||||
[](const auto& item) { return item->IsGoldenBug(); }) >= count;
|
||||
|
||||
case Type::HEARTS:
|
||||
count = std::get<int>(req._args[0]);
|
||||
heartPiece = world->GetItem("Piece of Heart");
|
||||
heartContainer = world->GetItem("Heart Container");
|
||||
return search->_ownedItems.count(heartPiece) +
|
||||
(search->_ownedItems.count(heartContainer) + 3) * 5 >= count * 5;
|
||||
|
||||
case Type::DUNGEONS_COMPLETED:
|
||||
count = std::get<int>(req._args[0]);
|
||||
return std::ranges::count_if(search->_ownedEvents, [&](int eventId){
|
||||
std::list<std::string> dungeonCompletionEvents = {
|
||||
"Can Complete Forest Temple",
|
||||
"Can Complete Goron Mines",
|
||||
"Can Complete Lakebed Temple",
|
||||
"Can Complete Arbiters Grounds",
|
||||
"Can Complete Snowpeak Ruins",
|
||||
"Can Complete Temple of Time",
|
||||
"Can Complete City in the Sky",
|
||||
"Can Complete Palace of Twilight"
|
||||
};
|
||||
for (const auto& eventName : dungeonCompletionEvents)
|
||||
{
|
||||
if (world->GetEventIndex(eventName) == eventId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}) >= count;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -534,7 +668,7 @@ namespace randomizer::logic::requirement
|
||||
// Some exits in the middle of entrance shuffling will not have a connected area. Ignore these
|
||||
if (exit->GetConnectedArea() == nullptr)
|
||||
{
|
||||
return EvalSuccess::UNNECESSARY;
|
||||
return EvalSuccess::DISCONNECTED;
|
||||
}
|
||||
|
||||
// If the exit is currently disabled, don't try it
|
||||
@@ -621,6 +755,36 @@ namespace randomizer::logic::requirement
|
||||
return evalSuccess;
|
||||
}
|
||||
|
||||
EvalSuccess EvaluateDisconnectedExitRequiremrnt(randomizer::logic::search::Search* search, randomizer::logic::entrance::Entrance* exit)
|
||||
{
|
||||
// If the exit is currently disabled, don't try it
|
||||
if (exit->IsDisabled())
|
||||
{
|
||||
return EvalSuccess::NONE;
|
||||
}
|
||||
|
||||
auto& exitFormTimeCache = exit->GetWorld()->GetExitTimeFormCache();
|
||||
auto parentArea = exit->GetParentArea();
|
||||
auto parentAreaFormTime = search->_areaFormTime[parentArea];
|
||||
|
||||
// Check each form time individually and spread the ones which succeed. If any of them pass, set the evaluation success
|
||||
// to partial.
|
||||
auto evalSuccess = EvalSuccess::NONE;
|
||||
const auto& formTimes = FormTime::ALL_FORM_TIMES;
|
||||
for (const auto& formTime : formTimes)
|
||||
{
|
||||
if (formTime & parentAreaFormTime)
|
||||
{
|
||||
if (EvaluateRequirementAtFormTime(exit->GetRequirement(), search, formTime, exit->GetWorld()))
|
||||
{
|
||||
return EvalSuccess::PARTIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EvalSuccess::NONE;
|
||||
}
|
||||
|
||||
EvalSuccess EvaluateLocationRequirement(randomizer::logic::search::Search* search, randomizer::logic::area::LocationAccess* locAccess)
|
||||
{
|
||||
auto& formTime = search->_areaFormTime[locAccess->GetArea()];
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace randomizer::logic::requirement
|
||||
WOLF_LINK,
|
||||
TWILIGHT,
|
||||
GOLDEN_BUGS,
|
||||
HEARTS,
|
||||
DUNGEONS_COMPLETED,
|
||||
};
|
||||
|
||||
enum class EvalSuccess
|
||||
@@ -58,7 +60,7 @@ namespace randomizer::logic::requirement
|
||||
NONE,
|
||||
PARTIAL,
|
||||
COMPLETE,
|
||||
UNNECESSARY,
|
||||
DISCONNECTED,
|
||||
};
|
||||
|
||||
// FormTime is a set of flags that cover all the possible cases of human-wolf/day-night combinations that are needed
|
||||
@@ -101,12 +103,22 @@ namespace randomizer::logic::requirement
|
||||
randomizer::logic::world::World* world,
|
||||
const bool& forceLogic = false);
|
||||
|
||||
/**
|
||||
* @brief Evaluates a requirement assuming it meets a simplistic criteria. This is used
|
||||
* for checking settings when reading them in from, for example, startflags.yaml
|
||||
*
|
||||
* @param req - The simple requirement
|
||||
* @return true if the requirment holds, false otherwise
|
||||
*/
|
||||
bool EvaluateSimpleRequirement(const randomizer::logic::requirement::Requirement& req, randomizer::logic::world::World* world);
|
||||
|
||||
bool EvaluateRequirementAtFormTime(const randomizer::logic::requirement::Requirement& req,
|
||||
randomizer::logic::search::Search* search,
|
||||
const int& formTime,
|
||||
randomizer::logic::world::World*);
|
||||
EvalSuccess EvaluateEventRequirement(randomizer::logic::search::Search* search, randomizer::logic::area::EventAccess* event);
|
||||
EvalSuccess EvaluateExitRequirement(randomizer::logic::search::Search* search, randomizer::logic::entrance::Entrance* exit);
|
||||
EvalSuccess EvaluateDisconnectedExitRequiremrnt(randomizer::logic::search::Search* search, randomizer::logic::entrance::Entrance* exit);
|
||||
EvalSuccess EvaluateLocationRequirement(randomizer::logic::search::Search* search,
|
||||
randomizer::logic::area::LocationAccess* locAccess);
|
||||
|
||||
|
||||
@@ -140,13 +140,9 @@ namespace randomizer::logic::search
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the exit is unnecessary, we'll just consider it successful and move on
|
||||
// If the exit is successful
|
||||
auto evalSuccess = randomizer::logic::requirement::EvaluateExitRequirement(this, exit);
|
||||
if (evalSuccess == randomizer::logic::requirement::EvalSuccess::UNNECESSARY)
|
||||
{
|
||||
this->_successfulExits.insert(exit);
|
||||
}
|
||||
else if (randomizer::utility::general::IsAnyOf(evalSuccess,
|
||||
if (randomizer::utility::general::IsAnyOf(evalSuccess,
|
||||
randomizer::logic::requirement::EvalSuccess::COMPLETE,
|
||||
randomizer::logic::requirement::EvalSuccess::PARTIAL))
|
||||
{
|
||||
@@ -305,9 +301,9 @@ namespace randomizer::logic::search
|
||||
this->Explore(exit->GetConnectedArea());
|
||||
}
|
||||
case randomizer::logic::requirement::EvalSuccess::NONE:
|
||||
[[fallthrough]];
|
||||
case randomizer::logic::requirement::EvalSuccess::DISCONNECTED:
|
||||
this->_exitsToTry.push_back(exit);
|
||||
case randomizer::logic::requirement::EvalSuccess::UNNECESSARY:
|
||||
this->_foundDisconnectedExit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,6 +372,19 @@ namespace randomizer::logic::search
|
||||
}
|
||||
}
|
||||
|
||||
bool Search::HasAccessibleDisconnectedExit()
|
||||
{
|
||||
for (const auto& exit : this->_exitsToTry)
|
||||
{
|
||||
if (exit->GetConnectedArea() == nullptr &&
|
||||
randomizer::logic::requirement::EvaluateDisconnectedExitRequiremrnt(this, exit) != requirement::EvalSuccess::NONE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Search::RemoveEmptySpheres()
|
||||
{
|
||||
// Get rid of any empty spheres in both the item playthrough and entrance playthrough
|
||||
|
||||
@@ -115,6 +115,7 @@ namespace randomizer::logic::search
|
||||
void ExpandFormTimes(randomizer::logic::area::Area* area);
|
||||
|
||||
void AddExitToEntranceSpheres(randomizer::logic::entrance::Entrance*);
|
||||
bool HasAccessibleDisconnectedExit();
|
||||
void RemoveEmptySpheres();
|
||||
|
||||
/**
|
||||
@@ -143,7 +144,6 @@ namespace randomizer::logic::search
|
||||
std::unordered_set<randomizer::logic::area::Area*> _visitedAreas;
|
||||
std::unordered_set<randomizer::logic::entrance::Entrance*> _successfulExits;
|
||||
std::unordered_set<randomizer::logic::entrance::Entrance*> _playthroughEntrances;
|
||||
bool _foundDisconnectedExit = false;
|
||||
|
||||
std::list<std::list<randomizer::logic::location::Location*>> _playthroughSpheres;
|
||||
std::list<std::list<randomizer::logic::entrance::Entrance*>> _entranceSpheres;
|
||||
|
||||
@@ -493,11 +493,7 @@ namespace randomizer::logic::world
|
||||
bool World::EvaluateSettingCondition(const std::string& condition)
|
||||
{
|
||||
auto req = randomizer::logic::requirement::ParseRequirementString(condition, this, true);
|
||||
if (req._type == requirement::Type::NOTHING)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return requirement::EvaluateSimpleRequirement(req, this);
|
||||
}
|
||||
|
||||
void World::GenerateItemPools()
|
||||
@@ -540,11 +536,14 @@ namespace randomizer::logic::world
|
||||
if ((this->Setting("Small Keys") == "Vanilla" &&
|
||||
(originalItem->IsDungeonSmallKey() ||
|
||||
randomizer::utility::str::Contains(originalItemName, "Ordon Pumpkin", "Ordon Cheese"))) ||
|
||||
// Vanilla Big Keys
|
||||
(this->Setting("Big Keys") == "Vanilla" && originalItem->IsBigKey()) ||
|
||||
// Vanilla Big Keys (only include Hyrule Castle Big Key if it has no requirements)
|
||||
(this->Setting("Big Keys") == "Vanilla" && originalItem->IsBigKey() &&
|
||||
(originalItemName != "Hyrule Castle Big Key" || this->Setting("Hyrule Castle Big Key Requirements") == "None")) ||
|
||||
// Vanilla Maps and Compasses
|
||||
(this->Setting("Maps and Compasses") == "Vanilla" &&
|
||||
(originalItem->IsDungeonMap() || originalItem->IsCompass())) ||
|
||||
// Hyrule Castle Big Key
|
||||
(originalItemName == "Hyrule Castle Big Key" && this->Setting("Hyrule Castle Big Key Requirements") != "None") ||
|
||||
// Vanilla Poe Souls
|
||||
(originalItemName == "Poe Soul" &&
|
||||
(this->Setting("Poe Souls") == "Vanilla" ||
|
||||
@@ -1134,15 +1133,22 @@ namespace randomizer::logic::world
|
||||
return this->_macros.at(macroIndex);
|
||||
}
|
||||
|
||||
int World::GetEventIndex(const std::string& eventName)
|
||||
int World::GetEventIndex(const std::string& eventName, bool addIfNone /*= true*/)
|
||||
{
|
||||
// Add the event if it doesn't exist yet
|
||||
// If the event doesn't exist
|
||||
if (!this->_eventIndexes.contains(eventName))
|
||||
{
|
||||
auto index = this->_randomizer->GetNewEventID();
|
||||
this->_eventIndexes.emplace(eventName, index);
|
||||
this->_eventNames.emplace(index, eventName);
|
||||
LOG_TO_DEBUG("Event \"" + eventName + "\" was assigned eventIndex " + std::to_string(index));
|
||||
if (addIfNone)
|
||||
{
|
||||
auto index = this->_randomizer->GetNewEventID();
|
||||
this->_eventIndexes.emplace(eventName, index);
|
||||
this->_eventNames.emplace(index, eventName);
|
||||
LOG_TO_DEBUG("Event \"" + eventName + "\" was assigned eventIndex " + std::to_string(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Event \"" + eventName + "\" does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
return this->_eventIndexes.at(eventName);
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace randomizer::logic::world
|
||||
|
||||
int GetMacroIndex(const std::string& macroName) const;
|
||||
const randomizer::logic::requirement::Requirement& GetMacro(const int& macroIndex);
|
||||
int GetEventIndex(const std::string& eventName);
|
||||
int GetEventIndex(const std::string& eventName, bool addIfNone = true);
|
||||
std::string GetEventName(const int& eventIndex);
|
||||
|
||||
randomizer::seedgen::settings::Setting& Setting(const std::string& settingName);
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace randomizer::test::test
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
std::cout << "Test \"" << testName << "\" failed! Failed settings saved to " << SETTINGS_PATH << std::endl;
|
||||
std::cout << "Error Message: " << std::endl;
|
||||
std::cout << "Error Message: " << e.what() << std::endl;
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user