diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 2323e7222..230d7aa1d 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -242,6 +242,9 @@ typedef enum { ``` */ VB_DRAW_AMMO_COUNT, + // Opt: *ObjTsubo + VB_POT_DRAW, + VB_POT_DROP_ITEM, /*** Play Cutscenes ***/ diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 82deeaf80..13a8021e7 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -64,6 +64,9 @@ void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)); //Mark: - Pause Menu void GameInteractor_ExecuteOnKaleidoUpdate(); +//Mark - Randomizer options spawning EnItem00 actors +void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play); + #ifdef __cplusplus } #endif diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 2571b7d96..12fb4e4d7 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -14,6 +14,7 @@ #include "soh/Enhancements/TimeSavers/TimeSavers.h" #include "soh/Enhancements/cheat_hook_handlers.h" #include "soh/Enhancements/randomizer/hook_handlers.h" +#include "soh/Enhancements/randomizer/ShufflePots.h" #include "objects/object_gi_compass/object_gi_compass.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" @@ -1822,4 +1823,5 @@ void InitMods() { RegisterHurtContainerModeHandler(); RegisterPauseMenuHooks(); RegisterSkeletonKey(); + RegisterShufflePots(); } diff --git a/soh/soh/Enhancements/randomizer/ShufflePots.cpp b/soh/soh/Enhancements/randomizer/ShufflePots.cpp new file mode 100644 index 000000000..65e27fef6 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShufflePots.cpp @@ -0,0 +1,126 @@ +#include "ShufflePots.h" +#include "soh_assets.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/OTRGlobals.h" + +extern "C" { +#include "overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.h" +#include "variables.h" + +u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); +GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId); +PotIdentity Randomizer_IdentifyPot(s32 sceneNum, s32 posX, s32 posZ); +extern PlayState* gPlayState; +} + + +extern "C" void ObjTsubo_RandomizerDraw(Actor* thisx, PlayState* play) { + float potSize = 1.0f; + + OPEN_DISPS(play->state.gfxCtx); + Gfx_SetupDL_25Opa(play->state.gfxCtx); + Matrix_Scale(potSize, potSize, potSize, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), + G_MTX_MODELVIEW | G_MTX_LOAD); + + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gRandoPotDL); + CLOSE_DISPS(play->state.gfxCtx); +} + +uint8_t ObjTsubo_RandomizerHoldsItem(ObjTsubo* potActor, PlayState* play) { + uint8_t isDungeon = + play->sceneNum < SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || + (play->sceneNum > SCENE_TREASURE_BOX_SHOP && play->sceneNum < SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR); + uint8_t potSetting = Randomizer_GetSettingValue(RSK_SHUFFLE_POTS); + + // Don't pull randomized item if pot isn't randomized or is already checked + if (!IS_RANDO || (potSetting == RO_SHUFFLE_POTS_OVERWORLD && isDungeon) || + (potSetting == RO_SHUFFLE_POTS_DUNGEONS && !isDungeon) || + Flags_GetRandomizerInf(potActor->potIdentity.randomizerInf) || + potActor->potIdentity.randomizerCheck == RC_UNKNOWN_CHECK) { + return false; + } else { + return true; + } +} + +uint8_t ObjTsubo_RandomizerSkipItemCutscene(ObjTsubo* potActor) { + /*return + potActor->actor.params == ITEM00_SMALL_KEY && giEntry.modIndex == MOD_NONE && + ((giEntry.itemId >= ITEM_RUPEE_GREEN && giEntry.itemId <= ITEM_RUPEE_RED) || giEntry.itemId == ITEM_HEART || + (giEntry.itemId >= ITEM_NUTS_5 && giEntry.itemId <= ITEM_SEEDS_30) || giEntry.itemId == ITEM_MAGIC_SMALL || + giEntry.itemId == ITEM_MAGIC_LARGE);*/ +} + +void ObjTsubo_RandomizerSpawnCollectible(ObjTsubo* potActor) { + EnItem00* item00 = + (EnItem00*)Item_DropCollectible2(gPlayState, &potActor->actor.world.pos, ITEM00_SOH_GIVE_ITEM_ENTRY); + item00->randoInf = potActor->potIdentity.randomizerInf; + item00->itemEntry = + OTRGlobals::Instance->gRandomizer->GetItemFromKnownCheck(potActor->potIdentity.randomizerCheck, GI_NONE); + item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem; + item00->actor.velocity.y = 8.0f; + item00->actor.speedXZ = 2.0f; + item00->actor.gravity = -0.9f; + item00->actor.world.rot.y = Rand_CenteredFloat(65536.0f); +} + +void ObjTsubo_RandomizerInit(void* actorRef) { + Actor* actor = static_cast(actorRef); + + if (actor->id != ACTOR_OBJ_TSUBO) return; + + ObjTsubo* potActor = static_cast(actorRef); + + potActor->potIdentity = + Randomizer_IdentifyPot(gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z); + + if (ObjTsubo_RandomizerHoldsItem(potActor, gPlayState)) { + potActor->potIdentity.isShuffled = true; + } else { + potActor->potIdentity.isShuffled = false; + } +} + +void PotOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void* optionalArg) { + ObjTsubo* potActor = static_cast(optionalArg); + + switch (id) { + case VB_POT_DRAW: { + if (potActor->potIdentity.isShuffled) { + *should = false; + potActor->actor.draw = (ActorFunc)ObjTsubo_RandomizerDraw; + } + break; + } + case VB_POT_DROP_ITEM: { + if (potActor->potIdentity.isShuffled) { + *should = false; + ObjTsubo_RandomizerSpawnCollectible(potActor); + } + break; + } + } +} + +void RegisterShufflePots() { + static uint32_t onActorInitHook = 0; + static uint32_t onVanillaBehaviorHook = 0; + + GameInteractor::Instance->RegisterGameHook([](int32_t fileNum) { + + GameInteractor::Instance->UnregisterGameHook(onActorInitHook); + GameInteractor::Instance->UnregisterGameHook(onVanillaBehaviorHook); + + onActorInitHook = 0; + onVanillaBehaviorHook = 0; + + if (!IS_RANDO) return; + if (!Randomizer_GetSettingValue(RSK_SHUFFLE_POTS)) return; + + onActorInitHook = GameInteractor::Instance->RegisterGameHook(ObjTsubo_RandomizerInit); + onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook(PotOnVanillaBehaviorHandler); + + }); +} diff --git a/soh/soh/Enhancements/randomizer/ShufflePots.h b/soh/soh/Enhancements/randomizer/ShufflePots.h new file mode 100644 index 000000000..f2b1aa23c --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShufflePots.h @@ -0,0 +1,15 @@ +#ifndef SHUFFLEPOTS_H +#define SHUFFLEPOTS_H + +#include "z64.h" + +#ifdef __cplusplus +extern "C" { +#endif +void ObjTsubo_RandomizerDraw(Actor* potActor, PlayState* play); +void RegisterShufflePots(); +#ifdef __cplusplus +}; +#endif + +#endif //SHUFFLEPOTS_H diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index b42cbccf9..9eadfa3e7 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -4899,6 +4899,7 @@ typedef struct CowIdentity { typedef struct PotIdentity { RandomizerInf randomizerInf; RandomizerCheck randomizerCheck; + uint8_t isShuffled; } PotIdentity; typedef struct FishIdentity { diff --git a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c index 23350794c..c0f6a8237 100644 --- a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c +++ b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c @@ -8,7 +8,7 @@ #include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" #include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" #include "objects/object_tsubo/object_tsubo.h" -#include "soh_assets.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS (ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_ALWAYS_THROWN) @@ -84,43 +84,11 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), }; -s8 ObjTsubo_HoldsRandomizedItem(ObjTsubo* this, PlayState* play) { - uint8_t isDungeon = play->sceneNum < SCENE_GANONS_TOWER_COLLAPSE_INTERIOR || - (play->sceneNum > SCENE_TREASURE_BOX_SHOP && play->sceneNum < SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR); - uint8_t potSetting = Randomizer_GetSettingValue(RSK_SHUFFLE_POTS); - - // Don't pull randomized item if pot isn't randomized or is already checked - if (!IS_RANDO || !potSetting || - (potSetting == RO_SHUFFLE_POTS_OVERWORLD && isDungeon) || - (potSetting == RO_SHUFFLE_POTS_DUNGEONS && !isDungeon) || - Flags_GetRandomizerInf(this->potIdentity.randomizerInf) || - this->potIdentity.randomizerCheck == RC_UNKNOWN_CHECK) { - return false; - } else { - return true; - } -} - void ObjTsubo_SpawnCollectible(ObjTsubo* this, PlayState* play) { - - if (IS_RANDO && ObjTsubo_HoldsRandomizedItem(this, play)) { - GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(this->potIdentity.randomizerCheck, GI_NONE); - - EnItem00* actor = (EnItem00*)Item_DropCollectible2(play, &this->actor.world.pos, ITEM00_SMALL_KEY); - actor->randoCheck = this->potIdentity.randomizerCheck; - actor->randoGiEntry = getItemEntry; - actor->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING; - actor->randoInf = this->potIdentity.randomizerInf; - actor->actor.velocity.y = 8.0f; - actor->actor.speedXZ = 2.0f; - actor->actor.gravity = -0.9f; - actor->actor.world.rot.y = Rand_CenteredFloat(65536.0f); - return; - } - s16 dropParams = this->actor.params & 0x1F; - if ((dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL)) { + if ((dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL) && + GameInteractor_Should(VB_POT_DROP_ITEM, true, this)) { Item_DropCollectible(play, &this->actor.world.pos, (dropParams | (((this->actor.params >> 9) & 0x3F) << 8))); } @@ -179,9 +147,6 @@ void ObjTsubo_Init(Actor* thisx, PlayState* play) { ObjTsubo_SetupWaitForObject(this); osSyncPrintf("(dungeon keep 壷)(arg_data 0x%04x)\n", this->actor.params); } - if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_POTS)) { - this->potIdentity = Randomizer_IdentifyPot(play->sceneNum, (s16)this->actor.world.pos.x, (s16)this->actor.world.pos.z); - } } void ObjTsubo_Destroy(Actor* thisx, PlayState* play2) { @@ -268,7 +233,9 @@ void ObjTsubo_SetupWaitForObject(ObjTsubo* this) { void ObjTsubo_WaitForObject(ObjTsubo* this, PlayState* play) { if (Object_IsLoaded(&play->objectCtx, this->objTsuboBankIndex)) { - this->actor.draw = ObjTsubo_Draw; + if (GameInteractor_Should(VB_POT_DRAW, true, this)) { + this->actor.draw = ObjTsubo_Draw; + } this->actor.objBankIndex = this->objTsuboBankIndex; ObjTsubo_SetupIdle(this); this->actor.flags &= ~ACTOR_FLAG_UPDATE_WHILE_CULLED; @@ -381,18 +348,5 @@ void ObjTsubo_Update(Actor* thisx, PlayState* play) { void ObjTsubo_Draw(Actor* thisx, PlayState* play) { ObjTsubo* this = (ObjTsubo*)thisx; - if (IS_RANDO && ObjTsubo_HoldsRandomizedItem(this, play)) { - float potSize = 1.0f; - - OPEN_DISPS(play->state.gfxCtx); - Gfx_SetupDL_25Opa(play->state.gfxCtx); - Matrix_Scale(potSize, potSize, potSize, MTXMODE_APPLY); - gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), - G_MTX_MODELVIEW | G_MTX_LOAD); - - gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gRandoPotDL); - CLOSE_DISPS(play->state.gfxCtx); - } else { - Gfx_DrawDListOpa(play, D_80BA1B84[(thisx->params >> 8) & 1]); - } + Gfx_DrawDListOpa(play, D_80BA1B84[(thisx->params >> 8) & 1]); } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 83c29c2e8..c5d1a65bd 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -6821,16 +6821,8 @@ s32 Player_ActionChange_2(Player* this, PlayState* play) { // 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; - // Automatically skip the pickup messages for very frequent items coming from pots with "Shuffle Pots" on. - uint8_t isPotItemToSkip = interactedActor->id == ACTOR_EN_ITEM00 && - interactedActor->params == ITEM00_SMALL_KEY && giEntry.modIndex == MOD_NONE && - ((giEntry.itemId >= ITEM_RUPEE_GREEN && giEntry.itemId <= ITEM_RUPEE_RED) || - giEntry.itemId == ITEM_HEART || - (giEntry.itemId >= ITEM_NUTS_5 && giEntry.itemId <= ITEM_SEEDS_30) || - giEntry.itemId == ITEM_MAGIC_SMALL || giEntry.itemId == ITEM_MAGIC_LARGE); - // Show cutscene when picking up a item. - if (showItemCutscene && !skipItemCutscene && !skipItemCutsceneRando && !isPotItemToSkip) { + if (showItemCutscene && !skipItemCutscene && !skipItemCutsceneRando) { Player_DetachHeldActor(play, this); func_8083AE40(this, giEntry.objectId);