Dungeon Rewards Own Dungeon + Light Medallion Handling Refactor (#6500)

Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com>
This commit is contained in:
Sophia Caspe 2026-04-15 06:44:52 -07:00 committed by GitHub
parent 17a8f460e0
commit 12dddc5e8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 173 additions and 135 deletions

View file

@ -933,102 +933,96 @@ static void AssumedFill(const std::vector<RandomizerGet>& items, const std::vect
} while (unsuccessfulPlacement);
}
static std::vector<RandomizerGet> GetStonesInPool(std::vector<RandomizerGet> pool) {
return FilterFromPool(pool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() >= RG_KOKIRI_EMERALD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() <= RG_ZORA_SAPPHIRE;
});
}
static std::vector<RandomizerGet> GetMedallionsInPool(std::vector<RandomizerGet> pool) {
return FilterFromPool(pool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() >= RG_FOREST_MEDALLION &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() <= RG_LIGHT_MEDALLION;
});
}
// This function will specifically randomize dungeon rewards for the End of Dungeons
// setting, or randomize one dungeon reward to Link's Pocket if that setting is on
// RANDOTODO this function assumes only 1 of each reward can exist, fix it when starting items are refactored
static void RandomizeDungeonRewards() {
auto ctx = Rando::Context::GetInstance();
// End of Dungeons includes Link's Pocket
if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON) ||
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) {
// make temporary pools of stones and medallions, get rewards
std::vector<RandomizerGet> stones = FilterFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() >= RG_KOKIRI_EMERALD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() <= RG_ZORA_SAPPHIRE;
});
std::vector<RandomizerGet> medallions = FilterFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() >= RG_FOREST_MEDALLION &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() <= RG_LIGHT_MEDALLION;
});
std::vector<RandomizerGet> rewards = FilterAndEraseFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD;
});
if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS)
.Is(RO_DUNGEON_REWARDS_VANILLA)) { // Place dungeon rewards in vanilla locations
for (RandomizerCheck loc : Rando::StaticData::dungeonRewardLocations) {
ctx->GetItemLocation(loc)->PlaceVanillaItem();
}
ctx->GetItemLocation(RC_GIFT_FROM_RAURU)->PlaceVanillaItem();
} else { // Randomize dungeon rewards with assumed fill
std::vector rewardLocations(Rando::StaticData::dungeonRewardLocations);
// If there are less than 9 dungeon rewards, prioritize actual dungeons for placement
if (rewards.size() < 9) {
ctx->PlaceItemInLocation(RC_LINKS_POCKET, RG_GREEN_RUPEE);
} else {
if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).IsNot(RO_LINKS_POCKET_REWARD)) {
if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_STONE)) {
// get one stone
RandomizerGet startingStone = RandomElement(stones, true);
// erase from rewards so remaining are placed
erase_if(rewards, [&](RandomizerGet r) { return r == startingStone; });
ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingStone);
} else {
// get one medallion
RandomizerGet startingMedallion = RandomElement(medallions, true);
// erase from rewards so remaining are placed
erase_if(rewards, [&](RandomizerGet r) { return r == startingMedallion; });
ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingMedallion);
}
} else {
rewardLocations.push_back(RC_LINKS_POCKET);
}
}
AssumedFill(rewards, rewardLocations);
}
} else if (ctx->GetOption(RSK_LINKS_POCKET).Is(RO_LINKS_POCKET_DUNGEON_REWARD)) {
// make temporary pools of stones, medallions, and rewards
std::vector<RandomizerGet> stones = FilterFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() >= RG_KOKIRI_EMERALD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() <= RG_ZORA_SAPPHIRE;
});
std::vector<RandomizerGet> medallions = FilterFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() >= RG_FOREST_MEDALLION &&
Rando::StaticData::RetrieveItem(i).GetRandomizerGet() <= RG_LIGHT_MEDALLION;
});
std::vector<RandomizerGet> rewards = FilterFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD;
});
// If there are no remaining stones/medallions, then Link's pocket won't get one
if (rewards.empty()) {
ctx->PlaceItemInLocation(RC_LINKS_POCKET, RG_GREEN_RUPEE);
return;
}
if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_STONE)) {
// get one stone
RandomizerGet startingStone = RandomElement(stones, true);
ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingStone);
// erase stone from item pool
FilterAndEraseFromPool(itemPool, [startingStone](const RandomizerGet i) { return i == startingStone; });
} else if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_MEDALLION)) {
// get one medallion
RandomizerGet startingMedallion = RandomElement(medallions, true);
ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingMedallion);
// erase medallion from item pool
FilterAndEraseFromPool(itemPool,
[startingMedallion](const RandomizerGet i) { return i == startingMedallion; });
} else {
// get one reward
RandomizerGet startingReward = RandomElement(rewards, true);
ctx->PlaceItemInLocation(RC_LINKS_POCKET, startingReward);
// erase the stone/medallion from the Item Pool
FilterAndEraseFromPool(itemPool, [startingReward](const RandomizerGet i) { return i == startingReward; });
if (ctx->GetOption(RSK_LINKS_POCKET).Is(RO_LINKS_POCKET_DUNGEON_REWARD) && rewards.size() >= 9) {
RandomizerGet pocketItem = RG_GREEN_RUPEE;
std::vector<RandomizerGet> pocketPossibilities = {};
if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_ANY_STONE)) {
// get existing stones
pocketPossibilities = GetStonesInPool(rewards);
} else if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_LIGHT_MEDALLION)) {
// check if Light medallion exists
std::vector<RandomizerGet> lightMedallion = FilterFromPool(rewards, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetRandomizerGet() == RG_LIGHT_MEDALLION;
});
// If there are no light med, then Link's pocket can't get one
if (!lightMedallion.empty()) {
pocketPossibilities = { RG_LIGHT_MEDALLION };
}
} else if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_ANY_MEDALLION)) {
// get existing medallions
pocketPossibilities = GetMedallionsInPool(rewards);
} else if (ctx->GetOption(RSK_LINKS_POCKET_REWARD).Is(RO_LINKS_POCKET_ANY_REWARD)) {
// get all existing rewards
pocketPossibilities = rewards;
}
if (!pocketPossibilities.empty()) {
// get one stone
pocketItem = RandomElement(pocketPossibilities);
}
// erase from rewards so remaining are placed
erase_if(rewards, [&](RandomizerGet r) { return r == pocketItem; });
// and from the item pool so it's not placed twice
FilterAndEraseFromPool(itemPool, [pocketItem](const RandomizerGet i) { return i == pocketItem; });
// and add to the pocket
ctx->PlaceItemInLocation(RC_LINKS_POCKET, pocketItem);
}
// If we didn't place the Light Medallion on pocket, and we have rewards in their own dungeons or at the end of
// dungeons...
if ((ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA) ||
ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_OWN_DUNGEON)) &&
ctx->GetOption(RSK_LINKS_POCKET).IsNot(RO_LINKS_POCKET_DUNGEON_REWARD)) {
// place it on Gift From Rauru
ctx->GetItemLocation(RC_GIFT_FROM_RAURU)->PlaceVanillaItem();
// then erase from rewards so remaining are placed
erase_if(rewards, [&](RandomizerGet r) { return r == RG_LIGHT_MEDALLION; });
// and from the item pool so it's not placed twice
FilterAndEraseFromPool(itemPool, [](const RandomizerGet i) { return i == RG_LIGHT_MEDALLION; });
}
if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
// Randomize dungeon rewards with assumed fill
AssumedFill(rewards, Rando::StaticData::dungeonRewardLocations);
// Then remove them from the item pool
FilterAndEraseFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD;
});
} else if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_VANILLA)) {
for (RandomizerCheck loc : Rando::StaticData::dungeonRewardLocations) {
ctx->GetItemLocation(loc)->PlaceVanillaItem();
}
// Then remove rewards from the item pool
FilterAndEraseFromPool(itemPool, [](const auto i) {
return Rando::StaticData::RetrieveItem(i).GetItemType() == ITEMTYPE_DUNGEONREWARD;
});
}
}
@ -1078,6 +1072,12 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) {
});
AddElementsToPool(dungeonItems, dungeonSmallKeys);
}
if (ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Is(RO_DUNGEON_REWARDS_OWN_DUNGEON) &&
dungeon->GetReward() != RG_NONE) {
std::vector<RandomizerGet> dungeonReward =
FilterAndEraseFromPool(itemPool, [dungeon](const RandomizerGet i) { return (i == dungeon->GetReward()); });
AddElementsToPool(dungeonItems, dungeonReward);
}
if ((ctx->GetOption(RSK_BOSS_KEYSANITY).Is(RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) &&
dungeon->GetBossKey() != RG_GANONS_CASTLE_BOSS_KEY) ||
@ -1088,7 +1088,7 @@ static void RandomizeOwnDungeon(const Rando::DungeonInfo* dungeon) {
AddElementsToPool(dungeonItems, dungeonBossKey);
}
// randomize boss key and small keys together for even distribution
// randomize boss key, small keys, and rewards together for even distribution
AssumedFill(dungeonItems, dungeonLocations);
// randomize map and compass separately since they're not progressive

View file

@ -303,7 +303,7 @@ void GenerateItemPool() {
ctx->PlaceItemInLocation(RC_SONG_FROM_WINDMILL, RG_SONG_OF_STORMS, false, true);
}
bool rewardIceTraps = ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Get() >= RO_DUNGEON_REWARDS_ANY_DUNGEON;
bool rewardIceTraps = ctx->GetOption(RSK_SHUFFLE_DUNGEON_REWARDS).Get() >= RO_DUNGEON_REWARDS_OWN_DUNGEON;
AddFixedItemToPool(RG_KOKIRI_EMERALD, 1, rewardIceTraps);
AddFixedItemToPool(RG_GORON_RUBY, 1, rewardIceTraps);
AddFixedItemToPool(RG_ZORA_SAPPHIRE, 1, rewardIceTraps);

View file

@ -7,14 +7,16 @@
namespace Rando {
DungeonInfo::DungeonInfo(std::string name_, const RandomizerHintTextKey hintKey_, const RandomizerGet map_,
const RandomizerGet compass_, const RandomizerGet smallKey_, const RandomizerGet keyRing_,
const RandomizerGet bossKey_, RandomizerArea area_, const uint8_t vanillaKeyCount_,
const uint8_t mqKeyCount_, const RandomizerSettingKey mqSetting_)
const RandomizerGet bossKey_, RandomizerGet reward_, RandomizerArea area_,
const uint8_t vanillaKeyCount_, const uint8_t mqKeyCount_,
const RandomizerSettingKey mqSetting_)
: name(std::move(name_)), hintKey(hintKey_), map(map_), compass(compass_), smallKey(smallKey_), keyRing(keyRing_),
bossKey(bossKey_), area(area_), vanillaKeyCount(vanillaKeyCount_), mqKeyCount(mqKeyCount_),
bossKey(bossKey_), reward(reward_), area(area_), vanillaKeyCount(vanillaKeyCount_), mqKeyCount(mqKeyCount_),
mqSetting(mqSetting_) {
}
DungeonInfo::DungeonInfo()
: hintKey(RHT_NONE), map(RG_NONE), compass(RG_NONE), smallKey(RG_NONE), keyRing(RG_NONE), bossKey(RG_NONE) {
: hintKey(RHT_NONE), map(RG_NONE), compass(RG_NONE), smallKey(RG_NONE), keyRing(RG_NONE), bossKey(RG_NONE),
reward(RG_NONE) {
}
DungeonInfo::~DungeonInfo() = default;
@ -82,6 +84,10 @@ RandomizerGet DungeonInfo::GetBossKey() const {
return bossKey;
}
RandomizerGet DungeonInfo::GetReward() const {
return reward;
}
RandomizerSettingKey DungeonInfo::GetMQSetting() const {
return mqSetting;
}
@ -147,40 +153,44 @@ std::vector<RandomizerCheck> DungeonInfo::GetDungeonLocations() const {
Dungeons::Dungeons() {
dungeonList[DEKU_TREE] = DungeonInfo("Deku Tree", RHT_DEKU_TREE, RG_DEKU_TREE_MAP, RG_DEKU_TREE_COMPASS, RG_NONE,
RG_NONE, RG_NONE, RA_DEKU_TREE, 0, 0, RSK_MQ_DEKU_TREE);
RG_NONE, RG_NONE, RG_KOKIRI_EMERALD, RA_DEKU_TREE, 0, 0, RSK_MQ_DEKU_TREE);
dungeonList[DODONGOS_CAVERN] =
DungeonInfo("Dodongo's Cavern", RHT_DODONGOS_CAVERN, RG_DODONGOS_CAVERN_MAP, RG_DODONGOS_CAVERN_COMPASS,
RG_NONE, RG_NONE, RG_NONE, RA_DODONGOS_CAVERN, 0, 0, RSK_MQ_DODONGOS_CAVERN);
RG_NONE, RG_NONE, RG_NONE, RG_GORON_RUBY, RA_DODONGOS_CAVERN, 0, 0, RSK_MQ_DODONGOS_CAVERN);
dungeonList[JABU_JABUS_BELLY] =
DungeonInfo("Jabu Jabu's Belly", RHT_JABU_JABUS_BELLY, RG_JABU_JABUS_BELLY_MAP, RG_JABU_JABUS_BELLY_COMPASS,
RG_NONE, RG_NONE, RG_NONE, RA_JABU_JABUS_BELLY, 0, 0, RSK_MQ_JABU_JABU);
dungeonList[FOREST_TEMPLE] = DungeonInfo(
"Forest Temple", RHT_FOREST_TEMPLE, RG_FOREST_TEMPLE_MAP, RG_FOREST_TEMPLE_COMPASS, RG_FOREST_TEMPLE_SMALL_KEY,
RG_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_BOSS_KEY, RA_FOREST_TEMPLE, 5, 6, RSK_MQ_FOREST_TEMPLE);
RG_NONE, RG_NONE, RG_NONE, RG_ZORA_SAPPHIRE, RA_JABU_JABUS_BELLY, 0, 0, RSK_MQ_JABU_JABU);
dungeonList[FOREST_TEMPLE] =
DungeonInfo("Forest Temple", RHT_FOREST_TEMPLE, RG_FOREST_TEMPLE_MAP, RG_FOREST_TEMPLE_COMPASS,
RG_FOREST_TEMPLE_SMALL_KEY, RG_FOREST_TEMPLE_KEY_RING, RG_FOREST_TEMPLE_BOSS_KEY,
RG_FOREST_MEDALLION, RA_FOREST_TEMPLE, 5, 6, RSK_MQ_FOREST_TEMPLE);
dungeonList[FIRE_TEMPLE] = DungeonInfo("Fire Temple", RHT_FIRE_TEMPLE, RG_FIRE_TEMPLE_MAP, RG_FIRE_TEMPLE_COMPASS,
RG_FIRE_TEMPLE_SMALL_KEY, RG_FIRE_TEMPLE_KEY_RING, RG_FIRE_TEMPLE_BOSS_KEY,
RA_FIRE_TEMPLE, 8, 5, RSK_MQ_FIRE_TEMPLE);
dungeonList[WATER_TEMPLE] = DungeonInfo(
"Water Temple", RHT_WATER_TEMPLE, RG_WATER_TEMPLE_MAP, RG_WATER_TEMPLE_COMPASS, RG_WATER_TEMPLE_SMALL_KEY,
RG_WATER_TEMPLE_KEY_RING, RG_WATER_TEMPLE_BOSS_KEY, RA_WATER_TEMPLE, 6, 2, RSK_MQ_WATER_TEMPLE);
dungeonList[SPIRIT_TEMPLE] = DungeonInfo(
"Spirit Temple", RHT_SPIRIT_TEMPLE, RG_SPIRIT_TEMPLE_MAP, RG_SPIRIT_TEMPLE_COMPASS, RG_SPIRIT_TEMPLE_SMALL_KEY,
RG_SPIRIT_TEMPLE_KEY_RING, RG_SPIRIT_TEMPLE_BOSS_KEY, RA_SPIRIT_TEMPLE, 5, 7, RSK_MQ_SPIRIT_TEMPLE);
dungeonList[SHADOW_TEMPLE] = DungeonInfo(
"Shadow Temple", RHT_SHADOW_TEMPLE, RG_SHADOW_TEMPLE_MAP, RG_SHADOW_TEMPLE_COMPASS, RG_SHADOW_TEMPLE_SMALL_KEY,
RG_SHADOW_TEMPLE_KEY_RING, RG_SHADOW_TEMPLE_BOSS_KEY, RA_SHADOW_TEMPLE, 5, 6, RSK_MQ_SHADOW_TEMPLE);
RG_FIRE_MEDALLION, RA_FIRE_TEMPLE, 8, 5, RSK_MQ_FIRE_TEMPLE);
dungeonList[WATER_TEMPLE] =
DungeonInfo("Water Temple", RHT_WATER_TEMPLE, RG_WATER_TEMPLE_MAP, RG_WATER_TEMPLE_COMPASS,
RG_WATER_TEMPLE_SMALL_KEY, RG_WATER_TEMPLE_KEY_RING, RG_WATER_TEMPLE_BOSS_KEY, RG_WATER_MEDALLION,
RA_WATER_TEMPLE, 6, 2, RSK_MQ_WATER_TEMPLE);
dungeonList[SPIRIT_TEMPLE] =
DungeonInfo("Spirit Temple", RHT_SPIRIT_TEMPLE, RG_SPIRIT_TEMPLE_MAP, RG_SPIRIT_TEMPLE_COMPASS,
RG_SPIRIT_TEMPLE_SMALL_KEY, RG_SPIRIT_TEMPLE_KEY_RING, RG_SPIRIT_TEMPLE_BOSS_KEY,
RG_SPIRIT_MEDALLION, RA_SPIRIT_TEMPLE, 5, 7, RSK_MQ_SPIRIT_TEMPLE);
dungeonList[SHADOW_TEMPLE] =
DungeonInfo("Shadow Temple", RHT_SHADOW_TEMPLE, RG_SHADOW_TEMPLE_MAP, RG_SHADOW_TEMPLE_COMPASS,
RG_SHADOW_TEMPLE_SMALL_KEY, RG_SHADOW_TEMPLE_KEY_RING, RG_SHADOW_TEMPLE_BOSS_KEY,
RG_SHADOW_MEDALLION, RA_SHADOW_TEMPLE, 5, 6, RSK_MQ_SHADOW_TEMPLE);
dungeonList[BOTTOM_OF_THE_WELL] =
DungeonInfo("Bottom of the Well", RHT_BOTTOM_OF_THE_WELL, RG_BOTTOM_OF_THE_WELL_MAP,
RG_BOTTOM_OF_THE_WELL_COMPASS, RG_BOTTOM_OF_THE_WELL_SMALL_KEY, RG_BOTTOM_OF_THE_WELL_KEY_RING,
RG_NONE, RA_BOTTOM_OF_THE_WELL, 3, 2, RSK_MQ_BOTTOM_OF_THE_WELL);
RG_NONE, RG_NONE, RA_BOTTOM_OF_THE_WELL, 3, 2, RSK_MQ_BOTTOM_OF_THE_WELL);
dungeonList[ICE_CAVERN] = DungeonInfo("Ice Cavern", RHT_ICE_CAVERN, RG_ICE_CAVERN_MAP, RG_ICE_CAVERN_COMPASS,
RG_NONE, RG_NONE, RG_NONE, RA_ICE_CAVERN, 0, 0, RSK_MQ_ICE_CAVERN);
RG_NONE, RG_NONE, RG_NONE, RG_NONE, RA_ICE_CAVERN, 0, 0, RSK_MQ_ICE_CAVERN);
dungeonList[GERUDO_TRAINING_GROUND] = DungeonInfo(
"Gerudo Training Ground", RHT_GERUDO_TRAINING_GROUND, RG_NONE, RG_NONE, RG_GERUDO_TRAINING_GROUND_SMALL_KEY,
RG_GERUDO_TRAINING_GROUND_KEY_RING, RG_NONE, RA_GERUDO_TRAINING_GROUND, 9, 3, RSK_MQ_GTG);
dungeonList[GANONS_CASTLE] =
DungeonInfo("Ganon's Castle", RHT_GANONS_CASTLE, RG_NONE, RG_NONE, RG_GANONS_CASTLE_SMALL_KEY,
RG_GANONS_CASTLE_KEY_RING, RG_GANONS_CASTLE_BOSS_KEY, RA_GANONS_CASTLE, 2, 3, RSK_MQ_GANONS_CASTLE);
RG_GERUDO_TRAINING_GROUND_KEY_RING, RG_NONE, RG_NONE, RA_GERUDO_TRAINING_GROUND, 9, 3, RSK_MQ_GTG);
dungeonList[GANONS_CASTLE] = DungeonInfo(
"Ganon's Castle", RHT_GANONS_CASTLE, RG_NONE, RG_NONE, RG_GANONS_CASTLE_SMALL_KEY, RG_GANONS_CASTLE_KEY_RING,
RG_GANONS_CASTLE_BOSS_KEY, RG_NONE, RA_GANONS_CASTLE, 2, 3, RSK_MQ_GANONS_CASTLE);
}
Dungeons::~Dungeons() = default;

