Use variadic arguments with GameInteractor_Should (#766)

* PoC of using variadic arguments with GameInteractor_Should

* va_list boilerplate in SHOULD macro

* drop opt arg in favor of variadic arg changes

* account for default type promotion

* remove demo code
This commit is contained in:
Archez 2024-09-26 12:39:05 -04:00 committed by GitHub
parent 1475e6af7b
commit b1eea9ca76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 68 additions and 47 deletions

View File

@ -232,7 +232,7 @@ void RegisterDebugCam() {
if (CVarGetInteger("gEnhancements.Camera.DebugCam.Enable", 0)) {
freeCamVBHookId = REGISTER_VB_SHOULD(GI_VB_USE_CUSTOM_CAMERA, {
Camera* camera = static_cast<Camera*>(opt);
Camera* camera = va_arg(args, Camera*);
Camera_DebugCam(camera);
*should = false;
});

View File

@ -173,7 +173,7 @@ void RegisterCameraFreeLook() {
if (CVarGetInteger("gEnhancements.Camera.FreeLook.Enable", 0)) {
freeLookCameraVBHookId = REGISTER_VB_SHOULD(GI_VB_USE_CUSTOM_CAMERA, {
Camera* camera = static_cast<Camera*>(opt);
Camera* camera = va_arg(args, Camera*);
switch (sCameraSettings[camera->setting].cameraModes[camera->mode].funcId) {
case CAM_FUNC_NORMAL0:
case CAM_FUNC_NORMAL1:

View File

@ -26,7 +26,7 @@ void RegisterSkipTatlInterrupts() {
// General interupt
REGISTER_VB_SHOULD(GI_VB_TATL_INTERUPT_MSG3, {
if (CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0) && *should) {
Actor* actor = static_cast<Actor*>(opt);
Actor* actor = va_arg(args, Actor*);
*should = false;
if (ELFMSG3_GET_SWITCH_FLAG(actor) != 0x7F) {
Flags_SetSwitch(gPlayState, ELFMSG3_GET_SWITCH_FLAG(actor));
@ -38,7 +38,7 @@ void RegisterSkipTatlInterrupts() {
// General interupt 2 (the flags were directly copied from the original code)
REGISTER_VB_SHOULD(GI_VB_TATL_INTERUPT_MSG6, {
if (CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0) && *should) {
Actor* actor = static_cast<Actor*>(opt);
Actor* actor = va_arg(args, Actor*);
*should = false;
switch (actor->textId) {
case 0x224:

View File

@ -8,7 +8,7 @@ extern "C" {
void RegisterFierceDeityZTargetMovement() {
REGISTER_VB_SHOULD(GI_VB_ZTARGET_SPEED_CHECK, {
Player* player = GET_PLAYER(gPlayState);
float* speedArg = (float*)opt;
float* speedArg = va_arg(args, float*);
// If the player is Fierce Deity and targeting,
if (player->lockOnActor != NULL && player->transformation == PLAYER_FORM_FIERCE_DEITY &&

View File

@ -230,15 +230,25 @@ void GameInteractor_ExecuteOnItemGive(u8 item) {
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnItemGive>(item);
}
bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* opt) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::ShouldVanillaBehavior>(flag, &result, opt);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::ShouldVanillaBehavior>(flag, flag, &result, opt);
if (opt != nullptr) {
GameInteractor::Instance->ExecuteHooksForPtr<GameInteractor::ShouldVanillaBehavior>((uintptr_t)opt, flag,
&result, opt);
}
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::ShouldVanillaBehavior>(flag, &result, opt);
return result;
bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...) {
// Only the external function can use the Variadic Function syntax
// To pass the va args to the next caller must be done using va_list and reading the args into it
// Because there can be N subscribers registered to each template call, the subscribers will be responsible for
// creating a copy of this va_list to avoid incrementing the original pointer between calls
va_list args;
va_start(args, result);
// Because of default argument promotion, even though our incoming "result" is just a bool, it needs to be typed as
// an int to be permitted to be used in `va_start`, otherwise it is undefined behavior.
// Here we downcast back to a bool for our actual hook handlers
bool boolResult = static_cast<bool>(result);
GameInteractor::Instance->ExecuteHooks<GameInteractor::ShouldVanillaBehavior>(flag, &boolResult, args);
GameInteractor::Instance->ExecuteHooksForID<GameInteractor::ShouldVanillaBehavior>(flag, flag, &boolResult, args);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::ShouldVanillaBehavior>(flag, &boolResult, args);
va_end(args);
return boolResult;
}
// Returns 1 or -1 based on a number of factors like CVars or other game states.

View File

@ -1,6 +1,8 @@
#ifndef GAME_INTERACTOR_H
#define GAME_INTERACTOR_H
#include <stdarg.h>
#ifdef __cplusplus
#include <string>
extern "C" {
@ -303,7 +305,7 @@ class GameInteractor {
DEFINE_HOOK(ShouldItemGive, (u8 item, bool* should));
DEFINE_HOOK(OnItemGive, (u8 item));
DEFINE_HOOK(ShouldVanillaBehavior, (GIVanillaBehavior flag, bool* should, void* optionalArg));
DEFINE_HOOK(ShouldVanillaBehavior, (GIVanillaBehavior flag, bool* should, va_list originalArgs));
};
extern "C" {
@ -353,10 +355,15 @@ void GameInteractor_ExecuteOnOpenText(u16 textId);
bool GameInteractor_ShouldItemGive(u8 item);
void GameInteractor_ExecuteOnItemGive(u8 item);
bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* optionalArg);
bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...);
#define REGISTER_VB_SHOULD(flag, body) \
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::ShouldVanillaBehavior>( \
flag, [](GIVanillaBehavior _, bool* should, void* opt) body)
flag, [](GIVanillaBehavior _, bool* should, va_list originalArgs) { \
va_list args; \
va_copy(args, originalArgs); \
body; \
va_end(args); \
})
int GameInteractor_InvertControl(GIInvertType type);
uint32_t GameInteractor_Dpad(GIDpadType type, uint32_t buttonCombo);

View File

@ -114,9 +114,9 @@ void EnItem00_3DItemsDraw(Actor* actor, PlayState* play) {
}
}
void DrawSlime3DItem(GIVanillaBehavior _, bool* should, void* opt) {
void DrawSlime3DItem(Actor* actor, bool* should) {
*should = false;
EnSlime* slime = static_cast<EnSlime*>(opt);
EnSlime* slime = (EnSlime*)actor;
// Rotate 3D item with chu body
Matrix_RotateYS(slime->actor.shape.rot.y, MTXMODE_APPLY);
@ -180,6 +180,8 @@ void Register3DItemDrops() {
actor->shape.rot.y += 0x3C0;
}
});
slimeVBHookID = GameInteractor::Instance->RegisterGameHookForID<GameInteractor::ShouldVanillaBehavior>(
GI_VB_DRAW_SLIME_BODY_ITEM, DrawSlime3DItem);
slimeVBHookID = REGISTER_VB_SHOULD(GI_VB_DRAW_SLIME_BODY_ITEM, {
Actor* actor = va_arg(args, Actor*);
DrawSlime3DItem(actor, should);
});
}

View File

@ -145,15 +145,16 @@ void RegisterPersistentMasks() {
// Overrides allowing them to equip a mask while transformed
REGISTER_VB_SHOULD(GI_VB_USE_ITEM_CONSIDER_LINK_HUMAN, {
PlayerItemAction* itemAction = va_arg(args, PlayerItemAction*);
if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) &&
*(PlayerItemAction*)opt == PLAYER_IA_MASK_BUNNY) {
*itemAction == PLAYER_IA_MASK_BUNNY) {
*should = true;
}
});
// When they do equip the mask, prevent it and instead set our state
REGISTER_VB_SHOULD(GI_VB_USE_ITEM_EQUIP_MASK, {
PlayerMask* maskId = (PlayerMask*)opt;
PlayerMask* maskId = va_arg(args, PlayerMask*);
Player* player = GET_PLAYER(gPlayState);
if (*maskId == PLAYER_MASK_BUNNY && CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0)) {
@ -172,21 +173,23 @@ void RegisterPersistentMasks() {
// Prevent the "equipped" white border from being drawn so ours shows instead (ours was drawn before it, so it's
// underneath)
REGISTER_VB_SHOULD(GI_VB_DRAW_ITEM_EQUIPPED_OUTLINE, {
if (*(ItemId*)opt == ITEM_MASK_BUNNY && CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.State", 0)) {
ItemId* itemId = va_arg(args, ItemId*);
if (*itemId == ITEM_MASK_BUNNY && CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.State", 0)) {
*should = false;
}
});
// Typically when you are in a transformation all masks are dimmed on the C-Buttons
REGISTER_VB_SHOULD(GI_VB_ITEM_BE_RESTRICTED, {
if (*(ItemId*)opt == ITEM_MASK_BUNNY && CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0)) {
ItemId* itemId = va_arg(args, ItemId*);
if (*itemId == ITEM_MASK_BUNNY && CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0)) {
*should = false;
}
});
// Override "A" press behavior on kaleido scope to toggle the mask state
REGISTER_VB_SHOULD(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, {
ItemId* itemId = (ItemId*)opt;
ItemId* itemId = va_arg(args, ItemId*);
if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) && *itemId == ITEM_MASK_BUNNY) {
*should = false;
CVarSetInteger("gEnhancements.Masks.PersistentBunnyHood.State",

View File

@ -4,7 +4,7 @@
void RegisterClimbSpeed() {
REGISTER_VB_SHOULD(GI_VB_SET_CLIMB_SPEED, {
if (CVarGetInteger("gEnhancements.Player.ClimbSpeed", 1) > 1) {
f32* speed = static_cast<f32*>(opt);
f32* speed = va_arg(args, f32*);
*speed *= CVarGetInteger("gEnhancements.Player.ClimbSpeed", 1);
}
});

View File

@ -7,7 +7,7 @@ extern "C" {
void RegisterEnableSunsSong() {
REGISTER_VB_SHOULD(GI_VB_SONG_AVAILABLE_TO_PLAY, {
uint8_t* songIndex = static_cast<uint8_t*>(opt);
uint8_t* songIndex = va_arg(args, uint8_t*);
// If the enhancement is on, and the currently played song is Sun's Song, set it to be available to be played.
if (CVarGetInteger("gEnhancements.Songs.EnableSunsSong", 0) && *songIndex == OCARINA_SONG_SUNS) {
*should = true;

View File

@ -1545,7 +1545,7 @@ void Cutscene_HandleEntranceTriggers(PlayState* play) {
} else if (!CHECK_CS_SPAWN_FLAG_WEEKEVENTREG(play->csCtx.scriptList[scriptIndex].spawnFlags)) {
// Entrance cutscenes that only run once
SET_CS_SPAWN_FLAG_WEEKEVENTREG(play->csCtx.scriptList[scriptIndex].spawnFlags);
if (GameInteractor_Should(GI_VB_PLAY_ENTRANCE_CS, true, NULL)) {
if (GameInteractor_Should(GI_VB_PLAY_ENTRANCE_CS, true)) {
CutsceneManager_Start(csId, NULL);
}
// The title card will be used by the cs misc command if necessary.
@ -1561,8 +1561,7 @@ void Cutscene_HandleEntranceTriggers(PlayState* play) {
if ((gSaveContext.respawnFlag == 0) || (gSaveContext.respawnFlag == -2)) {
scene = play->loadedScene;
if ((scene->titleTextId != 0) &&
GameInteractor_Should(GI_VB_SHOW_TITLE_CARD, gSaveContext.showTitleCard, NULL)) {
if ((scene->titleTextId != 0) && GameInteractor_Should(GI_VB_SHOW_TITLE_CARD, gSaveContext.showTitleCard)) {
if ((Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) +
((void)0, gSaveContext.sceneLayer)) &
0x4000) != 0) {
@ -1581,7 +1580,7 @@ void func_800EDDB0(PlayState* play) {
LUSLOG_INFO("Cutscene_HandleConditionalTriggers: entrance: %d, cutsceneIndex: 0x%X", gSaveContext.save.entrance,
gSaveContext.save.cutsceneIndex);
if (!GameInteractor_Should(GI_VB_PLAY_TRANSITION_CS, true, NULL)) {
if (!GameInteractor_Should(GI_VB_PLAY_TRANSITION_CS, true)) {
return;
}
}

View File

@ -2823,7 +2823,7 @@ void Interface_UpdateButtonsPart2(PlayState* play) {
(play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) &&
(play->sceneId != SCENE_SEA_BS) && (play->sceneId != SCENE_INISIE_BS) &&
(play->sceneId != SCENE_LAST_BS);
if (GameInteractor_Should(GI_VB_DISABLE_FD_MASK, vanillaSceneConditionResult, NULL)) {
if (GameInteractor_Should(GI_VB_DISABLE_FD_MASK, vanillaSceneConditionResult)) {
if (gSaveContext.buttonStatus[i] != BTN_DISABLED) {
gSaveContext.buttonStatus[i] = BTN_DISABLED;
restoreHudVisibility = true;
@ -2970,7 +2970,7 @@ void Interface_UpdateButtonsPart2(PlayState* play) {
(play->sceneId != SCENE_MITURIN_BS) && (play->sceneId != SCENE_HAKUGIN_BS) &&
(play->sceneId != SCENE_SEA_BS) && (play->sceneId != SCENE_INISIE_BS) &&
(play->sceneId != SCENE_LAST_BS);
if (GameInteractor_Should(GI_VB_DISABLE_FD_MASK, vanillaSceneConditionResult, NULL)) {
if (GameInteractor_Should(GI_VB_DISABLE_FD_MASK, vanillaSceneConditionResult)) {
if (gSaveContext.shipSaveContext.dpad.status[j] != BTN_DISABLED) {
gSaveContext.shipSaveContext.dpad.status[j] = BTN_DISABLED;
restoreHudVisibility = true;
@ -6151,7 +6151,7 @@ void Interface_DrawClock(PlayState* play) {
s16 finalHoursClockSlots[8];
s16 index;
if (GameInteractor_Should(GI_VB_PREVENT_CLOCK_DISPLAY, false, NULL)) {
if (GameInteractor_Should(GI_VB_PREVENT_CLOCK_DISPLAY, false)) {
return;
}

View File

@ -80,7 +80,7 @@ void ShrinkWindow_Draw(GraphicsContext* gfxCtx) {
s8 letterboxSize = sShrinkWindowPtr->letterboxSize;
s8 pillarboxSize = sShrinkWindowPtr->pillarboxSize;
if (GameInteractor_Should(GI_VB_DISABLE_LETTERBOX, false, NULL)) {
if (GameInteractor_Should(GI_VB_DISABLE_LETTERBOX, false)) {
return;
}

View File

@ -1373,7 +1373,7 @@ void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCtx) {
fileNum = gSaveContext.fileNum;
// Remove Owl saves on save continue
if (GameInteractor_Should(GI_VB_DELETE_OWL_SAVE, true, 0)) {
if (GameInteractor_Should(GI_VB_DELETE_OWL_SAVE, true)) {
func_80147314(sramCtx, fileNum);
}
}

View File

@ -160,7 +160,7 @@ void View_ApplyLetterbox(View* view) {
s32 lrx;
s32 lry;
if (GameInteractor_Should(GI_VB_DISABLE_LETTERBOX, false, NULL)) {
if (GameInteractor_Should(GI_VB_DISABLE_LETTERBOX, false)) {
return;
}

View File

@ -519,7 +519,7 @@ void EnRacedog_UpdateSpeed(EnRacedog* this) {
// Cap the speed of the dog, or always max it out with the 'Always Win Doggy Race' enhancement.
bool vanillaCondition = this->actor.speed > 7.5f;
if (GameInteractor_Should(GI_VB_DOGGY_RACE_SET_MAX_SPEED, vanillaCondition, NULL)) {
if (GameInteractor_Should(GI_VB_DOGGY_RACE_SET_MAX_SPEED, vanillaCondition)) {
this->actor.speed = 7.5f;
}
} else {

View File

@ -3814,7 +3814,7 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) {
if (bomb != NULL) {
bomb->timer = 0;
if (GameInteractor_Should(GI_VB_SET_BLAST_MASK_COOLDOWN_TIMER, true, NULL)) {
if (GameInteractor_Should(GI_VB_SET_BLAST_MASK_COOLDOWN_TIMER, true)) {
this->blastMaskTimer = 310;
}
}
@ -6720,7 +6720,7 @@ void func_80836AD8(PlayState* play, Player* this) {
}
void func_80836B3C(PlayState* play, Player* this, f32 arg2) {
if (GameInteractor_Should(GI_VB_PATCH_SIDEROLL, true, NULL)) {
if (GameInteractor_Should(GI_VB_PATCH_SIDEROLL, true)) {
this->currentYaw = this->actor.shape.rot.y;
this->actor.world.rot.y = this->actor.shape.rot.y;
}
@ -7814,7 +7814,7 @@ s32 Player_ActionChange_4(Player* this, PlayState* play) {
// !CutsceneManager_IsNext(CS_ID_GLOBAL_TALK), which is what prevented Tatl ISG from
// working
bool vanillaCondition = !CutsceneManager_IsNext(CS_ID_GLOBAL_TALK);
if (GameInteractor_Should(GI_VB_TATL_CONVERSATION_AVAILABLE, vanillaCondition, NULL) ||
if (GameInteractor_Should(GI_VB_TATL_CONVERSATION_AVAILABLE, vanillaCondition) ||
!CHECK_BTN_ALL(sPlayerControlInput->press.button, BTN_CUP)) {
return false;
}
@ -10098,7 +10098,7 @@ s32 func_8083FD80(Player* this, PlayState* play) {
if (!Player_IsGoronOrDeku(this) && (Player_GetMeleeWeaponHeld(this) != PLAYER_MELEEWEAPON_NONE) &&
(this->transformation != PLAYER_FORM_ZORA) && sPlayerUseHeldItem) {
//! Calling this function sets the meleeWeaponQuads' damage properties correctly, patching "Power Crouch Stab".
if (GameInteractor_Should(GI_VB_PATCH_POWER_CROUCH_STAB, true, NULL)) {
if (GameInteractor_Should(GI_VB_PATCH_POWER_CROUCH_STAB, true)) {
func_8083375C(this, PLAYER_MWA_STAB_1H);
}
Player_AnimationPlayOnce(play, this, &gPlayerAnim_link_normal_defense_kiru);
@ -10145,7 +10145,7 @@ s32 func_8083FF30(PlayState* play, Player* this) {
s32 func_8083FFEC(PlayState* play, Player* this) {
if (this->heldItemAction == PLAYER_IA_SWORD_RAZOR) {
if (gSaveContext.save.saveInfo.playerData.swordHealth > 0) {
if (GameInteractor_Should(GI_VB_LOWER_RAZOR_SWORD_DURABILITY, true, NULL)) {
if (GameInteractor_Should(GI_VB_LOWER_RAZOR_SWORD_DURABILITY, true)) {
gSaveContext.save.saveInfo.playerData.swordHealth--;
}
if (gSaveContext.save.saveInfo.playerData.swordHealth <= 0) {
@ -11255,7 +11255,7 @@ void Player_SetDoAction(PlayState* play, Player* this) {
}
if (doActionA != DO_ACTION_PUTAWAY) {
if (GameInteractor_Should(GI_VB_RESET_PUTAWAY_TIMER, true, NULL)) {
if (GameInteractor_Should(GI_VB_RESET_PUTAWAY_TIMER, true)) {
this->putAwayCountdown = 20;
}
} else if (this->putAwayCountdown != 0) {
@ -14894,7 +14894,7 @@ void Player_Action_25(Player* this, PlayState* play) {
Math_StepToF(&this->unk_B10[1], 0.0f, this->unk_B10[0]);
}
} else {
if (GameInteractor_Should(GI_VB_FLIP_HOP_VARIABLE, true, NULL)) {
if (GameInteractor_Should(GI_VB_FLIP_HOP_VARIABLE, true)) {
func_8083CBC4(this, speedTarget, yawTarget, 1.0f, 0.05f, 0.1f, 0xC8);
}
}
@ -18283,7 +18283,7 @@ void Player_Action_86(Player* this, PlayState* play) {
struct_8085D910* sp4C = D_8085D910;
s32 sp48 = false;
if (GameInteractor_Should(GI_VB_PREVENT_MASK_TRANSFORMATION_CS, false, NULL))
if (GameInteractor_Should(GI_VB_PREVENT_MASK_TRANSFORMATION_CS, false))
return;
func_808323C0(this, play->playerCsIds[PLAYER_CS_ID_MASK_TRANSFORMATION]);