From f1dc43258942400fa72942eca0de17fb1b8148c4 Mon Sep 17 00:00:00 2001 From: lilDavid <1337lilDavid@gmail.com> Date: Fri, 22 Nov 2024 21:03:37 -0600 Subject: [PATCH] Holiday Modding Event - Bomb Arrows (#4573) * Bomb Arrows: Turn arrows into bomb arrows * Bomb Arrows: Drain bombs when fired * Bomb Arrows: Equip Over Bow * Bomb Arrows: Item icons and ammo counts * Bomb Arrows: Save files * Bomb Arrows: Fix equip and ammo display bugs * Bomb Arrows: Interactions with multi-arrows * Bomb Arrows: Fix fuse graphics --- soh/soh/Enhancements/Holiday/lilDavid.cpp | 136 ++++++++++++++++++ .../GameInteractor_HookTable.h | 1 + .../game-interactor/GameInteractor_Hooks.cpp | 4 + .../game-interactor/GameInteractor_Hooks.h | 1 + soh/soh/SaveManager.cpp | 1 + soh/src/code/z_parameter.c | 31 ++++ soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c | 7 +- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 15 ++ 8 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 soh/soh/Enhancements/Holiday/lilDavid.cpp diff --git a/soh/soh/Enhancements/Holiday/lilDavid.cpp b/soh/soh/Enhancements/Holiday/lilDavid.cpp new file mode 100644 index 000000000..1ed871920 --- /dev/null +++ b/soh/soh/Enhancements/Holiday/lilDavid.cpp @@ -0,0 +1,136 @@ +#include "Holiday.hpp" + +#include "utils/StringHelper.h" + +extern "C" { +#include "macros.h" +#include "functions.h" +#include "variables.h" +extern PlayState* gPlayState; +} + +#include "src/overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "src/overlays/actors/ovl_En_Bom/z_en_bom.h" + +extern "C" { + void func_809B45E0(EnArrow*, PlayState*); + void func_809B4640(EnArrow*, PlayState*); +} + +#define AUTHOR "lilDavid" +#define CVAR(v) "gHoliday." AUTHOR "." v + +static void OnConfigurationChanged() { + if (!CVarGetInteger(CVAR("BombArrows.Enabled"), 0)) + CVarSetInteger(CVAR("BombArrows.Active"), 0); + + COND_HOOK(OnSaveFile, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](int32_t file) { + std::string cvar = StringHelper::Sprintf("%s%d", CVAR("BombArrows.Save"), file); + CVarSetInteger(cvar.c_str(), CVarGetInteger(CVAR("BombArrows.Active"), 0)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + }); + + COND_HOOK(OnLoadFile, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](int32_t file) { + std::string cvar = StringHelper::Sprintf("%s%d", CVAR("BombArrows.Save"), file); + CVarSetInteger(CVAR("BombArrows.Active"), CVarGetInteger(cvar.c_str(), 0)); + }); + + COND_HOOK(OnCopyFile, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](int32_t from, int32_t to) { + std::string cvarFrom = StringHelper::Sprintf("%s%d", CVAR("BombArrows.Save"), from); + std::string cvarTo = StringHelper::Sprintf("%s%d", CVAR("BombArrows.Save"), to); + CVarSetInteger(cvarTo.c_str(), CVarGetInteger(cvarFrom.c_str(), 0)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + }); + + COND_HOOK(OnDeleteFile, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](int32_t file) { + std::string cvar = StringHelper::Sprintf("%s%d", CVAR("BombArrows.Save"), file); + CVarSetInteger(cvar.c_str(), 0); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + }); + + COND_ID_HOOK(OnActorInit, ACTOR_EN_ARROW, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](void* actorRef) { + EnArrow* arrow = (EnArrow*) actorRef; + if (!CVarGetInteger(CVAR("BombArrows.Active"), 0) || + arrow->actor.params != ARROW_NORMAL || AMMO(ITEM_BOMB) == 0 || + gSaveContext.minigameState == 1 || gPlayState->shootingGalleryStatus > 1) + return; + + EnBom* bomb = (EnBom*) Actor_SpawnAsChild(&gPlayState->actorCtx, &arrow->actor, gPlayState, ACTOR_EN_BOM, + arrow->actor.world.pos.x, arrow->actor.world.pos.y, arrow->actor.world.pos.z, + 0, 0, 0, BOMB_BODY); + if (bomb == nullptr) + return; + + Actor_SetScale(&bomb->actor, 0.003f); + bomb->timer = 65; + }); + + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_ARROW, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](void* actorRef) { + EnArrow* arrow = (EnArrow*) actorRef; + if (!arrow->actor.child || arrow->actor.child->id != ACTOR_EN_BOM) + return; + + EnBom* bomb = (EnBom*) arrow->actor.child; + bomb->actor.world.pos = arrow->actor.world.pos; + f32 r = 8.0f; + f32 xrot = arrow->actor.world.rot.x; + f32 yrot = arrow->actor.world.rot.y; + bomb->actor.world.pos.x += r * Math_CosS(xrot) * Math_SinS(yrot); + bomb->actor.world.pos.y -= r * Math_SinS(xrot) + 2.0f; + bomb->actor.world.pos.z += r * Math_CosS(xrot) * Math_CosS(yrot); + + if (arrow->actor.parent == nullptr) { + if (bomb->timer > 60) { + Inventory_ChangeAmmo(ITEM_BOMB, -1); + } + bomb->timer = 52; + } else { + bomb->timer = 62; + } + + if (arrow->actionFunc == func_809B45E0 || + arrow->actionFunc == func_809B4640 || + arrow->actor.params == ARROW_NORMAL_LIT) + { + arrow->actor.child = nullptr; + bomb->actor.parent = nullptr; + bomb->timer = 2; + Actor_Kill(&arrow->actor); + } + }); + + COND_ID_HOOK(OnActorKill, ACTOR_EN_ARROW, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](void* actorRef) { + EnArrow* arrow = (EnArrow*) actorRef; + if (!arrow->actor.child || arrow->actor.child->id != ACTOR_EN_BOM) + return; + Actor_Kill(arrow->actor.child); + }); + + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_BOM, CVarGetInteger(CVAR("BombArrows.Enabled"), 0), [](void* actorRef) { + EnBom* bomb = (EnBom*) actorRef; + if (!bomb->actor.parent || bomb->actor.parent->id != ACTOR_EN_ARROW) + return; + + if (bomb->timer > 55 && bomb->timer < 60) + bomb->timer += 4; + if (bomb->timer > 45 && bomb->timer < 50) + bomb->timer += 4; + }); +} + +static void DrawMenu() { + ImGui::SeparatorText(AUTHOR); + if (UIWidgets::EnhancementCheckbox("Bomb Arrows", CVAR("BombArrows.Enabled"))) { + OnConfigurationChanged(); + } +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + OnConfigurationChanged(); + // #endregion + + CVarSetInteger(CVAR("BombArrows.Active"), 0); +} + +static Holiday holiday(DrawMenu, RegisterMod); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 989a56c75..e60acf496 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -37,6 +37,7 @@ DEFINE_HOOK(OnOpenText, (u16 * textId, bool* loadFromMessageTable)); DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs)); DEFINE_HOOK(OnSaveFile, (int32_t fileNum)); DEFINE_HOOK(OnLoadFile, (int32_t fileNum)); +DEFINE_HOOK(OnCopyFile, (int32_t sourceFileNum, uint32_t destFileNum)); DEFINE_HOOK(OnDeleteFile, (int32_t fileNum)); DEFINE_HOOK(OnDialogMessage, ()); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index f0de65364..8f56f6fd4 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -184,6 +184,10 @@ void GameInteractor_ExecuteOnLoadFile(int32_t fileNum) { GameInteractor::Instance->ExecuteHooks(fileNum); } +void GameInteractor_ExecuteOnCopyFile(int32_t sourceFileNum, int32_t destFileNum) { + GameInteractor::Instance->ExecuteHooks(sourceFileNum, destFileNum); +} + void GameInteractor_ExecuteOnDeleteFile(int32_t fileNum) { GameInteractor::Instance->ExecuteHooks(fileNum); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 24fe78dc1..99f1451c0 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -40,6 +40,7 @@ bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...); // MARK: - Save Files void GameInteractor_ExecuteOnSaveFile(int32_t fileNum); void GameInteractor_ExecuteOnLoadFile(int32_t fileNum); +void GameInteractor_ExecuteOnCopyFile(int32_t sourceFileNum, int32_t destFileNum); void GameInteractor_ExecuteOnDeleteFile(int32_t fileNum); // MARK: - Dialog diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 407372575..1e92cb3bf 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -2458,6 +2458,7 @@ void SaveManager::CopyZeldaFile(int from, int to) { fileMetaInfo[to].buildVersionPatch = fileMetaInfo[from].buildVersionPatch; SohUtils::CopyStringToCharArray(fileMetaInfo[to].buildVersion, fileMetaInfo[from].buildVersion, ARRAY_COUNT(fileMetaInfo[to].buildVersion)); + GameInteractor::Instance->ExecuteHooks(from, to); } void SaveManager::DeleteZeldaFile(int fileNum) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index a9fa846d2..aeeaed138 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -4722,6 +4722,11 @@ void Interface_DrawAmmoCount(PlayState* play, s16 button, s16 alpha) { } ammo = AMMO(i); + if (CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0) && + gSaveContext.equips.buttonItems[button] == ITEM_BOW && + AMMO(ITEM_BOMB) != 0 && AMMO(ITEM_BOMB) < AMMO(ITEM_BOW)) { + ammo = AMMO(ITEM_BOMB); + } gDPPipeSync(OVERLAY_DISP++); @@ -4734,6 +4739,11 @@ void Interface_DrawAmmoCount(PlayState* play, s16 button, s16 alpha) { if (ammo < 0) { ammo = 0; } + } else if (gSaveContext.equips.buttonItems[button] == ITEM_BOW && + CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + if (AMMO(ITEM_BOMB) != 0 && ammo == MIN(CUR_CAPACITY(UPG_QUIVER), CUR_CAPACITY(UPG_BOMB_BAG))) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, alpha); + } } else if (((i == ITEM_BOW) && (AMMO(i) == CUR_CAPACITY(UPG_QUIVER))) || ((i == ITEM_BOMB) && (AMMO(i) == CUR_CAPACITY(UPG_BOMB_BAG))) || ((i == ITEM_SLINGSHOT) && (AMMO(i) == CUR_CAPACITY(UPG_BULLET_BAG))) || @@ -5311,6 +5321,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[1] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[1] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 1); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[1]], 1); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, @@ -5324,6 +5337,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[2] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cDownAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[2] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 2); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[2]], 2); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, @@ -5337,6 +5353,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[3] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cRightAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[3] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 3); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[3]], 3); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, @@ -5396,6 +5415,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[4] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->dpadUpAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[4] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 4); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[4]], 4); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, @@ -5407,6 +5429,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[5] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->dpadDownAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[5] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 5); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[5]], 5); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, @@ -5418,6 +5443,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[6] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->dpadLeftAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[6] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 6); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[6]], 6); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, @@ -5429,6 +5457,9 @@ void Interface_Draw(PlayState* play) { if (gSaveContext.equips.buttonItems[7] < 0xF0) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->dpadRightAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + if (gSaveContext.equips.buttonItems[7] == ITEM_BOW && CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0)) { + Interface_DrawItemIconTexture(play, gItemIcons[ITEM_BOMB], 7); + } Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[7]], 7); gDPPipeSync(OVERLAY_DISP++); gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, diff --git a/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c index e379f0095..829c75570 100644 --- a/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c +++ b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c @@ -287,7 +287,12 @@ void EnBom_Update(Actor* thisx, PlayState* play2) { // spawn spark effect on even frames effPos = thisx->world.pos; - effPos.y += 17.0f; + if (CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0) && + thisx->parent && thisx->parent->id == ACTOR_EN_ARROW) { + effPos.y += 5.0f; + } else { + effPos.y += 17.0f; + } if ((play->gameplayFrames % 2) == 0) { EffectSsGSpk_SpawnFuse(play, thisx, &effPos, &effVelocity, &effAccel); } diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index 105b59e03..d4a586b2b 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -1136,6 +1136,21 @@ void KaleidoScope_UpdateItemEquip(PlayState* play) { } } + if (CVarGetInteger("gHoliday.lilDavid.BombArrows.Enabled", 0)) { + if (pauseCtx->equipTargetSlot == SLOT_BOW) { + CVarSetInteger("gHoliday.lilDavid.BombArrows.Active", 0); + } + u8 equipped_slot = gSaveContext.equips.cButtonSlots[pauseCtx->equipTargetCBtn]; + if (!CVarGetInteger("gHoliday.lilDavid.BombArrows.Active", 0) && + pauseCtx->equipTargetItem == ITEM_BOMB && equipped_slot == SLOT_BOW) + { + CVarSetInteger("gHoliday.lilDavid.BombArrows.Active", 1); + pauseCtx->equipTargetItem = ITEM_BOW; + pauseCtx->equipTargetSlot = SLOT_BOW; + Audio_PlaySoundGeneral(NA_SE_SY_SET_FIRE_ARROW, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + } + // If the item is on another button already, swap the two uint16_t targetButtonIndex = pauseCtx->equipTargetCBtn + 1; for (uint16_t otherSlotIndex = 0; otherSlotIndex < ARRAY_COUNT(gSaveContext.equips.cButtonSlots);