View file

@ -11,8 +11,8 @@ namespace Rando {
class DungeonInfo {
public:
DungeonInfo(std::string name_, RandomizerHintTextKey hintKey_, RandomizerGet map_, RandomizerGet compass_,
RandomizerGet smallKey_, RandomizerGet keyRing_, RandomizerGet bossKey_, RandomizerArea area_,
uint8_t vanillaKeyCount_, uint8_t mqKeyCount_, RandomizerSettingKey mqSetting_);
RandomizerGet smallKey_, RandomizerGet keyRing_, RandomizerGet bossKey_, RandomizerGet reward_,
RandomizerArea area_, uint8_t vanillaKeyCount_, uint8_t mqKeyCount_, RandomizerSettingKey mqSetting_);
DungeonInfo();
~DungeonInfo();
@ -32,6 +32,7 @@ class DungeonInfo {
RandomizerGet GetMap() const;
RandomizerGet GetCompass() const;
RandomizerGet GetBossKey() const;
RandomizerGet GetReward() const;
RandomizerSettingKey GetMQSetting() const;
void SetDungeonKnown(bool known);
void PlaceVanillaMap() const;
@ -50,6 +51,7 @@ class DungeonInfo {
RandomizerGet smallKey;
RandomizerGet keyRing;
RandomizerGet bossKey;
RandomizerGet reward;
RandomizerSettingKey mqSetting;
bool isDungeonModeKnown = true;
uint8_t vanillaKeyCount{};

View file

@ -804,6 +804,16 @@ void Settings::CreateOptionDescriptions() {
"of 20. The second one will upgrade this capacity to 30, and the final one will upgrade the capacity to the "
"usual 50.\n\n"
"Bombchu Bowling is opened by obtaining the first Bombchu bag.";
mOptionDescriptions[RSK_LINKS_POCKET] =
"Dungeon Reward - Link will start with a Spiritual Stone or Medallion, and specific options will open up\n\n"
"Advancement - Link will start with a useful item.\n\n"
"Anything - Link will start with a random item.\n\n"
"Nothing - Link will not start with a bonus item.";
mOptionDescriptions[RSK_LINKS_POCKET_REWARD] =
"Any Reward - Link starts with a random Spiritual Stone or Medallion\n\n"
"Stone - Link starts with a random Spiritual Stone.\n\n"
"Any Medallion - Link starts with a random Medallion.\n\n"
"Light Medallion - Link starts with the Light Medallion.";
mOptionDescriptions[RSK_ENABLE_BOMBCHU_DROPS] = "Once you obtain a Bombchu Bag, refills will sometimes replace "
"Bomb drops that would spawn."
"\n"

View file

@ -189,6 +189,7 @@ RANDO_ENUM_END(RandoOptionDungeonItemLocation)
RANDO_ENUM_BEGIN(RandoOptionDungeonRewards)
RANDO_ENUM_ITEM(RO_DUNGEON_REWARDS_VANILLA)
RANDO_ENUM_ITEM(RO_DUNGEON_REWARDS_END_OF_DUNGEON)
RANDO_ENUM_ITEM(RO_DUNGEON_REWARDS_OWN_DUNGEON)
RANDO_ENUM_ITEM(RO_DUNGEON_REWARDS_ANY_DUNGEON)
RANDO_ENUM_ITEM(RO_DUNGEON_REWARDS_OVERWORLD)
RANDO_ENUM_ITEM(RO_DUNGEON_REWARDS_ANYWHERE)
@ -415,9 +416,10 @@ RANDO_ENUM_END(RandoOptionLinksPocket)
// Link's Pocket Dungeon Reward Settings (dungeon reward, stone, medallion)
RANDO_ENUM_BEGIN(RandoOptionLinksPocketReward)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_REWARD)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_STONE)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_MEDALLION)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_ANY_REWARD)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_ANY_STONE)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_ANY_MEDALLION)
RANDO_ENUM_ITEM(RO_LINKS_POCKET_LIGHT_MEDALLION)
RANDO_ENUM_END(RandoOptionLinksPocketReward)
// Logic (glitchless/no logic)

