Compare commits

...

18 Commits

Author SHA1 Message Date
Chris 37db034815 Use singular on message for 1 token (#6567) 2026-05-09 22:25:09 +00:00
Pepper0ni 5b4d8edf51 fix bad merge with the suns song fairy in spirit (#6590) 2026-05-04 02:02:21 +00:00
Jameriquiah 1a46d2ec96 hyrule field typo fix (#6574) 2026-04-29 09:37:20 -07:00
Pepper0ni ea76550fc7 Fix owl talk logic to include kokiri (#6564) 2026-04-25 15:30:27 +00:00
Philip Dubé ccfa31a245 Fix drag&drop not updating excluded locations (#6559) 2026-04-23 22:48:12 +00:00
Philip Dubé 806398a65b Fix OGC great fairy reward in vanilla with skip misc interactions when fish is not obtainable (#6556)
Item_CheckObtainability should only be called with MOD_NONE GI

For RG_DOUBLE_DEFENSE that became ITEM_FISH. Nonsense ensued

To reproduce issue, create debug save & go straight to OGC great fairy with only magic/ocarina/lullaby
2026-04-23 12:34:26 +00:00
Pepper0ni 3221d8a988 Fix bean fairies + start with beans generation (#6548) 2026-04-21 23:04:37 +00:00
Philip Dubé 69681e608f Fix swimvoid in grottos to just respawn in grotto (#6529) 2026-04-21 16:02:33 +00:00
Philip Dubé 719bb87eda add hint text for sun song fairies in spirit temple (#6544) 2026-04-21 15:30:33 +00:00
Reppan bf37645d72 Fix mirror shield color editor (#6542)
Oversight on my end that customequipment.cpp unloaded and reloaded assets abit to aggressive.

Adds a guard to make sure to only unload if custom asset is used and to not unload then reload vanilla asset.

Have tested it with fados customequipment and confirmed that mirrorshield is working as intended.
2026-04-20 17:18:08 +00:00
Philip Dubé 3b65eaa4ef Fix cutscene skips causing credits to spawn player in Lake Hylia (#6534)
SkipBlueWarp was intercepting credits. Disable during GAMEMODE_END_CREDITS
2026-04-19 20:50:11 +00:00
Chris 3be7eff02c Arrow cycle should check for sufficient magic (#6532) 2026-04-19 15:04:13 +00:00
Philip Dubé 39dcc0a73c Triforce Hunt: drain queue before credits (#6519) 2026-04-18 21:21:29 +00:00
Philip Dubé 256ab01630 Fishing: don't say default 6/7 when minimum reduced by enhancement (#6523) 2026-04-17 04:31:02 +00:00
Philip Dubé 072838613a Fix kak bazaar items having articles (#6522)
IsShop didn't include SCENE_TEST01, but that's used as placeholder for kak bazaar vs market bazaar
2026-04-17 03:53:24 +00:00
Chris 9c321862ca Fix Rate Limited Success Chime (#6512) 2026-04-17 01:30:28 +00:00
A Green Spoon 412b60a02f Fix Duplicate Reticles in Anchor (#6520) 2026-04-16 23:50:55 +00:00
Philip Dubé 1876435e98 Fix skulltula hints ending text too soon (#6516) 2026-04-16 15:59:14 +00:00
27 changed files with 145 additions and 85 deletions
+3 -3
View File
@@ -43,11 +43,11 @@ static bool HasArrowType(PlayerItemAction itemAction) {
case PLAYER_IA_BOW:
return true;
case PLAYER_IA_BOW_FIRE:
return INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE;
return INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_ARROW_FIRE && gSaveContext.magic >= sMagicArrowCosts[0];
case PLAYER_IA_BOW_ICE:
return INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE;
return INV_CONTENT(ITEM_ARROW_ICE) == ITEM_ARROW_ICE && gSaveContext.magic >= sMagicArrowCosts[1];
case PLAYER_IA_BOW_LIGHT:
return INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT;
return INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT && gSaveContext.magic >= sMagicArrowCosts[2];
default:
return false;
}
+21
View File
@@ -0,0 +1,21 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/ShipInit.hpp"
extern "C" {
#include <variables.h>
f32 Fishing_GetMinimumRequiredScore();
}
void BuildFishingMessage(uint16_t* textId, bool* loadFromMessageTable) {
if (gSaveContext.minigameScore == 0) {
gSaveContext.minigameScore = Fishing_GetMinimumRequiredScore();
}
}
void RegisterFishingMessages() {
COND_ID_HOOK(OnOpenText, 0x40AE, CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0), BuildFishingMessage);
COND_ID_HOOK(OnOpenText, 0x4080, CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0), BuildFishingMessage);
}
static RegisterShipInitFunc initFunc(RegisterFishingMessages, { CVAR_ENHANCEMENT("CustomizeFishing") });
+6 -6
View File
@@ -6,11 +6,11 @@ extern "C" {
void BuildSkulltulaMessage(uint16_t* textId, bool* loadFromMessageTable) {
CustomMessage msg =
CustomMessage("You got a %rGold Skulltula Token%w!&You've collected %r[[gsCount]]%w tokens&in total!",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r[[gsCount]]&%wGoldene "
"Skulltula-Symbole&gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r[[gsCount]]%w symboles en "
"tout!",
CustomMessage("You got a %rGold Skulltula Token%w!&You've collected %r[[d]]%w |token|tokens|&in total!",
"Ein %rGoldenes Skulltula-Symbol%w!&Du hast nun insgesamt %r[[d]]&%w|Goldenes "
"Skulltula-Symbol|Goldene Skulltula-Symbole|&gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r[[d]]%w |symbole|symboles| "
"en tout!",
TEXTBOX_TYPE_BLUE);
// The freeze text cannot be manually dismissed and must be auto-dismissed.
// This is fine and even wanted when skull tokens are not shuffled, but when
@@ -25,7 +25,7 @@ void BuildSkulltulaMessage(uint16_t* textId, bool* loadFromMessageTable) {
msg = msg + "\x0E\x3C";
}
int16_t gsCount = gSaveContext.inventory.gsTokens;
msg.Replace("[[gsCount]]", std::to_string(gsCount));
msg.InsertNumber(gsCount);
msg.AutoFormat(ITEM_SKULL_TOKEN);
msg.LoadIntoFont();
*loadFromMessageTable = false;
@@ -36,7 +36,7 @@ void RegisterAdditionalReticles() {
bool shouldRegister = CVAR_BOW_RETICLE_VALUE || CVAR_BOOMERANG_RETICLE_VALUE;
COND_VB_SHOULD(VB_DRAW_ADDITIONAL_RETICLES, shouldRegister, {
Player* player = GET_PLAYER(gPlayState);
Player* player = va_arg(args, Player*);
Actor* heldActor = player->heldActor;
if (CVAR_BOW_RETICLE_VALUE &&
((player->heldItemAction >= PLAYER_IA_BOW && player->heldItemAction <= PLAYER_IA_BOW_LIGHT) ||
@@ -86,8 +86,7 @@ void RegisterShouldPlayBlueWarp() {
* should also account for the difference between your first and following visits to the blue warp.
*/
REGISTER_VB_SHOULD(VB_PLAY_TRANSITION_CS, {
// Do nothing when in a boss rush
if (IS_BOSS_RUSH) {
if (IS_BOSS_RUSH || gSaveContext.gameMode == GAMEMODE_END_CREDITS) {
return;
}
@@ -2707,6 +2707,7 @@ void RegisterCosmeticHooks() {
[](s16 sceneNum) { CosmeticsEditor_AutoRandomizeAll(); });
COND_HOOK(OnGameFrameUpdate, true, CosmeticsUpdateTick);
COND_HOOK(OnAssetAltChange, true, []() { ApplyOrResetCustomGfxPatches(true); });
}
void RegisterCosmeticWidgets() {
+12 -2
View File
@@ -121,6 +121,8 @@ static bool IsDummyPlayer(const Player* player) {
return player != nullptr && player->actor.update == DummyPlayer_Update;
}
static bool sPrevAltAssetsEnabled = false;
void PatchOrUnpatch(const char* resource, const char* gfx, const char* dlist1, const char* dlist2, const char* dlist3,
const char* alternateDL) {
if (resource == NULL || gfx == NULL || dlist1 == NULL || dlist2 == NULL) {
@@ -128,6 +130,7 @@ void PatchOrUnpatch(const char* resource, const char* gfx, const char* dlist1, c
}
const bool altAssetsRuntime = ResourceMgr_IsAltAssetsEnabled();
const bool altAssetsChanged = (altAssetsRuntime != sPrevAltAssetsEnabled);
if (!altAssetsRuntime) {
// Alt assets are off; ensure any prior patches using these names are reverted.
@@ -136,11 +139,16 @@ void PatchOrUnpatch(const char* resource, const char* gfx, const char* dlist1, c
if (dlist3 != NULL) {
ResourceMgr_UnpatchGfxByName(resource, dlist3);
}
// Drop any cached version of the resource so it reloads clean (unpatched) next use.
ResourceMgr_UnloadResource(resource);
if (altAssetsChanged) {
ResourceMgr_UnloadResource(resource);
}
return;
}
if (altAssetsChanged) {
ResourceMgr_UnloadResource(resource);
}
if (!ResourceGetIsCustomByName(gfx)) {
return;
}
@@ -510,4 +518,6 @@ void UpdatePatchCustomEquipmentDlists() {
}
ApplyCommonEquipmentPatches();
sPrevAltAssetsEnabled = ResourceMgr_IsAltAssetsEnabled();
}
@@ -540,10 +540,11 @@ typedef enum {
// #### `result`
// ```c
// true
// (this->heldItemAction == PLAYER_IA_HOOKSHOT) ||
// (this->heldItemAction == PLAYER_IA_LONGSHOT)
// ```
// #### `args`
// - None
// - '*Player'
VB_DRAW_ADDITIONAL_RETICLES,
// #### `result`
@@ -1318,6 +1318,14 @@ void StaticData::HintTable_Init_Exclude_Dungeon() {
/*german*/ "Man erzählt sich, daß sich bewacht von einem #Ring der Flammen#, im Geistertempel #[[1]]# |befände|befänden|.",
/*french*/ "Selon moi, protégé par un #cercle de flammes# dans le Temple de l'Esprit se trouve #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun past rolling boulders in Spirit Temple# reveals #[[1]]#.",
/*german*/ TODO_TRANSLATE,
/*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY] = HintText(CustomMessage("They say that #calling the sun in the spotlight by statues# reveals #[[1]]#.",
/*german*/ TODO_TRANSLATE,
/*french*/ TODO_TRANSLATE, {QM_RED, QM_GREEN}));
hintTextTable[RHT_CRATE_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that a #crate in Spirit Temple# contains #[[1]]#.",
/*german*/ "Man erzählt sich, daß eine #Kiste im Geistertempel# #[[1]]# enthielte.",
/*french*/ "Selon moi, une #caisse dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN}));
@@ -2128,7 +2128,7 @@ void StaticData::HintTable_Init_Exclude_Overworld() {
/*french*/ "Selon moi, un #arbre au Ranch Lon Lon# cache #[[1]]#.", { QM_RED, QM_GREEN }));
hintTextTable[RHT_BUSH_HYRULE_FIELD] =
HintText(CustomMessage("They say that a #bush in Hyrle Field# contains #[[1]]#.",
HintText(CustomMessage("They say that a #bush in Hyrule Field# contains #[[1]]#.",
/*german*/ "",
/*french*/ "Selon moi, un #buisson dans la Plaine d'Hyrule# cache #[[1]]#.", { QM_RED, QM_GREEN }));
hintTextTable[RHT_BUSH_ZORAS_FOUNTAIN] =
@@ -126,6 +126,7 @@ void GenerateStartingInventory() {
AddItemToInventory(RG_NOCTURNE_OF_SHADOW, ctx->GetOption(RSK_STARTING_NOCTURNE_OF_SHADOW) ? 1 : 0);
AddItemToInventory(RG_PRELUDE_OF_LIGHT, ctx->GetOption(RSK_STARTING_PRELUDE_OF_LIGHT) ? 1 : 0);
AddItemToInventory(RG_KOKIRI_SWORD, ctx->GetOption(RSK_STARTING_KOKIRI_SWORD) ? 1 : 0);
AddItemToInventory(RG_MAGIC_BEAN_PACK, ctx->GetOption(RSK_STARTING_BEANS) ? 1 : 0);
// if (ProgressiveGoronSword) {
// AddItemToInventory(RG_PROGRESSIVE_GORONSWORD, StartingBiggoronSword.Value<uint8_t>());
// } else {
@@ -140,7 +140,7 @@ void BuildSkulltulaPeopleMessage(uint16_t* textId, bool* loadFromMessageTable) {
"et j'aurai quelque chose à te donner! [[color]]([[1]])%w");
msg.InsertNumber(count);
msg.Replace("[[color]]", item.GetColor());
msg.InsertNames({ item.GetHint().GetHintMessage().GetForCurrentLanguage() });
msg.InsertNames({ item.GetHint().GetHintMessage() });
msg.AutoFormat();
msg.LoadIntoFont();
*loadFromMessageTable = false;
@@ -158,7 +158,7 @@ void Build100SkullsHintMessage(uint16_t* textId, bool* loadFromMessageTable) {
Rando::Item& item =
Rando::StaticData::RetrieveItem(RAND_GET_ITEM_LOC(RC_KAK_100_GOLD_SKULLTULA_REWARD)->GetPlacedRandomizerGet());
msg.Replace("[[color]]", item.GetColor());
msg.InsertNames({ item.GetHint().GetHintMessage().GetForCurrentLanguage() });
msg.InsertNames({ item.GetHint().GetHintMessage() });
msg.AutoFormat();
msg.LoadIntoFont();
*loadFromMessageTable = false;
@@ -2616,7 +2616,16 @@ void RandomizerOnPlayerUpdateHandler() {
gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawn->second.yaw;
}
Play_TriggerVoidOut(gPlayState);
if (gPlayState->sceneNum == SCENE_GROTTOS) {
// RESPAWN_MODE_DOWN isn't refreshed on grotto entry, reload grotto instead
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gPlayState->transitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_BLACK;
gSaveContext.respawnFlag = 0;
} else {
Play_TriggerVoidOut(gPlayState);
}
}
}
@@ -2629,8 +2638,14 @@ void RandomizerOnPlayerUpdateHandler() {
}
if (!GameInteractor::IsGameplayPaused() && RAND_GET_OPTION(RSK_TRIFORCE_HUNT).IsNot(RO_TRIFORCE_HUNT_OFF)) {
// Warp to credits
if (GameInteractor::State::TriforceHuntCreditsWarpActive) {
// Warp to credits once item queue has drained to avoid losing queued items
if (GameInteractor::State::TriforceHuntCreditsWarpActive && randomizerQueuedChecks.empty() &&
randomizerQueuedCheck == RC_UNKNOWN_CHECK) {
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] =
static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
gSaveContext.ship.stats.gameComplete = 1;
Play_PerformSave(gPlayState);
Notification::Emit({ .message = "Game autosaved" });
gPlayState->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0xFFF2;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
-1
View File
@@ -1,7 +1,6 @@
#pragma once
#include <string>
#include <variant>
#include <memory>
#include "3drando/text.hpp"
+1 -1
View File
@@ -64,7 +64,7 @@ bool Rando::Location::IsOverworld() const {
}
bool Rando::Location::IsShop() const {
return scene >= SCENE_BAZAAR && scene <= SCENE_BOMBCHU_SHOP;
return (scene >= SCENE_BAZAAR && scene <= SCENE_BOMBCHU_SHOP) || scene == SCENE_TEST01;
}
bool Rando::Location::IsVanillaCompletion() const {
@@ -134,7 +134,6 @@ void RegionTable_Init_SpiritTemple() {
LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanHitSwitch(ED_BOMB_THROW) && logic->HasItem(RG_OPEN_CHEST);})),
LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);}, false,
RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})),
LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH))) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash())),
}, {
//Exits
ENTRANCE(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_1F, true),
@@ -64,7 +64,7 @@ void RegionTable_Init_DeathMountainTrail() {
//Exits
ENTRANCE(RR_DEATH_MOUNTAIN_ROCKFALL, true),
ENTRANCE(RR_DMC_UPPER_ENTRY, true),
ENTRANCE(RR_DMT_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_DMT_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_KOKIRI) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_DMT_GREAT_FAIRY_FOUNTAIN, AnyAgeTime([]{return logic->BlastOrSmash();})),
});
@@ -83,11 +83,11 @@ void RegionTable_Init_LakeHylia() {
//Exits
ENTRANCE(RR_HF_TO_LAKE_HYLIA, true),
ENTRANCE(RR_LH_FROM_SHORTCUT, true),
ENTRANCE(RR_LH_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_LH_OWL_FLIGHT, logic->IsChild && (logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_KOKIRI) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_LH_FISHING_ISLAND, ((logic->IsChild || logic->Get(LOGIC_WATER_TEMPLE_CLEAR)) && logic->HasItem(RG_BRONZE_SCALE)) || (logic->IsAdult && (logic->ReachScarecrow() || CanPlantBean(RR_LAKE_HYLIA, RG_LAKE_HYLIA_BEAN_SOUL)))),
ENTRANCE(RR_LH_LAB, logic->CanOpenOverworldDoor(RG_HYLIA_LAB_KEY)),
ENTRANCE(RR_LH_FROM_WATER_TEMPLE, true),
ENTRANCE(RR_LH_GROTTO, logic->HasItem(RG_POWER_BRACELET) && (logic->IsAdult || logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
ENTRANCE(RR_LH_GROTTO, logic->HasItem(RG_POWER_BRACELET) && (logic->IsAdult || logic->HasItem(RG_SPEAK_DEKU) || logic->HasItem(RG_SPEAK_GERUDO) || logic->HasItem(RG_SPEAK_GORON) || logic->HasItem(RG_SPEAK_KOKIRI) || logic->HasItem(RG_SPEAK_HYLIAN) || logic->HasItem(RG_SPEAK_ZORA))),
});
areaTable[RR_LH_FROM_SHORTCUT] = Region("LH From Shortcut", SCENE_LAKE_HYLIA, TIME_DOESNT_PASS, {RA_LAKE_HYLIA}, {}, {}, {
@@ -3902,13 +3902,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) ==
RO_TRIFORCE_HUNT_WIN) {
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] =
static_cast<u32>(GAMEPLAYSTAT_TOTAL_TIME);
gSaveContext.ship.stats.gameComplete = 1;
Play_PerformSave(play);
Notification::Emit({
.message = "Game autosaved",
});
// Save and warp are deferred until item queue drains
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
}
}
@@ -24,7 +24,6 @@ extern "C" {
#include "src/overlays/actors/ovl_En_Jj/z_en_jj.h"
#include "src/overlays/actors/ovl_En_Daiku/z_en_daiku.h"
#include "src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h"
#include "src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h"
#include "src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h"
#include "src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h"
#include "src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h"
@@ -1241,41 +1240,41 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
case FLAG_EVENT_CHECK_INF:
switch (flag) {
case EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FAIRY_OCARINA).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FAIRY_OCARINA);
break;
case EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_KOKIRI_EMERALD).GetGIEntry_Copy();
TimeSaverQueueItem(RG_KOKIRI_EMERALD);
break;
case EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_GORON_RUBY).GetGIEntry_Copy();
TimeSaverQueueItem(RG_GORON_RUBY);
break;
case EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZORA_SAPPHIRE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_ZORA_SAPPHIRE);
break;
case EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FOREST_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FOREST_MEDALLION);
break;
case EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FIRE_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FIRE_MEDALLION);
break;
case EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_WATER_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_WATER_MEDALLION);
break;
case EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_ARROWS).GetGIEntry_Copy();
TimeSaverQueueItem(RG_LIGHT_ARROWS);
break;
case EVENTCHKINF_TIME_TRAVELED_TO_ADULT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_LIGHT_MEDALLION);
break;
}
break;
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SHADOW_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SHADOW_MEDALLION);
break;
case RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SPIRIT_MEDALLION).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SPIRIT_MEDALLION);
break;
}
break;
@@ -1287,22 +1286,22 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_ZF_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FARORES_WIND).GetGIEntry_Copy();
TimeSaverQueueItem(RG_FARORES_WIND);
break;
case RAND_INF_HC_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DINS_FIRE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_DINS_FIRE);
break;
case RAND_INF_COLOSSUS_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_NAYRUS_LOVE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_NAYRUS_LOVE);
break;
case RAND_INF_DMT_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MAGIC_SINGLE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_MAGIC_SINGLE);
break;
case RAND_INF_DMC_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MAGIC_DOUBLE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_MAGIC_DOUBLE);
break;
case RAND_INF_OGC_GREAT_FAIRY_REWARD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_DOUBLE_DEFENSE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_DOUBLE_DEFENSE);
break;
}
break;
@@ -1328,47 +1327,44 @@ void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
case FLAG_EVENT_CHECK_INF:
switch (flag) {
case EVENTCHKINF_LEARNED_ZELDAS_LULLABY:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZELDAS_LULLABY).GetGIEntry_Copy();
TimeSaverQueueItem(RG_ZELDAS_LULLABY);
break;
case EVENTCHKINF_LEARNED_MINUET_OF_FOREST:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MINUET_OF_FOREST).GetGIEntry_Copy();
TimeSaverQueueItem(RG_MINUET_OF_FOREST);
break;
case EVENTCHKINF_LEARNED_BOLERO_OF_FIRE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_BOLERO_OF_FIRE).GetGIEntry_Copy();
TimeSaverQueueItem(RG_BOLERO_OF_FIRE);
break;
case EVENTCHKINF_LEARNED_SERENADE_OF_WATER:
vanillaQueuedItemEntry =
Rando::StaticData::RetrieveItem(RG_SERENADE_OF_WATER).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SERENADE_OF_WATER);
break;
case EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT:
vanillaQueuedItemEntry =
Rando::StaticData::RetrieveItem(RG_REQUIEM_OF_SPIRIT).GetGIEntry_Copy();
TimeSaverQueueItem(RG_REQUIEM_OF_SPIRIT);
break;
case EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL:
vanillaQueuedItemEntry =
Rando::StaticData::RetrieveItem(RG_NOCTURNE_OF_SHADOW).GetGIEntry_Copy();
TimeSaverQueueItem(RG_NOCTURNE_OF_SHADOW);
break;
case EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_PRELUDE_OF_LIGHT).GetGIEntry_Copy();
TimeSaverQueueItem(RG_PRELUDE_OF_LIGHT);
break;
case EVENTCHKINF_LEARNED_SARIAS_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SARIAS_SONG).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SARIAS_SONG);
break;
case EVENTCHKINF_LEARNED_SONG_OF_TIME:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_TIME).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SONG_OF_TIME);
break;
case EVENTCHKINF_LEARNED_SONG_OF_STORMS:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_STORMS).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SONG_OF_STORMS);
break;
case EVENTCHKINF_LEARNED_SUNS_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SUNS_SONG).GetGIEntry_Copy();
TimeSaverQueueItem(RG_SUNS_SONG);
break;
}
break;
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_LEARNED_EPONA_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_EPONAS_SONG).GetGIEntry_Copy();
TimeSaverQueueItem(RG_EPONAS_SONG);
break;
}
break;
@@ -1415,12 +1411,10 @@ static void TimeSaverRegisterHooks() {
TimeSaverOnSceneInitHandler);
COND_HOOK(OnVanillaBehavior, true, TimeSaverOnVanillaBehaviorHandler);
COND_HOOK(OnActorInit, true, TimeSaverOnActorInitHandler);
COND_HOOK(OnSceneInit, true, [](int16_t sceneNum) { successChimeCooldown = 0; });
// item queue for use outside rando, rando has its own queue
COND_HOOK(OnLoadGame, !IS_RANDO, [](int32_t fileNum) {
vanillaQueuedItemEntry = GET_ITEM_NONE;
successChimeCooldown = 0;
});
COND_HOOK(OnLoadGame, !IS_RANDO, [](int32_t fileNum) { vanillaQueuedItemEntry = GET_ITEM_NONE; });
COND_HOOK(OnItemReceive, !IS_RANDO, TimeSaverOnItemReceiveHandler);
COND_HOOK(OnPlayerUpdate, !IS_RANDO, TimeSaverOnPlayerUpdateHandler);
COND_HOOK(OnFlagSet,
+1
View File
@@ -2536,6 +2536,7 @@ bool SoH_HandleConfigDrop(char* filePath) {
->ClearBindings();
Rando::Settings::GetInstance()->UpdateAllOptions();
SohGui::MarkRandomizerMenusDirty();
gui->SaveConsoleVariablesNextFrame();
ShipInit::Init("*");
+1
View File
@@ -28,6 +28,7 @@ static std::map<int32_t, const char*> languages = {
};
void UpdateMenuTricks();
void UpdateMenuLocations();
void MarkRandomizerMenusDirty();
class SohMenu : public Ship::Menu {
public:
+5
View File
@@ -181,6 +181,11 @@ void DrawLocationsMenu(WidgetInfo& info) {
ImGui::EndDisabled();
}
void MarkRandomizerMenusDirty() {
locationsDirty = true;
tricksDirty = true;
}
void UpdateMenuLocations() {
RandomizerCheckObjects::UpdateImGuiVisibility();
// todo: this efficiently when we build out cvar array support
+9 -6
View File
@@ -2032,8 +2032,10 @@ s32 GiveItemEntryWithoutActor(PlayState* play, GetItemEntry getItemEntry) {
PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_JUMPING | PLAYER_STATE1_FREEFALL | PLAYER_STATE1_FIRST_PERSON |
PLAYER_STATE1_CLIMBING_LADDER)) &&
Player_GetExplosiveHeld(player) < 0) {
if (((player->heldActor != NULL) && ((getItemEntry.getItemId > GI_NONE) && (getItemEntry.getItemId < GI_MAX)) ||
(IS_RANDO && (getItemEntry.getItemId > RG_NONE) && (getItemEntry.getItemId < RG_MAX))) ||
if (((player->heldActor != NULL && (getItemEntry.modIndex == MOD_NONE && getItemEntry.getItemId > GI_NONE &&
getItemEntry.getItemId < GI_MAX)) ||
(getItemEntry.modIndex == MOD_RANDOMIZER && getItemEntry.getItemId > RG_NONE &&
getItemEntry.getItemId < RG_MAX)) ||
(!(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)))) {
if ((getItemEntry.getItemId != GI_NONE)) {
player->getItemEntry = getItemEntry;
@@ -2073,8 +2075,10 @@ s32 GiveItemEntryFromActor(Actor* actor, PlayState* play, GetItemEntry getItemEn
PLAYER_STATE1_CLIMBING_LADDER)) &&
Player_GetExplosiveHeld(player) < 0) {
if ((((player->heldActor != NULL) || (actor == player->talkActor)) &&
((!IS_RANDO && ((getItemEntry.getItemId > GI_NONE) && (getItemEntry.getItemId < GI_MAX))) ||
(IS_RANDO && ((getItemEntry.getItemId > RG_NONE) && (getItemEntry.getItemId < RG_MAX))))) ||
((getItemEntry.getItemId == MOD_NONE &&
((getItemEntry.getItemId > GI_NONE) && (getItemEntry.getItemId < GI_MAX))) ||
(getItemEntry.getItemId == MOD_RANDOMIZER &&
((getItemEntry.getItemId > RG_NONE) && (getItemEntry.getItemId < RG_MAX))))) ||
(!(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)))) {
if ((actor->xzDistToPlayer < xzRange) && (fabsf(actor->yDistToPlayer) < yRange)) {
s16 yawDiff = actor->yawTowardsPlayer - player->actor.shape.rot.y;
@@ -2118,8 +2122,7 @@ s32 Actor_OfferGetItem(Actor* actor, PlayState* play, s32 getItemId, f32 xzRange
PLAYER_STATE1_CLIMBING_LADDER)) &&
Player_GetExplosiveHeld(player) < 0) {
if ((((player->heldActor != NULL) || (actor == player->talkActor)) &&
((!IS_RANDO && ((getItemId > GI_NONE) && (getItemId < GI_MAX))) ||
(IS_RANDO && ((getItemId > RG_NONE) && (getItemId < RG_MAX))))) ||
((getItemId > GI_NONE) && (getItemId < GI_MAX))) ||
(!(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)))) {
if ((actor->xzDistToPlayer < xzRange) && (fabsf(actor->yDistToPlayer) < yRange)) {
s16 yawDiff = actor->yawTowardsPlayer - player->actor.shape.rot.y;
+4 -3
View File
@@ -3,7 +3,6 @@
#include "objects/gameplay_field_keep/gameplay_field_keep.h"
#include "objects/object_link_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h"
#include "objects/object_triforce_spot/object_triforce_spot.h"
#include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
@@ -1906,8 +1905,10 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList, Ve
}
if (this->actor.scale.y >= 0.0f) {
if (GameInteractor_Should(VB_DRAW_ADDITIONAL_RETICLES, (this->heldItemAction == PLAYER_IA_HOOKSHOT) ||
(this->heldItemAction == PLAYER_IA_LONGSHOT))) {
if (GameInteractor_Should(VB_DRAW_ADDITIONAL_RETICLES,
(this->heldItemAction == PLAYER_IA_HOOKSHOT) ||
(this->heldItemAction == PLAYER_IA_LONGSHOT),
this)) {
Matrix_MultVec3f(&D_80126184, &this->unk_3C8);
if (heldActor != NULL) {
@@ -432,6 +432,8 @@ static FishingEffect sFishingEffects[FISHING_EFFECT_COUNT];
static Vec3f sStreamSoundProjectedPos;
static s16 sFishOnHandParams;
f32 Fishing_GetMinimumRequiredScore();
u8 AllHyruleLoaches() {
return CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0) &&
CVarGetInteger(CVAR_ENHANCEMENT("AllHyruleLoaches"), 0);
@@ -904,12 +906,16 @@ void Fishing_Init(Actor* thisx, PlayState* play2) {
if (sLinkAge == LINK_AGE_CHILD) {
if ((HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_CHILD) != 0) {
sFishingRecordLength = HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_CHILD;
} else if (CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0)) {
sFishingRecordLength = Fishing_GetMinimumRequiredScore();
} else {
sFishingRecordLength = 40.0f; // 6 lbs
}
} else {
if ((HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_ADULT) != 0) {
sFishingRecordLength = (HIGH_SCORE(HS_FISHING) & HS_FISH_LENGTH_ADULT) >> 0x18;
} else if (CVarGetInteger(CVAR_ENHANCEMENT("CustomizeFishing"), 0)) {
sFishingRecordLength = Fishing_GetMinimumRequiredScore();
} else {
sFishingRecordLength = 45.0f; // 7 lbs
}
@@ -7339,8 +7339,9 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
// getting bombchus need to show the cutscene) and whenever the player doesn't have the item yet. In
// rando, we're overruling this because we need to keep showing the cutscene because those items can be
// randomized and thus it's important to keep showing the cutscene.
uint8_t showItemCutscene = play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY ||
Item_CheckObtainability(giEntry.itemId) == ITEM_NONE || IS_RANDO;
uint8_t showItemCutscene = play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY || IS_RANDO ||
giEntry.modIndex == MOD_RANDOMIZER ||
Item_CheckObtainability(giEntry.itemId) == ITEM_NONE;
// Only skip cutscenes for drops when they're items/consumables from bushes/rocks/enemies.
uint8_t isDropToSkip =
@@ -7359,8 +7360,8 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) {
// the player already has because those items could be a randomized item coming from scrubs,
// freestanding PoH's and keys. So we need to once again overrule this specifically for items coming
// from bushes/rocks/enemies when the player has already picked that item up.
uint8_t skipItemCutsceneRando =
IS_RANDO && Item_CheckObtainability(giEntry.itemId) != ITEM_NONE && isDropToSkip;
uint8_t skipItemCutsceneRando = IS_RANDO && giEntry.modIndex == MOD_NONE &&
Item_CheckObtainability(giEntry.itemId) != ITEM_NONE && isDropToSkip;
// Show cutscene when picking up a item.
if (showItemCutscene && !skipItemCutscene && !skipItemCutsceneRando) {