Support hook unregistration (#3538)

This commit is contained in:
Garrett Cox 2024-02-15 20:08:31 -06:00 committed by GitHub
parent bdfcf39e56
commit a84227cbbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 106 additions and 58 deletions

View File

@ -163,11 +163,30 @@ public:
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
// Game Hooks
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; };
template <typename H> void RegisterGameHook(typename H::fn h) { RegisteredGameHooks<H>::functions.push_back(h); }
uint32_t nextHookId = 1;
template <typename H> struct RegisteredGameHooks { inline static std::unordered_map<uint32_t, typename H::fn> functions; };
template <typename H> struct HooksToUnregister { inline static std::vector<uint32_t> hooks; };
template <typename H> uint32_t RegisterGameHook(typename H::fn h) {
// Ensure hook id is unique and not 0, which is reserved for invalid hooks
if (this->nextHookId == 0 || this->nextHookId >= UINT32_MAX) this->nextHookId = 1;
while (RegisteredGameHooks<H>::functions.find(this->nextHookId) != RegisteredGameHooks<H>::functions.end()) {
this->nextHookId++;
}
RegisteredGameHooks<H>::functions[this->nextHookId] = h;
return this->nextHookId++;
}
template <typename H> void UnregisterGameHook(uint32_t id) {
HooksToUnregister<H>::hooks.push_back(id);
}
template <typename H, typename... Args> void ExecuteHooks(Args&&... args) {
for (auto& fn : RegisteredGameHooks<H>::functions) {
fn(std::forward<Args>(args)...);
for (auto& hookId : HooksToUnregister<H>::hooks) {
RegisteredGameHooks<H>::functions.erase(hookId);
}
HooksToUnregister<H>::hooks.clear();
for (auto& hook : RegisteredGameHooks<H>::functions) {
hook.second(std::forward<Args>(args)...);
}
}

View File

@ -519,8 +519,20 @@ void RegisterDaytimeGoldSkultullas() {
});
}
void RegisterHyperBosses() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
bool IsHyperBossesActive() {
return CVarGetInteger("gHyperBosses", 0) ||
(IS_BOSS_RUSH && gSaveContext.bossRushOptions[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES);
}
void UpdateHyperBossesState() {
static uint32_t actorUpdateHookId = 0;
if (actorUpdateHookId != 0) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(actorUpdateHookId);
actorUpdateHookId = 0;
}
if (IsHyperBossesActive()) {
actorUpdateHookId = 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);
@ -542,13 +554,8 @@ void RegisterHyperBosses() {
actor->id == ACTOR_BOSS_GANON || // Ganondorf
actor->id == ACTOR_BOSS_GANON2; // Ganon
uint8_t hyperBossesActive =
CVarGetInteger("gHyperBosses", 0) ||
(IS_BOSS_RUSH &&
gSaveContext.bossRushOptions[BR_OPTIONS_HYPERBOSSES] == BR_CHOICE_HYPERBOSSES_YES);
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses.
if (hyperBossesActive && isBossActor && !Player_InBlockingCsMode(gPlayState, player)) {
if (IsHyperBossesActive() && 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
@ -564,10 +571,25 @@ void RegisterHyperBosses() {
}
}
});
}
}
void RegisterHyperEnemies() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
void RegisterHyperBosses() {
UpdateHyperBossesState();
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int16_t fileNum) {
UpdateHyperBossesState();
});
}
void UpdateHyperEnemiesState() {
static uint32_t actorUpdateHookId = 0;
if (actorUpdateHookId != 0) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(actorUpdateHookId);
actorUpdateHookId = 0;
}
if (CVarGetInteger("gHyperEnemies", 0)) {
actorUpdateHookId = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make enemies and minibosses move and act twice as fast.
Player* player = GET_PLAYER(gPlayState);
@ -583,6 +605,7 @@ void RegisterHyperEnemies() {
GameInteractor::RawAction::UpdateActor(actor);
}
});
}
}
void RegisterBonkDamage() {
@ -1371,7 +1394,7 @@ void InitMods() {
RegisterPermanentHeartLoss();
RegisterDeleteFileOnDeath();
RegisterHyperBosses();
RegisterHyperEnemies();
UpdateHyperEnemiesState();
RegisterBonkDamage();
RegisterMenuPathFix();
RegisterMirrorModeHandler();

View File

@ -12,6 +12,8 @@ void UpdateMirrorModeState(int32_t sceneNum);
void UpdateHurtContainerModeState(bool newState);
void PatchToTMedallions();
void UpdatePermanentHeartLossState();
void UpdateHyperEnemiesState();
void UpdateHyperBossesState();
void InitMods();
void UpdatePatchHand();

View File

@ -959,9 +959,13 @@ void DrawEnhancementsMenu() {
UIWidgets::Tooltip("Bonking into trees will have a chance to drop up to 3 sticks. Must already have obtained sticks.");
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);
if (UIWidgets::PaddedEnhancementCheckbox("Hyper Bosses", "gHyperBosses", true, false)) {
UpdateHyperBossesState();
}
UIWidgets::Tooltip("All major bosses move and act twice as fast.");
UIWidgets::PaddedEnhancementCheckbox("Hyper Enemies", "gHyperEnemies", true, false);
if (UIWidgets::PaddedEnhancementCheckbox("Hyper Enemies", "gHyperEnemies", true, false)) {
UpdateHyperEnemiesState();
}
UIWidgets::Tooltip("All regular enemies and mini-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");