From 533ee9379addc019a1c622c6a03c6271508f81e5 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 15 Mar 2023 19:46:30 -0700 Subject: [PATCH 1/8] Balanced Shopsanity Bugfix (#2640) * Fixes settings bug with Balanced shopsanity prices from Shopsanity Affordable Update. * Wrong location * Updated fixed comment --- soh/soh/Enhancements/randomizer/3drando/shops.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 3cfaa01ad..4049ee628 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -150,8 +150,8 @@ int GetPriceAffordable() { } int GetRandomShopPrice() { - // If Affordable is enabled, no need to set randomizer max price - if (Settings::ShopsanityPricesAffordable.Is(true)) { + // If Shopsanity prices aren't Balanced, but Affordable is on, don't GetPriceFromMax + if (Settings::ShopsanityPricesAffordable.Is(true) && Settings::ShopsanityPrices.IsNot(RO_SHOPSANITY_PRICE_BALANCED)) { return GetPriceAffordable(); } From e76b990c8a37d4877ebd4aef9ab8093450e9a0c1 Mon Sep 17 00:00:00 2001 From: Malkierian Date: Wed, 22 Mar 2023 17:59:08 -0700 Subject: [PATCH 2/8] Randomzier: Fix starting rupee item collection (#2645) * When rupees are given as starting items, they still modify `gSaveContext.rupeeAccumulator`, which means that if you make a new file, start it, and reload without saving, or exit SoH before starting the new file, those rupees were lost. This adds a check for `gPlayState` being NULL, and if it is, adds those initial rupees to the rupee count directly. * Update soh/src/code/z_parameter.c Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> --------- Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> --- soh/src/code/z_parameter.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index c5bbb38ca..1b44f67d5 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -3084,7 +3084,11 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } void Rupees_ChangeBy(s16 rupeeChange) { - gSaveContext.rupeeAccumulator += rupeeChange; + if (gPlayState == NULL) { + gSaveContext.rupees += rupeeChange; + } else { + gSaveContext.rupeeAccumulator += rupeeChange; + } if (rupeeChange > 0) { gSaveContext.sohStats.count[COUNT_RUPEES_COLLECTED] += rupeeChange; From f682102c96ca7944662aff301cc3558fa4d8f080 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 31 Mar 2023 21:29:35 -0400 Subject: [PATCH 3/8] fix theater mask checks not always granting items (#2650) --- .../overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c index 93eeb9490..07c3ead6e 100644 --- a/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c +++ b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c @@ -136,19 +136,22 @@ void EnDntDemo_Judge(EnDntDemo* this, PlayState* play) { } } else { if (gSaveContext.n64ddFlag) { + Player* player = GET_PLAYER(play); switch (Player_GetMask(play)) { case PLAYER_MASK_SKULL: - if (!Flags_GetTreasure(play, 0x1F)) { + if (!Flags_GetTreasure(play, 0x1F) && !Player_InBlockingCsMode(play, player)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DEKU_THEATER_SKULL_MASK, GI_STICK_UPGRADE_30); GiveItemEntryWithoutActor(play, getItemEntry); - Flags_SetTreasure(play, 0x1F); + player->pendingFlag.flagID = 0x1F; + player->pendingFlag.flagType = FLAG_SCENE_TREASURE; } break; case PLAYER_MASK_TRUTH: - if (!Flags_GetTreasure(play, 0x1E)) { + if (!Flags_GetTreasure(play, 0x1E) && !Player_InBlockingCsMode(play, player)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DEKU_THEATER_MASK_OF_TRUTH, GI_NUT_UPGRADE_40); GiveItemEntryWithoutActor(play, getItemEntry); - Flags_SetTreasure(play, 0x1E); + player->pendingFlag.flagID = 0x1E; + player->pendingFlag.flagType = FLAG_SCENE_TREASURE; } break; } From 21a3bd9f5c6c948d70b22a2cd51ebed7439c8c9f Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 31 Mar 2023 22:05:14 -0400 Subject: [PATCH 4/8] Fix: Actually display correct GS token count in message for vanilla and rando (#2651) * Revert "fix skulltula token count message on vanilla (#2597)" This reverts commit 2117d98178af72e6abdcd2aec66a26d5d8711411. * fix vanilla vs rando gs token count to be correct --- soh/soh/OTRGlobals.cpp | 5 +++- soh/src/overlays/actors/ovl_En_Si/z_en_si.c | 30 +++++++++++---------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index f6e5bc5d8..e76576f39 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1677,8 +1677,11 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { } else { textId = TEXT_GS_FREEZE; } + // In vanilla, GS token count is incremented prior to the text box displaying + // In rando we need to bump the token count by one to show the correct count + s16 gsCount = gSaveContext.inventory.gsTokens + (gSaveContext.n64ddFlag ? 1 : 0); messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); - CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{gsCount}}", std::to_string(gSaveContext.inventory.gsTokens)); + CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{gsCount}}", std::to_string(gsCount)); } } if (textId == TEXT_HEART_CONTAINER && CVarGetInteger("gInjectItemCounts", 0)) { diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c index d9eb19ebe..344173805 100644 --- a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -103,11 +103,6 @@ void func_80AFB768(EnSi* this, PlayState* play) { if (gSaveContext.n64ddFlag) { Randomizer_UpdateSkullReward(this, play); - if (getItemId != RG_ICE_TRAP) { - Randomizer_GiveSkullReward(this, play); - } else { - gSaveContext.pendingIceTrapCount++; - } } else { Item_Give(play, giveItemId); } @@ -122,8 +117,14 @@ void func_80AFB768(EnSi* this, PlayState* play) { Message_StartTextbox(play, textId, NULL); - if (gSaveContext.n64ddFlag && getItemId != RG_ICE_TRAP) { - Audio_PlayFanfare_Rando(getItem); + if (gSaveContext.n64ddFlag) { + if (getItemId != RG_ICE_TRAP) { + Randomizer_GiveSkullReward(this, play); + Audio_PlayFanfare_Rando(getItem); + } else { + gSaveContext.pendingIceTrapCount++; + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + } } else { Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); } @@ -148,19 +149,20 @@ void func_80AFB89C(EnSi* this, PlayState* play) { if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { if (gSaveContext.n64ddFlag) { Randomizer_UpdateSkullReward(this, play); - if (getItemId != RG_ICE_TRAP) { - Randomizer_GiveSkullReward(this, play); - } else { - gSaveContext.pendingIceTrapCount++; - } } else { Item_Give(play, giveItemId); } Message_StartTextbox(play, textId, NULL); - if (gSaveContext.n64ddFlag && getItemId != RG_ICE_TRAP) { - Audio_PlayFanfare_Rando(getItem); + if (gSaveContext.n64ddFlag) { + if (getItemId != RG_ICE_TRAP) { + Randomizer_GiveSkullReward(this, play); + Audio_PlayFanfare_Rando(getItem); + } else { + gSaveContext.pendingIceTrapCount++; + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + } } else { Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); } From 545bc21fbe5be095a757fb9178d9e2172c34c264 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 31 Mar 2023 22:22:45 -0400 Subject: [PATCH 5/8] Tweak: CTMC/CSMC for treasure mini game reward and set loser green rupee as junk (#2652) * set green rupee to junk category for ctmc * allow treasure chest game reward to use CSTMC and cleanup logic --- soh/soh/OTRGlobals.cpp | 2 +- soh/src/overlays/actors/ovl_En_Box/z_en_box.c | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index e76576f39..7a0c10539 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -518,7 +518,7 @@ extern "C" void VanillaItemTable_Init() { GET_ITEM(ITEM_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, GI_POE), GET_ITEM(ITEM_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, 0xF9, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, GI_BIG_POE), GET_ITEM(ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0xF3, 0x80, CHEST_ANIM_SHORT, ITEM_CATEGORY_SMALL_KEY, MOD_NONE, GI_DOOR_KEY), - GET_ITEM(ITEM_RUPEE_GREEN, OBJECT_GI_RUPY, GID_RUPEE_GREEN, 0xF4, 0x00, CHEST_ANIM_SHORT, ITEM_CATEGORY_MAJOR, MOD_NONE, GI_RUPEE_GREEN_LOSE), + GET_ITEM(ITEM_RUPEE_GREEN, OBJECT_GI_RUPY, GID_RUPEE_GREEN, 0xF4, 0x00, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, GI_RUPEE_GREEN_LOSE), GET_ITEM(ITEM_RUPEE_BLUE, OBJECT_GI_RUPY, GID_RUPEE_BLUE, 0xF5, 0x01, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, GI_RUPEE_BLUE_LOSE), GET_ITEM(ITEM_RUPEE_RED, OBJECT_GI_RUPY, GID_RUPEE_RED, 0xF6, 0x02, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, GI_RUPEE_RED_LOSE), GET_ITEM(ITEM_RUPEE_PURPLE, OBJECT_GI_RUPY, GID_RUPEE_PURPLE, 0xF7, 0x14, CHEST_ANIM_SHORT, ITEM_CATEGORY_JUNK, MOD_NONE, GI_RUPEE_PURPLE_LOSE), diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c index 79426dc76..bfc2e9565 100644 --- a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -624,12 +624,14 @@ void EnBox_Update(Actor* thisx, PlayState* play) { void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) { EnBox_CreateExtraChestTextures(); - int cvar = CVarGetInteger("gChestSizeAndTextureMatchesContents", 0); - int agonyCVar = CVarGetInteger("gChestSizeDependsStoneOfAgony", 0); - int stoneCheck = CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY); + int cstmc = CVarGetInteger("gChestSizeAndTextureMatchesContents", 0); + int requiresStoneAgony = CVarGetInteger("gChestSizeDependsStoneOfAgony", 0); GetItemCategory getItemCategory; - if (play->sceneNum != SCENE_TAKARAYA && cvar > 0 && ((agonyCVar > 0 && stoneCheck) | agonyCVar == 0)) { + int isVanilla = cstmc == 0 || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)) || + (play->sceneNum == SCENE_TAKARAYA && this->dyna.actor.room != 6); // Exclude treasure game chests except for the final room + + if (!isVanilla) { getItemCategory = this->getItemEntry.getItemCategory; // If they don't have bombchu's yet consider the bombchu item major if (this->getItemEntry.gid == GID_BOMBCHU && INV_CONTENT(ITEM_BOMBCHU) != ITEM_BOMBCHU) { @@ -645,7 +647,8 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) { } } - if (play->sceneNum != SCENE_TAKARAYA && (cvar == 1 || cvar == 3) && ((agonyCVar > 0 && stoneCheck) | agonyCVar == 0)) { + // Change size + if (!isVanilla && (cstmc == 1 || cstmc == 3)) { switch (getItemCategory) { case ITEM_CATEGORY_JUNK: case ITEM_CATEGORY_SMALL_KEY: @@ -673,7 +676,8 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) { } } - if (play->sceneNum != SCENE_TAKARAYA && (cvar == 1 || cvar == 2) && ((agonyCVar > 0 && stoneCheck) | agonyCVar == 0)) { + // Change texture + if (!isVanilla && (cstmc == 1 || cstmc == 2)) { switch (getItemCategory) { case ITEM_CATEGORY_MAJOR: this->boxBodyDL = gGoldTreasureChestChestFrontDL; From 0c43fe7e48a972b4762b92ef0c55cf5cfc9c73b2 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Fri, 31 Mar 2023 23:13:24 -0400 Subject: [PATCH 6/8] fix mido blocking pathway when link has kokiri emerald (#2653) --- soh/include/z64save.h | 2 +- .../actors/ovl_Door_Warp1/z_door_warp1.c | 2 ++ soh/src/overlays/actors/ovl_En_Md/z_en_md.c | 19 ++++++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/soh/include/z64save.h b/soh/include/z64save.h index d44a703df..e6d27da5e 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -369,7 +369,7 @@ typedef enum { #define EVENTCHKINF_16 0x16 #define EVENTCHKINF_EPONA_OBTAINED 0x18 #define EVENTCHKINF_1B 0x1B -#define EVENTCHKINF_1C 0x1C +#define EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH 0x1C #define EVENTCHKINF_1D 0x1D #define EVENTCHKINF_1E 0x1E #define EVENTCHKINF_20 0x20 diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c index d42458235..094e00113 100644 --- a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -568,6 +568,8 @@ void DoorWarp1_ChildWarpOut(DoorWarp1* this, PlayState* play) { if (gSaveContext.n64ddFlag) { play->nextEntranceIndex = 0x0457; gSaveContext.nextCutsceneIndex = 0; + // Skip Mido complaining about dead Deku tree + Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH); } else { Item_Give(play, ITEM_KOKIRI_EMERALD); play->nextEntranceIndex = 0xEE; diff --git a/soh/src/overlays/actors/ovl_En_Md/z_en_md.c b/soh/src/overlays/actors/ovl_En_Md/z_en_md.c index 21bd913e7..b4b4a7b07 100644 --- a/soh/src/overlays/actors/ovl_En_Md/z_en_md.c +++ b/soh/src/overlays/actors/ovl_En_Md/z_en_md.c @@ -372,8 +372,11 @@ u16 EnMd_GetTextKokiriForest(PlayState* play, EnMd* this) { this->unk_208 = 0; this->unk_209 = TEXT_STATE_NONE; - // In rando, skip talking about the tree being dead so we can have the prompt sword and shield instead - if (!gSaveContext.n64ddFlag && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + // In rando, skip talking about the tree being dead so we can have the prompt for sword and shield instead + if ((!gSaveContext.n64ddFlag && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) || + (gSaveContext.n64ddFlag && Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD) && + Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE) && + !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH))) { return 0x1045; } @@ -492,6 +495,7 @@ u8 EnMd_ShouldSpawn(EnMd* this, PlayState* play) { } if (Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD) && + Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) && (Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER) || Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD))) { return play->sceneNum == SCENE_KOKIRI_HOME4 && !LINK_IS_ADULT; @@ -677,7 +681,8 @@ void EnMd_Init(Actor* thisx, PlayState* play) { if (((play->sceneNum == SCENE_SPOT04) && !(gSaveContext.eventChkInf[0] & 0x10)) || ((play->sceneNum == SCENE_SPOT04) && (gSaveContext.eventChkInf[0] & 0x10) && - CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) || + ((!gSaveContext.n64ddFlag && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) || + (gSaveContext.n64ddFlag && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE)))) || ((play->sceneNum == SCENE_SPOT10) && !(gSaveContext.eventChkInf[0] & 0x400))) { this->actor.home.pos = this->actor.world.pos; this->actionFunc = func_80AAB948; @@ -738,7 +743,9 @@ void func_80AAB948(EnMd* this, PlayState* play) { } if (this->interactInfo.talkState == NPC_TALK_STATE_ACTION) { - if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !(gSaveContext.eventChkInf[1] & 0x1000) && + if ((!gSaveContext.n64ddFlag && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) || + gSaveContext.n64ddFlag && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE) && + Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) && !(gSaveContext.eventChkInf[1] & 0x1000) && (play->sceneNum == SCENE_SPOT04)) { play->msgCtx.msgMode = MSGMODE_PAUSED; } @@ -805,7 +812,9 @@ void func_80AABD0C(EnMd* this, PlayState* play) { return; } - if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !(gSaveContext.eventChkInf[1] & 0x1000) && + if ((!gSaveContext.n64ddFlag && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) || + gSaveContext.n64ddFlag && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE) && + Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) && !(gSaveContext.eventChkInf[1] & 0x1000) && (play->sceneNum == SCENE_SPOT04)) { Message_CloseTextbox(play); gSaveContext.eventChkInf[1] |= 0x1000; From f7703e14e85d7646248320168dc0f4aa3c6c43de Mon Sep 17 00:00:00 2001 From: Malkierian Date: Fri, 31 Mar 2023 20:35:29 -0700 Subject: [PATCH 7/8] Fix Missing Custom Sequences (#2649) * Modifies custom sequence loading to bypass other sounds being loaded in AudioCollection to fix missing custom sequences on load. * Modified `audio_load`'s sequenceMap allocation to utilize `AudioCollection`'s sequenceMap size to account for all audio assets already loaded into that sequenceMap. This gives a non-arbitrary number in addition to the vanilla sequence count to allocate with for `audio_load`'s sequenceMap. Added `HasSequenceNum` to `AudioCollection` as well to streamline the check against `AudioCollection`'s sequenceMap to skip the non-sequence assets in there. Added clarification comment for seqNum and MAX_AUTHENTIC_SEQID section. * Clarified comment about AudioCollection seqNum and MAX_AUTHENTIC_SEQID. * A bit more on comment from the last commit. --- soh/soh/Enhancements/audio/AudioCollection.cpp | 16 ++++++++++++++++ soh/soh/Enhancements/audio/AudioCollection.h | 4 ++++ soh/src/code/audio_load.c | 17 ++++++++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/audio/AudioCollection.cpp b/soh/soh/Enhancements/audio/AudioCollection.cpp index 26494a7d5..3d988c9be 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.cpp +++ b/soh/soh/Enhancements/audio/AudioCollection.cpp @@ -261,6 +261,10 @@ extern "C" void AudioCollection_AddToCollection(char *otrPath, uint16_t seqNum) AudioCollection::Instance->AddToCollection(otrPath, seqNum); } +bool AudioCollection::HasSequenceNum(uint16_t seqId) { + return sequenceMap.contains(seqId); +} + const char* AudioCollection::GetSequenceName(uint16_t seqId) { auto seqIt = sequenceMap.find(seqId); if (seqIt != sequenceMap.end()) { @@ -269,6 +273,18 @@ const char* AudioCollection::GetSequenceName(uint16_t seqId) { return nullptr; } +size_t AudioCollection::SequenceMapSize() { + return sequenceMap.size(); +} + extern "C" const char* AudioCollection_GetSequenceName(uint16_t seqId) { return AudioCollection::Instance->GetSequenceName(seqId); +} + +extern "C" bool AudioCollection_HasSequenceNum(uint16_t seqId) { + return AudioCollection::Instance->HasSequenceNum(seqId); +} + +extern "C" size_t AudioCollection_SequenceMapSize() { + return AudioCollection::Instance->SequenceMapSize(); } \ No newline at end of file diff --git a/soh/soh/Enhancements/audio/AudioCollection.h b/soh/soh/Enhancements/audio/AudioCollection.h index 6b7a78fa0..75247793b 100644 --- a/soh/soh/Enhancements/audio/AudioCollection.h +++ b/soh/soh/Enhancements/audio/AudioCollection.h @@ -58,8 +58,12 @@ class AudioCollection { uint16_t GetReplacementSequence(uint16_t seqId); void InitializeShufflePool(); const char* GetSequenceName(uint16_t seqId); + bool HasSequenceNum(uint16_t seqId); + size_t SequenceMapSize(); }; #else void AudioCollection_AddToCollection(char *otrPath, uint16_t seqNum); const char* AudioCollection_GetSequenceName(uint16_t seqId); +bool AudioCollection_HasSequenceNum(uint16_t seqId); +size_t AudioCollection_SequenceMapSize(); #endif \ No newline at end of file diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index eb788fd0c..583ce84fe 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -1346,7 +1346,7 @@ void AudioLoad_Init(void* heap, size_t heapSize) { int customSeqListSize = 0; char** seqList = ResourceMgr_ListFiles("audio/sequences*", &seqListSize); char** customSeqList = ResourceMgr_ListFiles("custom/music/*", &customSeqListSize); - sequenceMapSize = (size_t)(seqListSize + customSeqListSize); + sequenceMapSize = (size_t)(AudioCollection_SequenceMapSize() + customSeqListSize); sequenceMap = malloc(sequenceMapSize * sizeof(char*)); gAudioContext.seqLoadStatus = malloc(sequenceMapSize * sizeof(char*)); @@ -1366,16 +1366,27 @@ void AudioLoad_Init(void* heap, size_t heapSize) { int startingSeqNum = MAX_AUTHENTIC_SEQID; // 109 is the highest vanilla sequence qsort(customSeqList, customSeqListSize, sizeof(char*), strcmp_sort); + // Because AudioCollection's sequenceMap actually has more than sequences (including instruments from 130-135 and sfx in the 2000s, 6000s, 10000s, 14000s, 18000s, and 26000s), + // it's better here to keep track of the next empty seqNum in AudioCollection instead of just skipping past the instruments at 130 with a higher MAX_AUTHENTIC_SEQID, + // especially if those others could be added to in the future. However, this really needs to be streamlined with specific ranges in AudioCollection for types, or unifying + // AudioCollection and the various maps in here + int seqNum = startingSeqNum; + for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) { + // ensure that what would be the next sequence number is actually unassigned in AudioCollection + while (AudioCollection_HasSequenceNum(seqNum)) { + seqNum++; + } int j = i - startingSeqNum; - AudioCollection_AddToCollection(customSeqList[j], i); + AudioCollection_AddToCollection(customSeqList[j], seqNum); SequenceData sDat = ResourceMgr_LoadSeqByName(customSeqList[j]); - sDat.seqNumber = i; + sDat.seqNumber = seqNum; char* str = malloc(strlen(customSeqList[j]) + 1); strcpy(str, customSeqList[j]); sequenceMap[sDat.seqNumber] = str; + seqNum++; } free(customSeqList); From f5bcc6dccfb52499da641f8b00e792b259e01bc0 Mon Sep 17 00:00:00 2001 From: Adam Bird Date: Sat, 1 Apr 2023 14:06:53 -0400 Subject: [PATCH 8/8] fix crash when fishing rod is cast and scene is reloaded (#2657) --- soh/src/overlays/actors/ovl_Fishing/z_fishing.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c index 29dc52c7f..e67039659 100644 --- a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -22,6 +22,7 @@ void Fishing_UpdateFish(Actor* thisx, PlayState* play); void Fishing_UpdateOwner(Actor* thisx, PlayState* play); void Fishing_DrawFish(Actor* thisx, PlayState* play); void Fishing_DrawOwner(Actor* thisx, PlayState* play); +void Fishing_Reset(void); typedef struct { /* 0x00 */ u8 unk_00; @@ -132,7 +133,7 @@ const ActorInit Fishing_InitVars = { (ActorFunc)Fishing_Destroy, (ActorFunc)Fishing_UpdateFish, (ActorFunc)Fishing_DrawFish, - NULL, + (ActorResetFunc)Fishing_Reset, }; static f32 D_80B7A650 = 0.0f; @@ -5888,3 +5889,10 @@ void Fishing_DrawOwner(Actor* thisx, PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } + +void Fishing_Reset(void) { + // Reset static variables for fishing camera and cinematic state to prevent crashing when dying + // or re-entering the scene while the fishing rod was cast + sCameraId = 0; + D_80B7A6CC = 0; +}