Shuffle Silver (#5659)

Co-authored-by: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com>
Co-authored-by: leggettc18 <chris@leggett.dev>
This commit is contained in:
Philip Dubé
2026-08-06 01:51:59 +00:00
committed by GitHub
parent edfdfe1338
commit b76e303ceb
58 changed files with 3332 additions and 1104 deletions
@@ -2,6 +2,8 @@
#include "randomizer_entrance_tracker.h"
#include "randomizer_item_tracker.h"
#include "randomizerTypes.h"
#include "soh/Enhancements/randomizer/randomizerEnums.h"
#include "soh/Enhancements/randomizer/static_data.h"
#include "soh/OTRGlobals.h"
#include "soh/SaveManager.h"
#include "soh/ResourceManagerHelpers.h"
@@ -67,6 +69,7 @@ bool showBeehives;
bool showCows;
bool showOverworldFreestanding;
bool showDungeonFreestanding;
bool showSilver;
bool showAdultTrade;
bool showKokiriSword;
bool showMasterSword;
@@ -358,6 +361,9 @@ std::map<RandomizerGet, RandomizerCheckArea> MapRGtoRandomizerCheckArea = {
{ RG_ICE_CAVERN_MAP, RCAREA_ICE_CAVERN }
};
// In the case that we get an excess key or silver rupee, it spoils the MQ status
// because it is not turned into a blue rupee as excess.
// For keyrings, we can count the keys on the viewmodel
void SpoilAreaFromCheck(RandomizerCheck rc) {
Rando::Location* loc = Rando::StaticData::GetLocation(rc);
Rando::ItemLocation* itemLoc = Rando::Context::GetInstance()->GetItemLocation(rc);
@@ -367,11 +373,177 @@ void SpoilAreaFromCheck(RandomizerCheck rc) {
SetAreaSpoiled(area);
}
}
if (itemLoc->GetPlacedItem().GetItemType() == ItemType::ITEMTYPE_SILVER) {
if (!Rando::StaticData::constantSilvers.contains(itemLoc->GetPlacedRandomizerGet()) &&
!IsAreaSpoiled(Rando::StaticData::silverToArea[itemLoc->GetPlacedRandomizerGet()])) {
SetAreaSpoiled(Rando::StaticData::silverToArea[itemLoc->GetPlacedRandomizerGet()]);
} else if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SILVER) ==
RO_SHUFFLE_SILVER_ON) {
switch (itemLoc->GetPlacedRandomizerGet()) {
case RG_SHADOW_SILVER_SPIKES:
if (*Randomizer::SilverFieldFromSaveContext(&gSaveContext, itemLoc->GetPlacedRandomizerGet()) >=
6) {
SetAreaSpoiled(RCAREA_SHADOW_TEMPLE);
}
break;
case RG_GTG_SILVER_LAVA:
if (*Randomizer::SilverFieldFromSaveContext(&gSaveContext, itemLoc->GetPlacedRandomizerGet()) >=
6) {
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
}
break;
case RG_GTG_SILVER_WATER:
if (*Randomizer::SilverFieldFromSaveContext(&gSaveContext, itemLoc->GetPlacedRandomizerGet()) >=
4) {
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
}
break;
default:
break;
}
}
} else if (itemLoc->GetPlacedItem().GetItemType() == ItemType::ITEMTYPE_SMALLKEY) {
switch (itemLoc->GetPlacedRandomizerGet()) {
case RG_FOREST_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::FOREST_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) >= 6) {
SetAreaSpoiled(RCAREA_FOREST_TEMPLE);
}
break;
case RG_FIRE_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::FIRE_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) >= 6) {
SetAreaSpoiled(RCAREA_FIRE_TEMPLE);
}
break;
case RG_WATER_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::WATER_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) >= 3) {
SetAreaSpoiled(RCAREA_WATER_TEMPLE);
}
break;
case RG_SPIRIT_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::SPIRIT_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) >= 6) {
SetAreaSpoiled(RCAREA_SPIRIT_TEMPLE);
}
break;
case RG_SHADOW_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::SHADOW_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) >= 6) {
SetAreaSpoiled(RCAREA_SHADOW_TEMPLE);
}
break;
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::BOTTOM_OF_THE_WELL)
->GetTotalSmallKeys(&gSaveContext) >= 3) {
SetAreaSpoiled(RCAREA_BOTTOM_OF_THE_WELL);
}
break;
case RG_GERUDO_TRAINING_GROUND_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::GERUDO_TRAINING_GROUND)
->GetTotalSmallKeys(&gSaveContext) >= 4) {
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
}
break;
case RG_FOREST_TEMPLE_KEY_RING:
SetAreaSpoiled(RCAREA_FOREST_TEMPLE);
break;
case RG_FIRE_TEMPLE_KEY_RING:
SetAreaSpoiled(RCAREA_FIRE_TEMPLE);
break;
case RG_WATER_TEMPLE_KEY_RING:
SetAreaSpoiled(RCAREA_WATER_TEMPLE);
break;
case RG_SPIRIT_TEMPLE_KEY_RING:
SetAreaSpoiled(RCAREA_SPIRIT_TEMPLE);
break;
case RG_SHADOW_TEMPLE_KEY_RING:
SetAreaSpoiled(RCAREA_SHADOW_TEMPLE);
break;
case RG_BOTTOM_OF_THE_WELL_KEY_RING:
SetAreaSpoiled(RCAREA_BOTTOM_OF_THE_WELL);
break;
case RG_GERUDO_TRAINING_GROUND_KEY_RING:
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
break;
default:
break;
}
}
if (!IsAreaSpoiled(loc->GetArea())) {
SetAreaSpoiled(loc->GetArea());
}
}
void SpoilAreaFromCantObtain(RandomizerGet rg) {
// only spoil if it wouldn't transform anyway, in case someone manages to glitch this value
switch (rg) {
case RG_FOREST_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::FOREST_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) < 6) {
SetAreaSpoiled(RCAREA_FOREST_TEMPLE);
}
break;
case RG_FIRE_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::FIRE_TEMPLE)->GetTotalSmallKeys(&gSaveContext) <
8) {
SetAreaSpoiled(RCAREA_FIRE_TEMPLE);
}
break;
case RG_WATER_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::WATER_TEMPLE)->GetTotalSmallKeys(&gSaveContext) <
6) {
SetAreaSpoiled(RCAREA_WATER_TEMPLE);
}
break;
case RG_SPIRIT_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::SPIRIT_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) < 7) {
SetAreaSpoiled(RCAREA_SPIRIT_TEMPLE);
}
break;
case RG_SHADOW_TEMPLE_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::SHADOW_TEMPLE)
->GetTotalSmallKeys(&gSaveContext) < 6) {
SetAreaSpoiled(RCAREA_SHADOW_TEMPLE);
}
break;
case RG_BOTTOM_OF_THE_WELL_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::BOTTOM_OF_THE_WELL)
->GetTotalSmallKeys(&gSaveContext) < 3) {
SetAreaSpoiled(RCAREA_BOTTOM_OF_THE_WELL);
}
break;
case RG_GERUDO_TRAINING_GROUND_SMALL_KEY:
if (OTRGlobals::Instance->gRandoContext->GetDungeon(Rando::GERUDO_TRAINING_GROUND)
->GetTotalSmallKeys(&gSaveContext) < 9) {
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
}
break;
case RG_SHADOW_SILVER_SPIKES:
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SILVER) == RO_SHUFFLE_SILVER_ON &&
*Randomizer::SilverFieldFromSaveContext(&gSaveContext, rg) < 10) {
SetAreaSpoiled(RCAREA_SHADOW_TEMPLE);
}
break;
case RG_GTG_SILVER_LAVA:
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SILVER) == RO_SHUFFLE_SILVER_ON &&
*Randomizer::SilverFieldFromSaveContext(&gSaveContext, rg) < 6) {
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
}
break;
case RG_GTG_SILVER_WATER:
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SILVER) == RO_SHUFFLE_SILVER_ON &&
*Randomizer::SilverFieldFromSaveContext(&gSaveContext, rg) < 5) {
SetAreaSpoiled(RCAREA_GERUDO_TRAINING_GROUND);
}
break;
default:
return;
}
}
void RecalculateAllAreaTotals() {
for (auto& [rcArea, checks] : checksByArea) {
if (rcArea == RCAREA_INVALID) {
@@ -629,6 +801,7 @@ void CheckTrackerLoadGame(int32_t fileNum) {
}
for (int i = RCAREA_KOKIRI_FOREST; i < RCAREA_INVALID; i++) {
if (!IsAreaSpoiled(static_cast<RandomizerCheckArea>(i)) &&
(RandomizerCheckObjects::AreaIsOverworld(static_cast<RandomizerCheckArea>(i)) || !IS_RANDO ||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_NONE ||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) ==
@@ -905,11 +1078,10 @@ void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if ((!IS_RANDO && ((loc.GetQuest() == RCQUEST_MQ && !IS_MASTER_QUEST) ||
(loc.GetQuest() == RCQUEST_VANILLA && IS_MASTER_QUEST))) ||
(IS_RANDO &&
!(OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene()) == nullptr) &&
((OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene())->IsMQ() &&
(IS_RANDO && !(OTRGlobals::Instance->gRandoContext->GetDungeonFromScene(loc.GetScene()) == nullptr) &&
((OTRGlobals::Instance->gRandoContext->GetDungeonFromScene(loc.GetScene())->IsMQ() &&
loc.GetQuest() == RCQUEST_VANILLA) ||
OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene())->IsVanilla() &&
OTRGlobals::Instance->gRandoContext->GetDungeonFromScene(loc.GetScene())->IsVanilla() &&
loc.GetQuest() == RCQUEST_MQ))) {
continue;
}
@@ -1044,9 +1216,17 @@ bool IsAreaSpoiled(RandomizerCheckArea rcArea) {
return areasSpoiled & (1 << rcArea);
}
// we don't care how many silvers for this check, only that all possible silver items are known
bool AreAllSilversSpoiled() {
return IsAreaSpoiled(RCAREA_DODONGOS_CAVERN) && IsAreaSpoiled(RCAREA_BOTTOM_OF_THE_WELL) &&
IsAreaSpoiled(RCAREA_GANONS_CASTLE) && IsAreaSpoiled(RCAREA_SHADOW_TEMPLE) &&
IsAreaSpoiled(RCAREA_SPIRIT_TEMPLE) && IsAreaSpoiled(RCAREA_ICE_CAVERN);
}
void SetAreaSpoiled(RandomizerCheckArea rcArea) {
areasSpoiled |= (1 << rcArea);
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
RefreshItemTrackerMainWindow();
}
void InternalRecalculateAvailableChecks(RandomizerRegion startingRegion, RandoAgeTime startingAgeTime);
@@ -1325,8 +1505,7 @@ void CheckTrackerWindow::DrawElement() {
areaTotalsTooltipSS << "Checked / Total";
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
if (OTRGlobals::Instance->gRandoContext->GetDungeons()
->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))
if (OTRGlobals::Instance->gRandoContext->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))
->IsMQ()) {
areaTotalsSS << " - MQ";
} else {
@@ -1701,6 +1880,8 @@ void LoadSettings() {
showDungeonFreestanding = false;
break;
}
showSilver = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SILVER);
} else { // Vanilla
showOverworldFreestanding = false;
showDungeonFreestanding = true;
@@ -1796,6 +1977,7 @@ bool IsCheckShuffled(RandomizerCheck rc) {
(loc->GetRCType() != RCTYPE_FREESTANDING ||
(showOverworldFreestanding && RandomizerCheckObjects::AreaIsOverworld(loc->GetArea())) ||
(showDungeonFreestanding && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()))) &&
(loc->GetRCType() != RCTYPE_SILVER || showSilver) &&
(loc->GetRCType() != RCTYPE_ADULT_TRADE || showAdultTrade ||
rc == RC_KAK_ANJU_AS_ADULT || // adult trade checks that are always shuffled
rc == RC_DMT_TRADE_CLAIM_CHECK // even when shuffle adult trade is off