Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
82b00c2
Re Add All the branch (but no Shuffle Silver Rupee) | V1.0
TheLynk Nov 17, 2025
272f223
Add Branch "Shuffle Roll" | Version : 2.0
TheLynk Nov 18, 2025
7cb8893
Fix Text in Item tracker and Logic for ZR | V:2.1
TheLynk Nov 19, 2025
b5eb52e
Fix Logic in KF | 2.2
TheLynk Nov 22, 2025
9b52cba
Add Branch add the ability to Randomize Music (Seeded) | V:3.0
TheLynk Nov 24, 2025
bd31a0e
Add branch "Invisible Navi option" | V:4.0
TheLynk Nov 25, 2025
8259c4d
Add branch "Rando: Add Triforce Hunt GBK setting" | V:5.0
TheLynk Nov 30, 2025
15b75a7
Add branch "Add Edit buttons for custom presets" | V6.0
TheLynk Dec 1, 2025
b97a2fb
Add branch "Check tracker improvements" | V:7.0
TheLynk Dec 1, 2025
b397099
Add branch "Improvements to Custom Kaleido Menu" | V:8.0
TheLynk Dec 1, 2025
042b163
Fix Logic | V:8.1
TheLynk Dec 2, 2025
19d7c41
Fix Logic Shuffle Bean Souls | V:8.2
TheLynk Dec 4, 2025
1752085
Update model for Shuffle Roll | V:8.3
TheLynk Dec 7, 2025
6f2fb97
Merge branch 'develop' into V3-Anchor-Fusion-All-New-Option
TheLynk Dec 8, 2025
cc4695f
Fix Update Auto Merge | V:9.0
TheLynk Dec 8, 2025
a64c1c7
Fix crash Logic | V:9.1
TheLynk Dec 8, 2025
6940160
Add branch "rocksanity" | V:10.0
TheLynk Dec 8, 2025
c142b67
clang-format
TheLynk Dec 8, 2025
cd7d871
Fix Logic | V:10.1
TheLynk Dec 12, 2025
a9af840
Merge branch 'develop' into V3-Anchor-Fusion-All-New-Option
PABessero Jan 4, 2026
94f560e
Major Update | V:11
TheLynk Jan 14, 2026
31e719f
Merge branch 'develop' into V3-Anchor-Fusion-All-New-Option
TheLynk Jan 14, 2026
e0348c0
Good Luck Update | V:12
TheLynk Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions soh/soh/Enhancements/Cheats/NoLikeLikeItemSteal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <libultraship/bridge.h>
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"

extern "C" {
#include "macros.h"
}

static constexpr int32_t CVAR_NOLIKELIKEITEMSTEAL_DEFAULT = 0;
#define CVAR_NOLIKELIKEITEMSTEAL_NAME CVAR_CHEAT("NoLikeLikeItemSteal")
#define CVAR_NOLIKELIKEITEMSTEAL_VALUE CVarGetInteger(CVAR_NOLIKELIKEITEMSTEAL_NAME, CVAR_NOLIKELIKEITEMSTEAL_DEFAULT)

void RegisterNoLikeLikeItemSteal() {
COND_VB_SHOULD(VB_LIKE_LIKE_STEAL_EQUIPMENT, CVAR_NOLIKELIKEITEMSTEAL_VALUE, { *should = false; });
}

static RegisterShipInitFunc initFunc(RegisterNoLikeLikeItemSteal, { CVAR_NOLIKELIKEITEMSTEAL_NAME });
17 changes: 17 additions & 0 deletions soh/soh/Enhancements/DropThrowOnlyActors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"

void OnAllowThrowOnlyDrop(void* actor) {
Actor* thisx = (Actor*)actor;
if (CVarGetInteger(CVAR_ENHANCEMENT("DropThrowOnlyObjects"), 0)) {
thisx->flags &= ~ACTOR_FLAG_THROW_ONLY;
} else {
thisx->flags |= ACTOR_FLAG_THROW_ONLY;
}
}

void RegisterAllowThrowOnlyDrop() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>(OnAllowThrowOnlyDrop);
}

static RegisterShipInitFunc initFunc(RegisterAllowThrowOnlyDrop, { CVAR_ENHANCEMENT("DropThrowOnlyObjects") });
221 changes: 215 additions & 6 deletions soh/soh/Enhancements/ExtraTraps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/Notification/Notification.h"
#include "soh/OTRGlobals.h"
#include "soh/SohGui/ImGuiUtils.h"
#include "soh/SaveManager.h"

