From ca23d87a3abc7184850011a1ea09b949c957dbe9 Mon Sep 17 00:00:00 2001 From: aMannus Date: Sun, 2 Apr 2023 10:00:21 +0200 Subject: [PATCH] Hyper Bosses (act and move twice as fast) (#2555) * Hyper bosses * Actor* to void* + cast to fix build * Cleaner implementation * Fix enemies taking double damage * Fix smaller dodongo's being sped up * Additional fix and code cleanup * Proper fix for double damage * Extern variable -> GI state --- .../game-interactor/GameInteractor.h | 4 ++ .../game-interactor/GameInteractor_Hooks.cpp | 4 ++ .../game-interactor/GameInteractor_Hooks.h | 1 + .../GameInteractor_RawAction.cpp | 21 +++++++++ .../game-interactor/GameInteractor_State.cpp | 6 +++ soh/soh/Enhancements/mods.cpp | 43 ++++++++++++++++++- soh/soh/GameMenuBar.cpp | 2 + soh/src/code/z_actor.c | 2 + soh/src/code/z_collision_check.c | 35 +++++++++++++++ .../actors/ovl_Boss_Dodongo/z_boss_dodongo.c | 2 +- 10 files changed, 118 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 3da93146e..1489c1d8f 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -82,6 +82,7 @@ uint8_t GameInteractor_GetDisableLedgeGrabsActive(); uint8_t GameInteractor_GetRandomWindActive(); uint8_t GameInteractor_GetRandomBonksActive(); uint8_t GameInteractor_GetSlipperyFloorActive(); +uint8_t GameInteractor_SecondCollisionUpdate(); #ifdef __cplusplus } #endif @@ -121,6 +122,7 @@ public: static uint8_t RandomWindSecondsSinceLastDirectionChange; static uint8_t RandomBonksActive; static uint8_t SlipperyFloorActive; + static uint8_t SecondCollisionUpdate; static void SetPacifistMode(bool active); }; @@ -146,6 +148,7 @@ public: DEFINE_HOOK(OnSaleEnd, void(GetItemEntry itemEntry)); DEFINE_HOOK(OnSceneInit, void(int16_t sceneNum)); DEFINE_HOOK(OnPlayerUpdate, void()); + DEFINE_HOOK(OnActorUpdate, void(void* actor)); DEFINE_HOOK(OnPlayerBonk, void()); DEFINE_HOOK(OnSaveFile, void(int32_t fileNum)); @@ -189,6 +192,7 @@ public: static void KnockbackPlayer(float strength); static void GiveOrTakeShield(int32_t shield); static void ForceInterfaceUpdate(); + static void UpdateActor(void* refActor); static void TeleportPlayer(int32_t nextEntrance); static void ClearAssignedButtons(uint8_t buttonSet); static void SetTimeOfDay(uint32_t time); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 9b830563b..09153f5ed 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -30,6 +30,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnActorUpdate(void* actor) { + GameInteractor::Instance->ExecuteHooks(actor); +} + void GameInteractor_ExecuteOnPlayerBonk() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 3a6373eff..6449c3613 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -8,6 +8,7 @@ extern "C" void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) extern "C" void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); extern "C" void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); extern "C" void GameInteractor_ExecuteOnPlayerUpdate(); +extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor); extern "C" void GameInteractor_ExecuteOnPlayerBonk(); // MARK: - Save Files diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 20cc19464..3f40889ea 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -178,6 +178,27 @@ void GameInteractor::RawAction::ForceInterfaceUpdate() { Interface_Update(gPlayState); } +void GameInteractor::RawAction::UpdateActor(void* refActor) { + // Update actor again outside of their normal update cycle. + + Actor* actor = static_cast(refActor); + + // Sometimes the actor is destroyed in the previous Update, so check if the update function still exists. + if (actor->update != NULL) { + // Fix for enemies sometimes taking a "fake" hit, where their invincibility timer is + // reset but damage isn't applied. + if (actor->colorFilterTimer > 0) { + actor->colorFilterTimer--; + } + + // This variable is used to not let the collider subscribe a second time when the actor update function + // is ran a second time, incorrectly applying double damage in some cases. + GameInteractor::State::SecondCollisionUpdate = 1; + actor->update(actor, gPlayState); + GameInteractor::State::SecondCollisionUpdate = 0; + } +} + void GameInteractor::RawAction::TeleportPlayer(int32_t nextEntrance) { Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); gPlayState->nextEntranceIndex = nextEntrance; diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp index 00fdd5858..881fb7ff5 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp @@ -19,6 +19,7 @@ uint8_t GameInteractor::State::RandomWindActive = 0; uint8_t GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0; uint8_t GameInteractor::State::RandomBonksActive = 0; uint8_t GameInteractor::State::SlipperyFloorActive = 0; +uint8_t GameInteractor::State::SecondCollisionUpdate = 0; void GameInteractor::State::SetPacifistMode(bool active) { PacifistModeActive = active; @@ -121,3 +122,8 @@ uint8_t GameInteractor_GetRandomBonksActive() { uint8_t GameInteractor_GetSlipperyFloorActive() { return GameInteractor::State::SlipperyFloorActive; } + +// MARK: - GameInteractor::State::SecondCollisionUpdate +uint8_t GameInteractor_SecondCollisionUpdate() { + return GameInteractor::State::SecondCollisionUpdate; +} diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 95023dcf3..917c6b495 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -264,6 +264,46 @@ void RegisterRupeeDash() { }); } +void RegisterHyperBosses() { + GameInteractor::Instance->RegisterGameHook([](void* refActor) { + // Run the update function a second time to make bosses move and act twice as fast. + + Player* player = GET_PLAYER(gPlayState); + Actor* actor = static_cast(refActor); + + uint8_t isBossActor = + actor->id == ACTOR_BOSS_GOMA || // Gohma + actor->id == ACTOR_BOSS_DODONGO || // King Dodongo + actor->id == ACTOR_BOSS_VA || // Barinade + actor->id == ACTOR_BOSS_GANONDROF || // Phantom Ganon + (actor->id == 0 && actor->category == ACTORCAT_BOSS) || // Phantom Ganon/Ganondorf Energy Ball/Thunder + actor->id == ACTOR_EN_FHG || // Phantom Ganon's Horse + actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || // Volvagia (grounded/flying) + actor->id == ACTOR_BOSS_MO || // Morpha + actor->id == ACTOR_BOSS_SST || // Bongo Bongo + actor->id == ACTOR_BOSS_TW || // Twinrova + actor->id == ACTOR_BOSS_GANON || // Ganondorf + actor->id == ACTOR_BOSS_GANON2; // Ganon + + // Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses. + if (CVarGetInteger("gHyperBosses", 0) && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) { + // Barinade needs to be updated in sequence to avoid unintended behaviour. + if (actor->id == ACTOR_BOSS_VA) { + // params -1 is BOSSVA_BODY + if (actor->params == -1) { + Actor* actorList = gPlayState->actorCtx.actorLists[ACTORCAT_BOSS].head; + while (actorList != NULL) { + GameInteractor::RawAction::UpdateActor(actorList); + actorList = actorList->next; + } + } + } else { + GameInteractor::RawAction::UpdateActor(actor); + } + } + }); +} + void RegisterBonkDamage() { GameInteractor::Instance->RegisterGameHook([]() { uint8_t bonkOption = CVarGetInteger("gBonkDamageMul", 0); @@ -322,7 +362,8 @@ void InitMods() { RegisterUnrestrictedItems(); RegisterFreezeTime(); RegisterSwitchAge(); - RegisterRupeeDash(); RegisterAutoSave(); + RegisterRupeeDash(); + RegisterHyperBosses(); RegisterBonkDamage(); } diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index fddaa7ee1..a421d4667 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -446,6 +446,8 @@ namespace GameMenuBar { UIWidgets::Tooltip("Bombchus will sometimes drop in place of bombs"); UIWidgets::PaddedEnhancementCheckbox("No Heart Drops", "gNoHeartDrops", true, false); UIWidgets::Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series"); + UIWidgets::PaddedEnhancementCheckbox("Hyper Bosses", "gHyperBosses", true, false); + UIWidgets::Tooltip("All major bosses move and act twice as fast."); UIWidgets::PaddedEnhancementCheckbox("Always Win Goron Pot", "gGoronPot", true, false); UIWidgets::Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot"); UIWidgets::PaddedEnhancementCheckbox("Always Win Dampe Digging Game", "gDampeWin", true, false, SaveManager::Instance->IsRandoFile(), diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 9394c3b8a..ef7708919 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -8,6 +8,7 @@ #include "objects/object_bdoor/object_bdoor.h" #include "soh/frame_interpolation.h" #include "soh/Enhancements/enemyrandomizer.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" #if defined(_MSC_VER) || defined(__GNUC__) #include @@ -2597,6 +2598,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { actor->colorFilterTimer--; } actor->update(actor, play); + GameInteractor_ExecuteOnActorUpdate(actor, play); func_8003F8EC(play, &play->colCtx.dyna, actor); } diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c index c27f35d6d..785e68836 100644 --- a/soh/src/code/z_collision_check.c +++ b/soh/src/code/z_collision_check.c @@ -1,6 +1,7 @@ #include "global.h" #include "vt.h" #include "overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" typedef s32 (*ColChkResetFunc)(PlayState*, Collider*); typedef void (*ColChkBloodFunc)(PlayState*, Collider*, Vec3f*); @@ -1177,6 +1178,10 @@ static ColChkResetFunc sATResetFuncs[] = { s32 CollisionCheck_SetAT(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider) { s32 index; + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } @@ -1206,9 +1211,15 @@ s32 CollisionCheck_SetAT(PlayState* play, CollisionCheckContext* colChkCtx, Coll s32 CollisionCheck_SetAT_SAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider, s32 index) { ASSERT(collider->shape <= COLSHAPE_QUAD); + + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } + sATResetFuncs[collider->shape](play, collider); if (collider->actor != NULL && collider->actor->update == NULL) { return -1; @@ -1246,6 +1257,10 @@ static ColChkResetFunc sACResetFuncs[] = { s32 CollisionCheck_SetAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider) { s32 index; + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } @@ -1275,9 +1290,15 @@ s32 CollisionCheck_SetAC(PlayState* play, CollisionCheckContext* colChkCtx, Coll s32 CollisionCheck_SetAC_SAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider, s32 index) { ASSERT(collider->shape <= COLSHAPE_QUAD); + + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } + sACResetFuncs[collider->shape](play, collider); if (collider->actor != NULL && collider->actor->update == NULL) { return -1; @@ -1315,6 +1336,10 @@ static ColChkResetFunc sOCResetFuncs[] = { s32 CollisionCheck_SetOC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider) { s32 index; + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } @@ -1345,9 +1370,15 @@ s32 CollisionCheck_SetOC(PlayState* play, CollisionCheckContext* colChkCtx, Coll */ s32 CollisionCheck_SetOC_SAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider, s32 index) { + + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } + ASSERT(collider->shape <= COLSHAPE_QUAD); sOCResetFuncs[collider->shape](play, collider); if (collider->actor != NULL && collider->actor->update == NULL) { @@ -1380,6 +1411,10 @@ s32 CollisionCheck_SetOC_SAC(PlayState* play, CollisionCheckContext* colChkCtx, s32 CollisionCheck_SetOCLine(PlayState* play, CollisionCheckContext* colChkCtx, OcLine* collider) { s32 index; + if (GameInteractor_SecondCollisionUpdate()) { + return -1; + } + if (FrameAdvance_IsEnabled(play) == true) { return -1; } diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index 63896bc92..255b0034f 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -35,7 +35,7 @@ void BossDodongo_DrawEffects(PlayState* play); void BossDodongo_UpdateEffects(PlayState* play); const ActorInit Boss_Dodongo_InitVars = { - ACTOR_EN_DODONGO, + ACTOR_BOSS_DODONGO, ACTORCAT_BOSS, FLAGS, OBJECT_KINGDODONGO,