mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-01-30 23:10:14 -05:00
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
This commit is contained in:
parent
21d82e7c4c
commit
ca23d87a3a
@ -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);
|
||||
|
@ -30,6 +30,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorUpdate>(actor);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnPlayerBonk() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerBonk>();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<Actor*>(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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -264,6 +264,46 @@ void RegisterRupeeDash() {
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterHyperBosses() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](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<Actor*>(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<GameInteractor::OnPlayerBonk>([]() {
|
||||
uint8_t bonkOption = CVarGetInteger("gBonkDamageMul", 0);
|
||||
@ -322,7 +362,8 @@ void InitMods() {
|
||||
RegisterUnrestrictedItems();
|
||||
RegisterFreezeTime();
|
||||
RegisterSwitchAge();
|
||||
RegisterRupeeDash();
|
||||
RegisterAutoSave();
|
||||
RegisterRupeeDash();
|
||||
RegisterHyperBosses();
|
||||
RegisterBonkDamage();
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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 <string.h>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user