extern "C" {
#include "variables.h"
#include "functions.h"
#include "macros.h"
extern PlayState* gPlayState;
GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID);
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
RandomizerGet RetrieveRandomizerGetFromItemID(ItemID itemID);
}

#define CVAR_EXTRA_TRAPS_NAME CVAR_ENHANCEMENT("ExtraTraps.Enabled")
Expand All @@ -27,19 +31,49 @@ typedef enum {
ADD_AMMO_TRAP,
ADD_KILL_TRAP,
ADD_TELEPORT_TRAP,
ADD_POCKET_TRAP,
ADD_PERMADEATH_TRAP,
ADD_TRAP_MAX
} AltTrapType;

static AltTrapType roll = ADD_TRAP_MAX;
static int statusTimer = -1;
static int eventTimer = -1;
static int permaDeathTimer = -1;
bool shouldFileDelete = false;

const char* altTrapTypeCvars[] = {
CVAR_ENHANCEMENT("ExtraTraps.Ice"), CVAR_ENHANCEMENT("ExtraTraps.Burn"),
CVAR_ENHANCEMENT("ExtraTraps.Shock"), CVAR_ENHANCEMENT("ExtraTraps.Knockback"),
CVAR_ENHANCEMENT("ExtraTraps.Speed"), CVAR_ENHANCEMENT("ExtraTraps.Bomb"),
CVAR_ENHANCEMENT("ExtraTraps.Void"), CVAR_ENHANCEMENT("ExtraTraps.Ammo"),
CVAR_ENHANCEMENT("ExtraTraps.Kill"), CVAR_ENHANCEMENT("ExtraTraps.Teleport"),
CVAR_ENHANCEMENT("ExtraTraps.Ice"), CVAR_ENHANCEMENT("ExtraTraps.Burn"),
CVAR_ENHANCEMENT("ExtraTraps.Shock"), CVAR_ENHANCEMENT("ExtraTraps.Knockback"),
CVAR_ENHANCEMENT("ExtraTraps.Speed"), CVAR_ENHANCEMENT("ExtraTraps.Bomb"),
CVAR_ENHANCEMENT("ExtraTraps.Void"), CVAR_ENHANCEMENT("ExtraTraps.Ammo"),
CVAR_ENHANCEMENT("ExtraTraps.Kill"), CVAR_ENHANCEMENT("ExtraTraps.Teleport"),
CVAR_ENHANCEMENT("ExtraTraps.Pocket"), CVAR_ENHANCEMENT("ExtraTraps.Permadeath"),
};

static std::unordered_map<ItemID, QuestItem> itemToQuestMap = {
{ ITEM_MEDALLION_FOREST, QUEST_MEDALLION_FOREST },
{ ITEM_MEDALLION_FIRE, QUEST_MEDALLION_FIRE },
{ ITEM_MEDALLION_WATER, QUEST_MEDALLION_WATER },
{ ITEM_MEDALLION_SPIRIT, QUEST_MEDALLION_SPIRIT },
{ ITEM_MEDALLION_SHADOW, QUEST_MEDALLION_SHADOW },
{ ITEM_MEDALLION_LIGHT, QUEST_MEDALLION_LIGHT },
{ ITEM_SONG_MINUET, QUEST_SONG_MINUET },
{ ITEM_SONG_BOLERO, QUEST_SONG_BOLERO },
{ ITEM_SONG_SERENADE, QUEST_SONG_SERENADE },
{ ITEM_SONG_REQUIEM, QUEST_SONG_REQUIEM },
{ ITEM_SONG_NOCTURNE, QUEST_SONG_NOCTURNE },
{ ITEM_SONG_PRELUDE, QUEST_SONG_PRELUDE },
{ ITEM_SONG_LULLABY, QUEST_SONG_LULLABY },
{ ITEM_SONG_EPONA, QUEST_SONG_EPONA },
{ ITEM_SONG_SARIA, QUEST_SONG_SARIA },
{ ITEM_SONG_SUN, QUEST_SONG_SUN },
{ ITEM_SONG_TIME, QUEST_SONG_TIME },
{ ITEM_SONG_STORMS, QUEST_SONG_STORMS },
{ ITEM_KOKIRI_EMERALD, QUEST_KOKIRI_EMERALD },
{ ITEM_GORON_RUBY, QUEST_GORON_RUBY },
{ ITEM_ZORA_SAPPHIRE, QUEST_ZORA_SAPPHIRE },
{ ITEM_GERUDO_CARD, QUEST_GERUDO_CARD },
};