View file

@ -564,7 +564,7 @@ void Settings::CreateOptions() {
OPT_U8(RSK_MQ_ICE_CAVERN, "Ice Cavern Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsIceCavern"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE);
OPT_U8(RSK_MQ_GTG, "Gerudo Training Ground Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGTG"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA, false, nullptr, IMFLAG_NONE);
OPT_U8(RSK_MQ_GANONS_CASTLE, "Ganon's Castle Quest", {"Vanilla", "Master Quest", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MQDungeonsGanonsCastle"), "", WIDGET_CVAR_COMBOBOX, RO_MQ_SET_VANILLA);
OPT_U8(RSK_SHUFFLE_DUNGEON_REWARDS, "Shuffle Dungeon Rewards", {"Vanilla", "End of Dungeons", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_REWARDS_END_OF_DUNGEON);
OPT_U8(RSK_SHUFFLE_DUNGEON_REWARDS, "Shuffle Dungeon Rewards", {"Vanilla", "End of Dungeons", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), mOptionDescriptions[RSK_SHUFFLE_DUNGEON_REWARDS], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_REWARDS_END_OF_DUNGEON);
OPT_CALLBACK(RSK_SHUFFLE_DUNGEON_REWARDS, {
// Link's Pocket - Disabled when Dungeon Rewards are shuffled to End of Dungeon
if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
@ -573,32 +573,39 @@ void Settings::CreateOptions() {
"This option is disabled because \"Dungeon Rewards\" are shuffled to \"End of Dungeons\".");
mOptions[RSK_LINKS_POCKET_REWARD].Enable();
mOptions[RSK_LINKS_POCKET_REWARD].Unhide();
} else if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
RO_DUNGEON_REWARDS_VANILLA) {
mOptions[RSK_LINKS_POCKET_REWARD].Disable("This option is disabled because \"Dungeon Rewards\" are shuffled to \"Vanilla\".");
mOptions[RSK_LINKS_POCKET_REWARD].Hide();
} else {
if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
RO_DUNGEON_REWARDS_OWN_DUNGEON) {
mOptions[RSK_LINKS_POCKET].Enable();
mOptions[RSK_LINKS_POCKET_REWARD].Disable(
"As \"Link's Pocket\" is set to \"Dungeon Reward\" while \"Dungeon Rewards\" is set to \"Own Dungeon\", Link's Pocket will always have the Light Medallion");
}else if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
RO_DUNGEON_REWARDS_VANILLA) {
mOptions[RSK_LINKS_POCKET].Enable();
mOptions[RSK_LINKS_POCKET_REWARD].Disable(
"As \"Link's Pocket\" is set to \"Dungeon Reward\" while \"Dungeon Rewards\" is set to \"Vanilla\", Link's Pocket will always have the Light Medallion");
} else {
mOptions[RSK_LINKS_POCKET].Enable();
mOptions[RSK_LINKS_POCKET_REWARD].Enable();
}
if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LinksPocket"), RO_LINKS_POCKET_DUNGEON_REWARD) == RO_LINKS_POCKET_DUNGEON_REWARD) {
mOptions[RSK_LINKS_POCKET_REWARD].Unhide();
} else {
mOptions[RSK_LINKS_POCKET_REWARD].Hide();
}
}
});
OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), "", WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_DUNGEON_REWARD);
OPT_U8(RSK_LINKS_POCKET, "Link's Pocket", {"Dungeon Reward", "Advancement", "Anything", "Nothing"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocket"), mOptionDescriptions[RSK_LINKS_POCKET], WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_DUNGEON_REWARD);
OPT_CALLBACK(RSK_LINKS_POCKET, {
// Only show the dungeon reward type if Link's Pocket is set to Dungeon Reward and Dungeon Rewards are not Vanilla, OR Dungeon Rewards are end of dungeon
if ((CVarGetInteger(CVAR_RANDOMIZER_SETTING("LinksPocket"), RO_LINKS_POCKET_DUNGEON_REWARD) ==
RO_LINKS_POCKET_DUNGEON_REWARD && CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) !=
RO_DUNGEON_REWARDS_VANILLA) || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) ==
RO_DUNGEON_REWARDS_END_OF_DUNGEON) {
if (CVarGetInteger(CVAR_RANDOMIZER_SETTING("LinksPocket"), RO_LINKS_POCKET_DUNGEON_REWARD) == RO_LINKS_POCKET_DUNGEON_REWARD ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleDungeonReward"), RO_DUNGEON_REWARDS_END_OF_DUNGEON) == RO_DUNGEON_REWARDS_END_OF_DUNGEON) {
mOptions[RSK_LINKS_POCKET_REWARD].Unhide();
} else {
mOptions[RSK_LINKS_POCKET_REWARD].Hide();
}
});
OPT_U8(RSK_LINKS_POCKET_REWARD, "Link's Pocket Reward Type", {"Dungeon Reward", "Stone", "Medallion"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocketReward"), "", WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_REWARD);
OPT_U8(RSK_LINKS_POCKET_REWARD, "Link's Pocket Reward Type", {"Any Reward", "Any Stone", "Any Medallion", "Light Medallion"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("LinksPocketReward"), mOptionDescriptions[RSK_LINKS_POCKET_REWARD], WIDGET_CVAR_COMBOBOX, RO_LINKS_POCKET_ANY_REWARD);
OPT_U8(RSK_SHUFFLE_SONGS, "Shuffle Songs", {"Off", "Song Locations", "Dungeon Rewards", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSongs"), mOptionDescriptions[RSK_SHUFFLE_SONGS], WIDGET_CVAR_COMBOBOX, RO_SONG_SHUFFLE_SONG_LOCATIONS);
OPT_U8(RSK_SHOPSANITY, "Shop Shuffle", {"Off", "Specific Count", "Random"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Shopsanity"), mOptionDescriptions[RSK_SHOPSANITY], WIDGET_CVAR_COMBOBOX, RO_SHOPSANITY_OFF);
OPT_CALLBACK(RSK_SHOPSANITY, {
@ -2519,6 +2526,13 @@ void Context::FinalizeSettings(const std::set<RandomizerCheck>& excludedLocation
if (mOptions[RSK_SHUFFLE_DUNGEON_REWARDS].Is(RO_DUNGEON_REWARDS_END_OF_DUNGEON)) {
mOptions[RSK_LINKS_POCKET].Set(RO_LINKS_POCKET_DUNGEON_REWARD);
} else if (mOptions[RSK_SHUFFLE_DUNGEON_REWARDS].Is(RO_DUNGEON_REWARDS_OWN_DUNGEON) ||
mOptions[RSK_SHUFFLE_DUNGEON_REWARDS].Is(RO_DUNGEON_REWARDS_VANILLA)) {
mOptions[RSK_LINKS_POCKET_REWARD].Set(RO_LINKS_POCKET_LIGHT_MEDALLION);
}
if (mOptions[RSK_LINKS_POCKET].IsNot(RO_LINKS_POCKET_DUNGEON_REWARD)) {
mOptions[RSK_LINKS_POCKET_REWARD].Set(RO_LINKS_POCKET_ANY_REWARD);
}
for (const auto locationKey : this->everyPossibleLocation) {