Skip to content
29 changes: 29 additions & 0 deletions soh/include/z64save.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,35 @@ typedef struct {
typedef struct ShipRandomizerSaveContextData {
u8 triforcePiecesCollected;
u8 bombchuUpgradeLevel;
s8 silverShadowBlades;
s8 silverShadowPit;
s8 silverShadowSpikes;
s8 silverSpiritChild;
s8 silverSpiritSun;
s8 silverSpiritBoulders;
s8 silverBotw;
s8 silverIceCavernBlades;
s8 silverIceCavernBlock;
s8 silverGtgSlope;
s8 silverGtgLava;
s8 silverGtgWater;
s8 silverGanonLight;
s8 silverGanonForest;
s8 silverGanonFire;
s8 silverGanonSpirit;
s8 silverMqDodongosCavern;
s8 silverMqShadowBlades;
s8 silverMqShadowPit;
s8 silverMqShadowInvisibleBlades;
s8 silverMqShadowSpikes;
s8 silverMqSpiritLobby;
s8 silverMqSpiritBigWall;
s8 silverMqGtgSlope;
s8 silverMqGtgLava;
s8 silverMqGtgWater;
s8 silverMqGanonFire;
s8 silverMqGanonWater;
s8 silverMqGanonShadow;
} ShipRandomizerSaveContextData;

typedef struct ShipBossRushSaveContextData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2214,6 +2214,30 @@ typedef enum {
// - None
VB_SHOW_TITLE_CARD,

// #### `result`
// ```c
// this->actor.xyzDistToPlayerSq < 900.0f
// ```
// #### `args`
// - *EnGSwitch
VB_SILVER_COLLECT,

// #### `result`
// ```c
// true
// ```
// #### `args`
// - *EnGSwitch
VB_SILVER_COUNT_CHECK,

// #### `result`
// ```c
// Flags_GetSwitch(play, this->switchFlag)
// ```
// #### `args`
// - *EnGSwitch
VB_SILVER_DESPAWN,

// #### `result`
// ```c
// true
Expand Down
4 changes: 2 additions & 2 deletions soh/soh/Enhancements/randomizer/3drando/fill.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,8 @@ bool AddCheckToLogic(LocationAccess& locPair, GetAccessibleLocationsStruct& gals
RandomizerGet locItem = location->GetPlacedRandomizerGet();
RandomizerCheckQuest quest = Rando::StaticData::GetLocation(loc)->GetQuest();
assert(ctx->GetOption(RSK_LOGIC_RULES).Is(RO_LOGIC_NO_LOGIC) || quest == RCQUEST_BOTH ||
(quest == RCQUEST_VANILLA && ctx->GetDungeons()->GetDungeonFromScene(parentRegion->scene)->IsVanilla()) ||
(quest == RCQUEST_MQ && ctx->GetDungeons()->GetDungeonFromScene(parentRegion->scene)->IsMQ()));
(quest == RCQUEST_VANILLA && ctx->GetDungeonFromScene(parentRegion->scene)->IsVanilla()) ||
(quest == RCQUEST_MQ && ctx->GetDungeonFromScene(parentRegion->scene)->IsMQ()));

if (!location->IsAddedToPool() && locPair.ConditionsMet(parentRegion, logic->CalculatingAvailableChecks)) {
location->AddToPool();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ namespace Rando {
void StaticData::HintTable_Init_Exclude_Dungeon() {
// clang-format off

// TODO move these to region specific sections when hint text stable
hintTextTable[RHT_DODONGOS_CAVERN_SILVER] = HintText(CustomMessage("They say that silver in #Dodongo's Cavern# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_SHADOW_TEMPLE_SILVER] = HintText(CustomMessage("They say that silver in #Shadow Temple# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_SPIRIT_TEMPLE_SILVER] = HintText(CustomMessage("They say that silver in #Spirit Temple# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_BOTW_SILVER] = HintText(CustomMessage("They say that silver in #Bottom of the Well# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_ICE_CAVERN_SILVER] = HintText(CustomMessage("They say that silver in #Ice Cavern# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_GTG_SILVER] = HintText(CustomMessage("They say that silver in #Gerudo Training Ground# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_GANONS_CASTLE_SILVER] = HintText(CustomMessage("They say that silver in #Ganon's Castle# holds #[[1]]#.", TODO_TRANSLATE, TODO_TRANSLATE, {QM_RED, QM_GREEN}));

/*--------------------------
| DEKU TREE |
---------------------------*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2108,6 +2108,8 @@ void StaticData::HintTable_Init_Item() {
}, {
CustomMessage("sack of mice", /*german*/"ein Sack Mäuse", /*french*/"un Sac rempli de souris")});

hintTextTable[RHT_SILVER] = HintText(CustomMessage("a Silver Rupee", /*german*/ TODO_TRANSLATE, /*french*/ TODO_TRANSLATE));

hintTextTable[RHT_SKELETON_KEY] = HintText(CustomMessage("a Skeleton Key", /*german*/ "der Skelettschlüssel", /*french*/ "une Clé Squelette"),
// /*spanish*/una Llave Maestra
{
Expand Down
74 changes: 70 additions & 4 deletions soh/soh/Enhancements/randomizer/3drando/item_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,12 @@ static void PlaceItemsForType(RandomizerCheckType rctype, bool overworldActive =
if (currentQuest == RCQUEST_BOTH) {
AddFixedItemToPool(loc->GetVanillaItem(), 1, false);
} else {
// Check if current item's dungeon is vanilla or MQ, and only add if quest corresponds to it.
// Check if current item's dungeon is vanilla or MQ, and only add if quest corresponds to it
SceneID itemScene = loc->GetScene();
auto dungeon = ctx->GetDungeonFromScene(itemScene);

if (itemScene >= SCENE_DEKU_TREE && itemScene <= SCENE_GERUDO_TRAINING_GROUND) {
bool isMQ = ctx->GetDungeon(itemScene)->IsMQ();
if (dungeon != nullptr) {
bool isMQ = dungeon->IsMQ();

if ((isMQ && currentQuest == RCQUEST_MQ) || (!isMQ && currentQuest == RCQUEST_VANILLA)) {
AddFixedItemToPool(loc->GetVanillaItem(), 1, false);
Expand Down Expand Up @@ -218,7 +219,7 @@ void GenerateItemPool() {
1 + infiniteProgressive);
//clang-format on

int extraWallets =(ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET) ? 1 : 0) + (ctx->GetOption(RSK_INCLUDE_TYCOON_WALLET) ? 1 : 0);
int extraWallets = (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET) ? 1 : 0) + (ctx->GetOption(RSK_INCLUDE_TYCOON_WALLET) ? 1 : 0);
AddItemToPool(RG_PROGRESSIVE_WALLET, 3 + infiniteProgressive + extraWallets,
2 + infiniteProgressive + extraWallets,
2 + infiniteProgressive + extraWallets,
Expand Down Expand Up @@ -864,6 +865,71 @@ void GenerateItemPool() {
}
}

bool silverActive = ctx->GetOption(RSK_SHUFFLE_SILVER).Get();
if (silverActive) {
PlaceItemsForType(RCTYPE_SILVER, silverActive, silverActive);
}

if (ctx->GetOption(RSK_SHUFFLE_SILVER).Is(RO_SHUFFLE_SILVER_ON) ||
ctx->GetOption(RSK_SHUFFLE_SILVER).Is(RO_SHUFFLE_SILVER_WALLET)) {
ctx->possibleIceTrapModels.insert(RG_SHADOW_SILVER_BLADES); // ice traps reroll this into a random silver rupee
bool isWallet = ctx->GetOption(RSK_SHUFFLE_SILVER).Is(RO_SHUFFLE_SILVER_WALLET);
auto dungeons = ctx->GetDungeons();
if (dungeons->GetDungeonFromScene(SCENE_DODONGOS_CAVERN)->IsMQ()) {
AddFixedItemToPool(RG_DODONGOS_CAVERN_MQ_SILVER, isWallet ? 1 : 5, false);
}

if (dungeons->GetDungeonFromScene(SCENE_SHADOW_TEMPLE)->IsVanilla()) {
AddFixedItemToPool(RG_SHADOW_SILVER_BLADES, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SHADOW_SILVER_PIT, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SHADOW_SILVER_SPIKES, isWallet ? 1 : 5, false);
} else {
AddFixedItemToPool(RG_SHADOW_MQ_SILVER_BLADES, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SHADOW_MQ_SILVER_PIT, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SHADOW_MQ_SILVER_INVISIBLE_BLADES, isWallet ? 1 : 10, false);
AddFixedItemToPool(RG_SHADOW_MQ_SILVER_SPIKES, isWallet ? 1 : 10, false);
}

if (dungeons->GetDungeonFromScene(SCENE_SPIRIT_TEMPLE)->IsVanilla()) {
AddFixedItemToPool(RG_SPIRIT_SILVER_CHILD, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SPIRIT_SILVER_SUN, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SPIRIT_SILVER_BOULDERS, isWallet ? 1 : 5, false);
} else {
AddFixedItemToPool(RG_SPIRIT_MQ_SILVER_LOBBY, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_SPIRIT_MQ_SILVER_BIG_WALL, isWallet ? 1 : 5, false);
}

if (dungeons->GetDungeonFromScene(SCENE_BOTTOM_OF_THE_WELL)->IsVanilla()) {
AddFixedItemToPool(RG_BOTW_SILVER, isWallet ? 1 : 5, false);
}

if (dungeons->GetDungeonFromScene(SCENE_ICE_CAVERN)->IsVanilla()) {
AddFixedItemToPool(RG_ICE_CAVERN_SILVER_BLADES, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_ICE_CAVERN_SILVER_BLOCK, isWallet ? 1 : 5, false);
}

if (dungeons->GetDungeonFromScene(SCENE_GERUDO_TRAINING_GROUND)->IsVanilla()) {
AddFixedItemToPool(RG_GTG_SILVER_SLOPE, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GTG_SILVER_LAVA, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GTG_SILVER_WATER, isWallet ? 1 : 5, false);
} else {
AddFixedItemToPool(RG_GTG_MQ_SILVER_SLOPE, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GTG_MQ_SILVER_LAVA, isWallet ? 1 : 6, false);
AddFixedItemToPool(RG_GTG_MQ_SILVER_WATER, isWallet ? 1 : 3, false);
}

if (dungeons->GetDungeonFromScene(SCENE_INSIDE_GANONS_CASTLE)->IsVanilla()) {
AddFixedItemToPool(RG_GANONS_CASTLE_SILVER_LIGHT, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GANONS_CASTLE_SILVER_FOREST, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GANONS_CASTLE_SILVER_FIRE, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GANONS_CASTLE_SILVER_SPIRIT, isWallet ? 1 : 5, false);
} else {
AddFixedItemToPool(RG_GANONS_CASTLE_MQ_SILVER_FIRE, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GANONS_CASTLE_MQ_SILVER_WATER, isWallet ? 1 : 5, false);
AddFixedItemToPool(RG_GANONS_CASTLE_MQ_SILVER_SHADOW, isWallet ? 1 : 5, false);
}
}

int maxHearts = 20;
switch (ctx->GetOption(RSK_ITEM_POOL).Get()) {
case RO_ITEM_POOL_PLENTIFUL:
Expand Down
2 changes: 1 addition & 1 deletion soh/soh/Enhancements/randomizer/3drando/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ bool GenerateRandomizer(std::set<RandomizerCheck> excludedLocations, std::set<Ra
ctx->ClearItemLocations();
int ret = Playthrough::Playthrough_Init(ctx->GetSeed(), excludedLocations, enabledTricks);
if (ret < 0) {
if (ret == -1) { // Failed to generate after 5 tries
if (ret == -1) {
SPDLOG_ERROR("Failed to generate after 5 tries.");
return false;
} else {
Expand Down
15 changes: 6 additions & 9 deletions soh/soh/Enhancements/randomizer/3drando/playthrough.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "../location_access.h"
#include "random.hpp"
#include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/randomizer/settings.h"
#include "variables.h"
#include "soh/cvar_prefixes.h"
Expand Down Expand Up @@ -72,14 +71,12 @@ int Playthrough_Init(uint32_t seed, std::set<RandomizerCheck> excludedLocations,

GenerateHash();

if (true) {
// TODO: Handle different types of file output (Spoiler Log, Plando Template, Patch Files, Race Files, etc.)
SPDLOG_INFO("Writing Spoiler Log...");
StartPerformanceTimer(PT_SPOILER_LOG);
SpoilerLog_Write();
StopPerformanceTimer(PT_SPOILER_LOG);
SPDLOG_INFO("Writing Spoiler Log Done");
}
// TODO: Handle different types of file output (Spoiler Log, Plando Template, Patch Files, Race Files, etc.)
SPDLOG_INFO("Writing Spoiler Log...");
StartPerformanceTimer(PT_SPOILER_LOG);
SpoilerLog_Write();
StopPerformanceTimer(PT_SPOILER_LOG);
SPDLOG_INFO("Writing Spoiler Log Done");

ctx->playthroughLocations.clear();
ctx->playthroughBeatable = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ void GenerateStartingInventory() {
AddItemToInventory(RG_SHADOW_TEMPLE_BOSS_KEY);
}

if (ctx->GetOption(RSK_SHUFFLE_SILVER).Is(RO_SHUFFLE_SILVER_STARTWITH)) {
for (int rg = (int)RG_SHADOW_SILVER_BLADES; rg <= (int)RG_GANONS_CASTLE_MQ_SILVER_SHADOW; rg++) {
AddItemToInventory((RandomizerGet)rg);
}
}

// Add Ganon's Boss key with Triforce Hunt's Win setting so the game thinks it's obtainable from the start.
// During save init, the boss key isn't actually given and it's instead given when completing the triforce.
if (ctx->GetOption(RSK_GANONS_BOSS_KEY).Is(RO_GANON_BOSS_KEY_STARTWITH) ||
Expand Down
5 changes: 5 additions & 0 deletions soh/soh/Enhancements/randomizer/SeedContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ void Context::GenerateLocationPool() {
mOptions[RSK_SHUFFLE_WONDER_ITEMS].Is(RO_SHUFFLE_WONDER_ITEMS_OFF)) ||
(location.GetRCType() == RCTYPE_FREESTANDING &&
mOptions[RSK_SHUFFLE_FREESTANDING].Is(RO_SHUFFLE_FREESTANDING_OFF)) ||
(location.GetRCType() == RCTYPE_SILVER && !mOptions[RSK_SHUFFLE_SILVER]) ||
(location.GetRCType() == RCTYPE_BEEHIVE && !mOptions[RSK_SHUFFLE_BEEHIVES])) {
continue;
}
Expand Down Expand Up @@ -524,6 +525,10 @@ DungeonInfo* Context::GetDungeon(size_t key) const {
return mDungeons->GetDungeon(static_cast<DungeonKey>(key));
}

DungeonInfo* Context::GetDungeonFromScene(SceneID scene) const {
return mDungeons->GetDungeonFromScene(scene);
}

std::shared_ptr<Logic> Context::GetLogic() {
if (mLogic.get() == nullptr) {
mLogic = std::make_shared<Logic>();
Expand Down
1 change: 1 addition & 0 deletions soh/soh/Enhancements/randomizer/SeedContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class Context {
std::shared_ptr<Dungeons> GetDungeons();
std::shared_ptr<Fishsanity> GetFishsanity();
DungeonInfo* GetDungeon(size_t key) const;
DungeonInfo* GetDungeonFromScene(SceneID key) const;
std::shared_ptr<Logic> GetLogic();
std::shared_ptr<Trials> GetTrials();
std::shared_ptr<Kaleido> GetKaleido();
Expand Down
Loading
Loading