std::vector<AltTrapType> getEnabledAddTraps() {
Expand All @@ -59,11 +93,149 @@ std::vector<AltTrapType> getEnabledAddTraps() {
return enabledAddTraps;
};

void TriggerVoidOut() {
Player* player = GET_PLAYER(gPlayState);

Audio_PlaySoundGeneral(NA_SE_EN_BUBLE_LAUGH, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale,
&gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);

// Hyrule Castle: Very likely to fall through floor, so we force a specific entrance
if (gPlayState->sceneNum == SCENE_HYRULE_CASTLE || gPlayState->sceneNum == SCENE_OUTSIDE_GANONS_CASTLE) {
gPlayState->nextEntranceIndex = ENTR_CASTLE_GROUNDS_SOUTH_EXIT;
} else {
gSaveContext.respawnFlag = 1;
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;

// Preserve the player's position and orientation
gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = gPlayState->nextEntranceIndex;
gSaveContext.respawn[RESPAWN_MODE_DOWN].roomIndex = gPlayState->roomCtx.curRoom.num;
gSaveContext.respawn[RESPAWN_MODE_DOWN].pos = player->actor.world.pos;
gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = player->actor.shape.rot.y;

if (gPlayState->roomCtx.curRoom.behaviorType2 < 4) {
gSaveContext.respawn[RESPAWN_MODE_DOWN].playerParams = 0x0DFF;
} else {
// Scenes with static backgrounds use a special camera we need to preserve
Camera* camera = GET_ACTIVE_CAM(gPlayState);
s16 camId = camera->camDataIdx;
gSaveContext.respawn[RESPAWN_MODE_DOWN].playerParams = 0x0D00 | camId;
}
}

gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_INSTANT;
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK_FAST;
}

void ExecutePocketTrap() {
std::vector<uint32_t> currentLoadout;

for (auto& items : itemMapping) {
if (INV_CONTENT(items.second.id) == items.second.id) {
currentLoadout.push_back(items.second.id);
continue;
}
auto questItem = itemToQuestMap.find(
(ItemID)items.second.id); // std::find(itemToQuestMap.begin(), itemToQuestMap.end(), items.second.id);
if (questItem == itemToQuestMap.end()) {
continue;
}
if (CHECK_QUEST_ITEM(questItem->second)) {
currentLoadout.push_back(items.second.id);
continue;
}
}

if (currentLoadout.size() == 0) {
GameInteractor::RawAction::FreezePlayer();
return;
}

RandomizerGet rgItem = RG_NONE;

uint32_t roll = Random(0, currentLoadout.size() - 1);
if (currentLoadout[roll] >= ITEM_SONG_MINUET) {
rgItem = RetrieveRandomizerGetFromItemID((ItemID)currentLoadout[roll]);
} else {
GetItemID getItemId = RetrieveGetItemIDFromItemID((ItemID)currentLoadout[roll]);
for (auto& randoGet : Rando::StaticData::GetItemTable()) {
if (randoGet.GetItemID() == getItemId) {
rgItem = randoGet.GetRandomizerGet();
break;
}
}
}

if (rgItem == RG_NONE) {
GameInteractor::RawAction::FreezePlayer();
return;
}

for (auto& check : Rando::StaticData::GetLocationTable()) {
if (Rando::Context::GetInstance()->GetItemLocation(check.GetRandomizerCheck())->GetPlacedRandomizerGet() ==
rgItem) {
if (check.GetRandomizerCheck() == RC_UNKNOWN_CHECK || check.GetRandomizerCheck() == RC_LINKS_POCKET) {
GameInteractor::RawAction::FreezePlayer();
return;
}
switch (check.GetActorID()) {
case ACTOR_EN_BOX:
GameInteractor::RawAction::UnsetSceneFlag(check.GetScene(), FLAG_SCENE_TREASURE,
check.GetActorParams() & 0x1F);
break;
default:
if (check.GetCollectionCheck().type != SPOILER_CHK_NONE) {
switch (check.GetCollectionCheck().type) {
case SPOILER_CHK_ITEM_GET_INF:
Flags_UnsetItemGetInf(check.GetCollectionCheck().flag);
break;
case SPOILER_CHK_RANDOMIZER_INF:
Flags_UnsetRandomizerInf((RandomizerInf)check.GetCollectionCheck().flag);
break;
case SPOILER_CHK_EVENT_CHK_INF:
Flags_UnsetEventInf(check.GetCollectionCheck().flag);
break;
case SPOILER_CHK_CHEST:
GameInteractor::RawAction::UnsetSceneFlag(check.GetScene(), FLAG_SCENE_TREASURE,
check.GetCollectionCheck().flag);
break;
case SPOILER_CHK_COLLECTABLE:
GameInteractor::RawAction::UnsetSceneFlag(check.GetScene(), FLAG_SCENE_COLLECTIBLE,
check.GetCollectionCheck().flag);
break;
case SPOILER_CHK_INF_TABLE:
Flags_UnsetInfTable(check.GetCollectionCheck().flag);
break;
default:
GameInteractor::RawAction::FreezePlayer();
return;
}
}
break;
}

Rando::Context::GetInstance()->GetItemLocation(check.GetRandomizerCheck())->SetCheckStatus(RCSHOW_SEEN);
INV_CONTENT(currentLoadout[roll]) = ITEM_NONE;
break;
}
}

Notification::Emit({ .itemIcon = (const char*)gItemIcons[currentLoadout[roll]],
.prefix = "You've lost your",
.prefixColor = UIWidgets::ColorValues.at(UIWidgets::Colors::White),
.message = Rando::StaticData::RetrieveItem(rgItem).GetName().english.c_str(),
.messageColor = UIWidgets::ColorValues.at(UIWidgets::Colors::Green),
.suffix = ". I remember seeing it somewhere...",
.suffixColor = UIWidgets::ColorValues.at(UIWidgets::Colors::White) });

TriggerVoidOut();
}

static void RollRandomTrap(uint32_t seed) {
uint32_t finalSeed = seed + (IS_RANDO ? Rando::Context::GetInstance()->GetSeed()
: static_cast<uint32_t>(gSaveContext.ship.stats.fileCreatedAt));
Random_Init(finalSeed);

// roll = ADD_PERMADEATH_TRAP;
roll = RandomElement(getEnabledAddTraps());
switch (roll) {
case ADD_ICE_TRAP:
Expand Down Expand Up @@ -103,6 +275,18 @@ static void RollRandomTrap(uint32_t seed) {
case ADD_TELEPORT_TRAP:
eventTimer = 3;
break;
case ADD_POCKET_TRAP:
ExecutePocketTrap();
break;
case ADD_PERMADEATH_TRAP:
permaDeathTimer = 180;
shouldFileDelete = true;
Notification::Emit({ .itemIcon = (const char*)gItemIcons[ITEM_BOMB],
.message = "Collect a Check or Perma Death executes in ",
.messageColor = UIWidgets::ColorValues.at(UIWidgets::Colors::White),
.suffix = "60 seconds.",
.suffixColor = UIWidgets::ColorValues.at(UIWidgets::Colors::Red) });
break;
default:
break;
}
Expand Down Expand Up @@ -167,6 +351,17 @@ static void OnPlayerUpdate() {
break;
}
}
if (permaDeathTimer == 0) {
if (shouldFileDelete) {
SaveManager::Instance->DeleteZeldaFile(gSaveContext.fileNum);
std::reinterpret_pointer_cast<Ship::ConsoleWindow>(
Ship::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))
->Dispatch("reset");
}
}
if (permaDeathTimer >= 0) {
permaDeathTimer--;
}
if (statusTimer >= 0) {
statusTimer--;
}
Expand Down Expand Up @@ -195,6 +390,20 @@ void RegisterExtraTraps() {
GameInteractor::RawAction::FreezePlayer();
}
});

COND_HOOK(OnFlagSet, CVAR_EXTRA_TRAPS_NAME, [](int16_t flagType, int16_t flag) {
SPDLOG_INFO("Flag Set Here {}", std::to_string(flagType).c_str());
if (flagType != FLAG_SCENE_CLEAR) {
shouldFileDelete = false;
}
});

COND_HOOK(OnSceneFlagSet, CVAR_EXTRA_TRAPS_NAME, [](int16_t sceneNum, int16_t flagType, int16_t flag) {
SPDLOG_INFO("Scene Flag Set Here {}", std::to_string(flagType).c_str());
if (flagType != FLAG_SCENE_CLEAR) {
shouldFileDelete = false;
}
});
}

static RegisterShipInitFunc initFunc(RegisterExtraTraps, { CVAR_EXTRA_TRAPS_NAME });
Loading
Loading