mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-02-07 10:50:29 -05:00
Support hook unregistration (#3538)
This commit is contained in:
parent
bdfcf39e56
commit
a84227cbbb
@ -163,11 +163,30 @@ public:
|
|||||||
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
|
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
|
||||||
|
|
||||||
// Game Hooks
|
// Game Hooks
|
||||||
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; };
|
uint32_t nextHookId = 1;
|
||||||
template <typename H> void RegisterGameHook(typename H::fn h) { RegisteredGameHooks<H>::functions.push_back(h); }
|
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) {
|
template <typename H, typename... Args> void ExecuteHooks(Args&&... args) {
|
||||||
for (auto& fn : RegisteredGameHooks<H>::functions) {
|
for (auto& hookId : HooksToUnregister<H>::hooks) {
|
||||||
fn(std::forward<Args>(args)...);
|
RegisteredGameHooks<H>::functions.erase(hookId);
|
||||||
|
}
|
||||||
|
HooksToUnregister<H>::hooks.clear();
|
||||||
|
for (auto& hook : RegisteredGameHooks<H>::functions) {
|
||||||
|
hook.second(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,8 +519,20 @@ void RegisterDaytimeGoldSkultullas() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterHyperBosses() {
|
bool IsHyperBossesActive() {
|
||||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
|
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.
|
// Run the update function a second time to make bosses move and act twice as fast.
|
||||||
|
|
||||||
Player* player = GET_PLAYER(gPlayState);
|
Player* player = GET_PLAYER(gPlayState);
|
||||||
@ -542,13 +554,8 @@ void RegisterHyperBosses() {
|
|||||||
actor->id == ACTOR_BOSS_GANON || // Ganondorf
|
actor->id == ACTOR_BOSS_GANON || // Ganondorf
|
||||||
actor->id == ACTOR_BOSS_GANON2; // Ganon
|
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.
|
// 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.
|
// Barinade needs to be updated in sequence to avoid unintended behaviour.
|
||||||
if (actor->id == ACTOR_BOSS_VA) {
|
if (actor->id == ACTOR_BOSS_VA) {
|
||||||
// params -1 is BOSSVA_BODY
|
// params -1 is BOSSVA_BODY
|
||||||
@ -564,10 +571,25 @@ void RegisterHyperBosses() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterHyperEnemies() {
|
void RegisterHyperBosses() {
|
||||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
|
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.
|
// Run the update function a second time to make enemies and minibosses move and act twice as fast.
|
||||||
|
|
||||||
Player* player = GET_PLAYER(gPlayState);
|
Player* player = GET_PLAYER(gPlayState);
|
||||||
@ -583,6 +605,7 @@ void RegisterHyperEnemies() {
|
|||||||
GameInteractor::RawAction::UpdateActor(actor);
|
GameInteractor::RawAction::UpdateActor(actor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterBonkDamage() {
|
void RegisterBonkDamage() {
|
||||||
@ -1371,7 +1394,7 @@ void InitMods() {
|
|||||||
RegisterPermanentHeartLoss();
|
RegisterPermanentHeartLoss();
|
||||||
RegisterDeleteFileOnDeath();
|
RegisterDeleteFileOnDeath();
|
||||||
RegisterHyperBosses();
|
RegisterHyperBosses();
|
||||||
RegisterHyperEnemies();
|
UpdateHyperEnemiesState();
|
||||||
RegisterBonkDamage();
|
RegisterBonkDamage();
|
||||||
RegisterMenuPathFix();
|
RegisterMenuPathFix();
|
||||||
RegisterMirrorModeHandler();
|
RegisterMirrorModeHandler();
|
||||||
|
@ -12,6 +12,8 @@ void UpdateMirrorModeState(int32_t sceneNum);
|
|||||||
void UpdateHurtContainerModeState(bool newState);
|
void UpdateHurtContainerModeState(bool newState);
|
||||||
void PatchToTMedallions();
|
void PatchToTMedallions();
|
||||||
void UpdatePermanentHeartLossState();
|
void UpdatePermanentHeartLossState();
|
||||||
|
void UpdateHyperEnemiesState();
|
||||||
|
void UpdateHyperBossesState();
|
||||||
void InitMods();
|
void InitMods();
|
||||||
void UpdatePatchHand();
|
void UpdatePatchHand();
|
||||||
|
|
||||||
|
@ -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::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::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::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::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::Tooltip("All regular enemies and mini-bosses move and act twice as fast.");
|
||||||
UIWidgets::PaddedEnhancementCheckbox("Always Win Goron Pot", "gGoronPot", true, false);
|
UIWidgets::PaddedEnhancementCheckbox("Always Win Goron Pot", "gGoronPot", true, false);
|
||||||
UIWidgets::Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot");
|
UIWidgets::Tooltip("Always get the heart piece/purple rupee from the spinning Goron pot");
|
||||||
|
Loading…
Reference in New Issue
Block a user