From 9ac7e8d08944dedf268d045c195b1c52662d3d65 Mon Sep 17 00:00:00 2001 From: lil David <1337lilDavid@gmail.com> Date: Sun, 24 Nov 2024 20:04:33 -0600 Subject: [PATCH 1/5] Add mods menu based on let-it-snow --- soh/soh/Enhancements/Mods/Mods.hpp | 42 +++++++++++++++++++ .../game-interactor/GameInteractor.h | 28 +++++++++++++ .../GameInteractor_HookTable.h | 2 + .../game-interactor/GameInteractor_Hooks.cpp | 15 +++++++ .../game-interactor/GameInteractor_Hooks.h | 2 + .../GameInteractor_RawAction.cpp | 1 - .../game-interactor/GameInteractor_State.cpp | 4 ++ soh/soh/Enhancements/mods.cpp | 2 + soh/soh/SohMenuBar.cpp | 5 +++ 9 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 soh/soh/Enhancements/Mods/Mods.hpp diff --git a/soh/soh/Enhancements/Mods/Mods.hpp b/soh/soh/Enhancements/Mods/Mods.hpp new file mode 100644 index 00000000000..bdd3a90f44a --- /dev/null +++ b/soh/soh/Enhancements/Mods/Mods.hpp @@ -0,0 +1,42 @@ +#ifndef MODS_HPP +#define MODS_HPP + +#include +#include +#include +#include "soh/UIWidgets.hpp" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/cosmetics/CosmeticsEditor.h" + +inline std::vector> modDrawFuncs = {}; +inline std::vector> modRegisterFuncs = {}; + +inline void DrawModsMenu() { + if (modDrawFuncs.empty()) { + return; + } + + if (ImGui::BeginMenu("Mods")) { + for (auto& drawFunc : modDrawFuncs) { + ImGui::PushID(&drawFunc); + drawFunc(); + ImGui::PopID(); + } + ImGui::EndMenu(); + } +} + +inline void RegisterMods() { + for (auto& regFunc : modRegisterFuncs) { + regFunc(); + } +} + +struct Mod { + Mod(std::function drawFunc, std::function registerFunc) { + modDrawFuncs.push_back(drawFunc); + modRegisterFuncs.push_back(registerFunc); + } +}; + +#endif //MODS_HPP diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 61cfbe72ec5..ad2160a5721 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -488,6 +488,7 @@ typedef enum { extern "C" { #endif uint8_t GameInteractor_NoUIActive(); +void GameInteractor_SetNoUIActive(uint8_t state); GILinkSize GameInteractor_GetLinkSize(); void GameInteractor_SetLinkSize(GILinkSize size); uint8_t GameInteractor_InvisibleLinkActive(); @@ -572,6 +573,33 @@ struct HookInfo { body; \ va_end(args); \ }) +#define COND_HOOK(hookType, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ + GameInteractor::Instance->UnregisterGameHook(hookId); \ + hookId = 0; \ + if (condition) { \ + hookId = GameInteractor::Instance->RegisterGameHook(body); \ + } \ + } +#define COND_ID_HOOK(hookType, id, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ + GameInteractor::Instance->UnregisterGameHookForID(hookId); \ + hookId = 0; \ + if (condition) { \ + hookId = GameInteractor::Instance->RegisterGameHookForID(id, body); \ + } \ + } +#define COND_VB_SHOULD(id, condition, body) \ + { \ + static HOOK_ID hookId = 0; \ + GameInteractor::Instance->UnregisterGameHookForID(hookId); \ + hookId = 0; \ + if (condition) { \ + hookId = REGISTER_VB_SHOULD(id, body); \ + } \ + } class GameInteractor { public: diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index a2d5c56ec52..06ef7cc21be 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -20,6 +20,7 @@ DEFINE_HOOK(OnSceneSpawnActors, ()); DEFINE_HOOK(OnPlayerUpdate, ()); DEFINE_HOOK(OnOcarinaSongAction, ()); DEFINE_HOOK(OnShopSlotChange, (uint8_t cursorIndex, int16_t price)); +DEFINE_HOOK(ShouldActorInit, (void* actor, bool* result)); DEFINE_HOOK(OnActorInit, (void* actor)); DEFINE_HOOK(OnActorUpdate, (void* actor)); DEFINE_HOOK(OnActorKill, (void* actor)); @@ -31,6 +32,7 @@ DEFINE_HOOK(OnPlayerHealthChange, (int16_t amount)); DEFINE_HOOK(OnPlayerBottleUpdate, (int16_t contents)); DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); +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)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 39fc298a889..a2235f7f0e1 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -79,6 +79,15 @@ void GameInteractor_ExecuteOnActorInit(void* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +bool GameInteractor_ShouldActorInit(void* actor) { + bool result = true; + GameInteractor::Instance->ExecuteHooks(actor, &result); + GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor, &result); + GameInteractor::Instance->ExecuteHooksForPtr((uintptr_t)actor, actor, &result); + GameInteractor::Instance->ExecuteHooksForFilter(actor, &result); + return result; +} + void GameInteractor_ExecuteOnActorUpdate(void* actor) { GameInteractor::Instance->ExecuteHooks(actor); GameInteractor::Instance->ExecuteHooksForID(((Actor*)actor)->id, actor); @@ -131,6 +140,12 @@ void GameInteractor_ExecuteOnPlayDrawEnd() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnOpenText(u16* textId, bool* loadFromMessageTable) { + GameInteractor::Instance->ExecuteHooks(textId, loadFromMessageTable); + GameInteractor::Instance->ExecuteHooksForID(*textId, textId, loadFromMessageTable); + GameInteractor::Instance->ExecuteHooksForFilter(textId, loadFromMessageTable); +} + bool GameInteractor_Should(GIVanillaBehavior flag, u32 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 diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 3438d269d84..60326614dd9 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -19,6 +19,7 @@ void GameInteractor_ExecuteOnFlagUnset(int16_t flagType, int16_t flag); void GameInteractor_ExecuteOnSceneSpawnActors(); void GameInteractor_ExecuteOnPlayerUpdate(); void GameInteractor_ExecuteOnOcarinaSongAction(); +bool GameInteractor_ShouldActorInit(void* actor); void GameInteractor_ExecuteOnActorInit(void* actor); void GameInteractor_ExecuteOnActorUpdate(void* actor); void GameInteractor_ExecuteOnActorKill(void* actor); @@ -32,6 +33,7 @@ void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnPlayDestroy(); void GameInteractor_ExecuteOnPlayDrawEnd(); +void GameInteractor_ExecuteOnOpenText(u16* textId, bool* loadFromMessageTable); bool GameInteractor_Should(GIVanillaBehavior flag, uint32_t result, ...); // MARK: - Save Files diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 24e38d9b4ae..9f7a73ad3ed 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -8,7 +8,6 @@ extern "C" { #include "variables.h" #include "macros.h" -#include "soh/cvar_prefixes.h" #include "functions.h" extern PlayState* gPlayState; } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp index 21642ddedce..38d125786e6 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp @@ -36,6 +36,10 @@ uint8_t GameInteractor_NoUIActive() { return GameInteractor::State::NoUIActive; } +void GameInteractor_SetNoUIActive(uint8_t state) { + GameInteractor::State::NoUIActive = state; +} + // MARK: - GameInteractor::State::LinkSize GILinkSize GameInteractor_GetLinkSize() { return GameInteractor::State::LinkSize; diff --git a/soh/soh/Enhancements/mods.cpp b/soh/soh/Enhancements/mods.cpp index 8c18daa00f6..ecd8f0cdac4 100644 --- a/soh/soh/Enhancements/mods.cpp +++ b/soh/soh/Enhancements/mods.cpp @@ -16,6 +16,7 @@ #include "soh/Enhancements/TimeSavers/TimeSavers.h" #include "soh/Enhancements/cheat_hook_handlers.h" #include "soh/Enhancements/randomizer/hook_handlers.h" +#include "soh/Enhancements/Mods/Mods.hpp" #include "objects/object_gi_compass/object_gi_compass.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" @@ -1439,4 +1440,5 @@ void InitMods() { RegisterHurtContainerModeHandler(); RegisterPauseMenuHooks(); RandoKaleido_RegisterHooks(); + RegisterMods(); } diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 28a0c1c266e..144f5d63ed7 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -44,6 +44,7 @@ #include "Enhancements/enemyrandomizer.h" #include "Enhancements/timesplits/TimeSplits.h" #include "Enhancements/randomizer/Plandomizer.h" +#include "Enhancements/Mods/Mods.hpp" // FA icons are kind of wonky, if they worked how I expected them to the "+ 2.0f" wouldn't be needed, but // they don't work how I expect them to so I added that because it looked good when I eyeballed it @@ -2268,6 +2269,10 @@ void SohMenuBar::DrawElement() { DrawRandomizerMenu(); + ImGui::SetCursorPosY(0.0f); + + DrawModsMenu(); + ImGui::PopStyleVar(1); ImGui::EndMenuBar(); } From fab3019e7dda7ef1fdb2ae32fbf2b230df6412d1 Mon Sep 17 00:00:00 2001 From: lil David <1337lilDavid@gmail.com> Date: Thu, 21 Nov 2024 21:56:44 -0600 Subject: [PATCH 2/5] Bomb Arrows --- soh/soh/Enhancements/Mods/lilDavid.cpp | 138 ++++++++++++++++++ .../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, 197 insertions(+), 1 deletion(-) create mode 100644 soh/soh/Enhancements/Mods/lilDavid.cpp diff --git a/soh/soh/Enhancements/Mods/lilDavid.cpp b/soh/soh/Enhancements/Mods/lilDavid.cpp new file mode 100644 index 00000000000..74b11485886 --- /dev/null +++ b/soh/soh/Enhancements/Mods/lilDavid.cpp @@ -0,0 +1,138 @@ +#include "Mods.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) "gMods." 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(); + } + UIWidgets::Tooltip("Equip Bombs onto the same item as your bow to shoot Bomb Arrows that explode on contact."); +} + +static void RegisterMod() { + // #region Leave this alone unless you know what you are doing + OnConfigurationChanged(); + // #endregion + + CVarSetInteger(CVAR("BombArrows.Active"), 0); +} + +static Mod mod(DrawMenu, RegisterMod); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 06ef7cc21be..fe749383a75 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -36,6 +36,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 a2235f7f0e1..cafc0bd6699 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -177,6 +177,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 60326614dd9..7411758f56c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -39,6 +39,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 1d2ce5a6f3f..f2cc04fe925 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -2459,6 +2459,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 2c180c584a0..dec066f7c35 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -4725,6 +4725,11 @@ void Interface_DrawAmmoCount(PlayState* play, s16 button, s16 alpha) { } ammo = AMMO(i); + if (CVarGetInteger("gMods.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++); @@ -4737,6 +4742,11 @@ void Interface_DrawAmmoCount(PlayState* play, s16 button, s16 alpha) { if (ammo < 0) { ammo = 0; } + } else if (gSaveContext.equips.buttonItems[button] == ITEM_BOW && + CVarGetInteger("gMods.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))) || @@ -5314,6 +5324,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("gMods.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, @@ -5327,6 +5340,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("gMods.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, @@ -5340,6 +5356,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("gMods.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, @@ -5399,6 +5418,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("gMods.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, @@ -5410,6 +5432,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("gMods.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, @@ -5421,6 +5446,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("gMods.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, @@ -5432,6 +5460,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("gMods.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 e379f009551..a7c7aa33eec 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("gMods.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 91051f5f3ff..c55cecd35b6 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 @@ -1137,6 +1137,21 @@ void KaleidoScope_UpdateItemEquip(PlayState* play) { } } + if (CVarGetInteger("gMods.lilDavid.BombArrows.Enabled", 0)) { + if (pauseCtx->equipTargetSlot == SLOT_BOW) { + CVarSetInteger("gMods.lilDavid.BombArrows.Active", 0); + } + u8 equipped_slot = gSaveContext.equips.cButtonSlots[pauseCtx->equipTargetCBtn]; + if (!CVarGetInteger("gMods.lilDavid.BombArrows.Active", 0) && + pauseCtx->equipTargetItem == ITEM_BOMB && equipped_slot == SLOT_BOW) + { + CVarSetInteger("gMods.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); From 082b28800d1424fc0068b9b570b42714cdbf79ef Mon Sep 17 00:00:00 2001 From: lilDavid <1337lilDavid@gmail.com> Date: Wed, 21 Jun 2023 21:28:45 -0500 Subject: [PATCH 3/5] Visual Small Key display --- soh/soh/Enhancements/Mods/lilDavid.cpp | 7 +++++++ soh/src/code/z_parameter.c | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/soh/soh/Enhancements/Mods/lilDavid.cpp b/soh/soh/Enhancements/Mods/lilDavid.cpp index 74b11485886..a7e5099b3b6 100644 --- a/soh/soh/Enhancements/Mods/lilDavid.cpp +++ b/soh/soh/Enhancements/Mods/lilDavid.cpp @@ -125,6 +125,13 @@ static void DrawMenu() { OnConfigurationChanged(); } UIWidgets::Tooltip("Equip Bombs onto the same item as your bow to shoot Bomb Arrows that explode on contact."); + + UIWidgets::EnhancementCheckbox("Visual Small Key Display", CVAR("VisualSmallKeys.Enabled")); + UIWidgets::Tooltip("Displays Small Key count using multiple icons rather than a numeric counter"); + const bool disableKeySpacing = !CVarGetInteger(CVAR("VisualSmallKeys.Enabled"), 0); + static const char* disableKeySpacingTooltip = "This option is disabled because \"Visual Small Key Display\" is turned off"; + UIWidgets::EnhancementSliderInt("Small Key Icon Spacing: %d", "##SmallKeySpacing", CVAR("VisualSmallKeys.Spacing"), 1, 16, "", 8, true, disableKeySpacing, disableKeySpacingTooltip); + UIWidgets::EnhancementCheckbox("Right Align Key Icons", CVAR("VisualSmallKeys.RightAlign"), disableKeySpacing, disableKeySpacingTooltip); } static void RegisterMod() { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index dec066f7c35..777ec1e8085 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -5128,6 +5128,21 @@ void Interface_Draw(PlayState* play) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, keyCountColor.r,keyCountColor.g,keyCountColor.b, interfaceCtx->magicAlpha); gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 20, 255); //We reset this here so it match user color :) + + if (CVarGetInteger("gMods.lilDavid.VisualSmallKeys.Enabled", 0)) { + s8 keyCount = gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]; + s16 rectLeft = PosX_SKC; + s16 keyOffset = CVarGetInteger("gMods.lilDavid.VisualSmallKeys.Spacing", 8); + if (CVarGetInteger("gMods.lilDavid.VisualSmallKeys.RightAlign", 0)) { + keyOffset = -keyOffset; + } + for (int i = 0; i < keyCount; i++, rectLeft += keyOffset) { + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gSmallKeyCounterIconTex, 16, 16, rectLeft, PosY_SKC, 16, 16, + 1 << 10, 1 << 10); + } + break; + } + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gSmallKeyCounterIconTex, 16, 16, PosX_SKC, PosY_SKC, 16, 16, 1 << 10, 1 << 10); From 3d9ff44f4e1b4d55528f4607210a85ad42e6e867 Mon Sep 17 00:00:00 2001 From: lil David <1337lilDavid@gmail.com> Date: Mon, 25 Nov 2024 08:12:33 -0600 Subject: [PATCH 4/5] Catch Poes with bottles --- soh/soh/Enhancements/Mods/lilDavid.cpp | 63 +++++++++++++++++++ .../game-interactor/GameInteractor.h | 8 +++ .../actors/ovl_En_Po_Field/z_en_po_field.c | 14 +++-- soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c | 14 +++-- .../actors/ovl_player_actor/z_player.c | 10 ++- 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/soh/soh/Enhancements/Mods/lilDavid.cpp b/soh/soh/Enhancements/Mods/lilDavid.cpp index a7e5099b3b6..b8869cf4e17 100644 --- a/soh/soh/Enhancements/Mods/lilDavid.cpp +++ b/soh/soh/Enhancements/Mods/lilDavid.cpp @@ -11,16 +11,21 @@ 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" +#include "src/overlays/actors/ovl_En_Poh/z_en_poh.h" +#include "src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h" extern "C" { void func_809B45E0(EnArrow*, PlayState*); void func_809B4640(EnArrow*, PlayState*); + void func_80ADFE80(EnPoh*, PlayState*); } #define AUTHOR "lilDavid" #define CVAR(v) "gMods." AUTHOR "." v static void OnConfigurationChanged() { + // Bomb Arrows + if (!CVarGetInteger(CVAR("BombArrows.Enabled"), 0)) CVarSetInteger(CVAR("BombArrows.Active"), 0); @@ -116,6 +121,59 @@ static void OnConfigurationChanged() { if (bomb->timer > 45 && bomb->timer < 50) bomb->timer += 4; }); + + + // Catch Poes with a Bottle + + COND_VB_SHOULD(VB_BOTTLE_ACTOR, CVarGetInteger(CVAR("MMPoeBottling"), 0), { + Actor* interactRangeActor = va_arg(args, Actor*); + + if (interactRangeActor->id == ACTOR_EN_PO_FIELD) { + *should = true; + return; + } + + if (interactRangeActor->id != ACTOR_EN_POH) + return; + + // Don't catch Sharp or Flat + // I think they talk to you before you can get in catching range, but might as well prevent it outright + if (interactRangeActor->params >= EN_POH_SHARP) { + *should = false; + return; + } + + *should = true; + }); + + COND_VB_SHOULD(VB_POE_SOUL_TALK_TO_PLAYER, CVarGetInteger(CVAR("MMPoeBottling"), 0), { + EnPoh* poe = va_arg(args, EnPoh*); + + if (poe->actor.params >= EN_POH_SHARP) + return; + + *should = false; + + if (Actor_HasParent(&poe->actor, gPlayState)) { + Actor_Kill(&poe->actor); + } else { + // GI_MAX in this case allows the player to catch the actor in a bottle + Actor_OfferGetItem(&poe->actor, gPlayState, GI_MAX, 35.0f, 60.0f); + } + }); + + COND_VB_SHOULD(VB_FIELD_POE_SOUL_TALK_TO_PLAYER, CVarGetInteger(CVAR("MMPoeBottling"), 0), { + EnPoField* poe = va_arg(args, EnPoField*); + + *should = false; + + if (Actor_HasParent(&poe->actor, gPlayState)) { + Actor_Kill(&poe->actor); + } else { + // GI_MAX in this case allows the player to catch the actor in a bottle + Actor_OfferGetItem(&poe->actor, gPlayState, GI_MAX, 35.0f, 60.0f); + } + }); } static void DrawMenu() { @@ -126,6 +184,11 @@ static void DrawMenu() { } UIWidgets::Tooltip("Equip Bombs onto the same item as your bow to shoot Bomb Arrows that explode on contact."); + if (UIWidgets::PaddedEnhancementCheckbox("Catch Poes With a Bottle", CVAR("MMPoeBottling"))) { + OnConfigurationChanged(); + } + UIWidgets::Tooltip("Catch Poes by swinging an empty bottle at them instead of from a text box like you can in Majora's Mask."); + UIWidgets::EnhancementCheckbox("Visual Small Key Display", CVAR("VisualSmallKeys.Enabled")); UIWidgets::Tooltip("Displays Small Key count using multiple icons rather than a numeric counter"); const bool disableKeySpacing = !CVarGetInteger(CVAR("VisualSmallKeys.Enabled"), 0); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index ad2160a5721..d4cdda128be 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -482,6 +482,14 @@ typedef enum { // Vanilla condition: Actor is ACTOR_EN_ELF, ACTOR_EN_FISH, ACTOR_EN_ICE_HONO, or ACTOR_EN_INSECT // Opt: *Actor VB_BOTTLE_ACTOR, + + /*** Catch Poes With a Bottle ***/ + // Vanilla condition: true + // Opt: *EnPoh + VB_POE_SOUL_TALK_TO_PLAYER, + // Vanilla condition: true + // Opt: *EnPoField + VB_FIELD_POE_SOUL_TALK_TO_PLAYER, } GIVanillaBehavior; #ifdef __cplusplus diff --git a/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c index e1bb232f866..2702a32cdcc 100644 --- a/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c +++ b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c @@ -669,12 +669,14 @@ void func_80AD58D4(EnPoField* this, PlayState* play) { EnPoField_SetupSoulDisappear(this); return; } - if (this->collider.base.ocFlags1 & OC1_HIT) { - this->actor.flags |= ACTOR_FLAG_WILL_TALK; - func_8002F2F4(&this->actor, play); - } else { - this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; - CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + if (GameInteractor_Should(VB_FIELD_POE_SOUL_TALK_TO_PLAYER, true, this)) { + if (this->collider.base.ocFlags1 & OC1_HIT) { + this->actor.flags |= ACTOR_FLAG_WILL_TALK; + func_8002F2F4(&this->actor, play); + } else { + this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + } } this->actor.world.pos.y = Math_SinS(this->unk_194 * 0x800) * 5.0f + this->actor.home.pos.y; if (this->unk_194 != 0) { diff --git a/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c index c1e4c28eb65..8624a34ecb2 100644 --- a/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c +++ b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c @@ -784,12 +784,14 @@ void func_80ADFE80(EnPoh* this, PlayState* play) { this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; return; } - if (this->colliderCyl.base.ocFlags1 & OC1_HIT) { - this->actor.flags |= ACTOR_FLAG_WILL_TALK; - func_8002F2F4(&this->actor, play); - } else { - this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; - CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliderCyl.base); + if (GameInteractor_Should(VB_POE_SOUL_TALK_TO_PLAYER, true, this)) { + if (this->colliderCyl.base.ocFlags1 & OC1_HIT) { + this->actor.flags |= ACTOR_FLAG_WILL_TALK; + func_8002F2F4(&this->actor, play); + } else { + this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; + CollisionCheck_SetOC(play, &play->colChkCtx, &this->colliderCyl.base); + } } this->actor.world.pos.y = Math_SinS(this->unk_195 * 0x800) * 5.0f + this->actor.home.pos.y; if (this->unk_195 != 0) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 135c66e89a1..95900cf51dc 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -14662,6 +14662,8 @@ static BottleCatchInfo sBottleCatchInfo[] = { { ACTOR_EN_FISH, ITEM_FISH, PLAYER_IA_BOTTLE_FISH, 0x47 }, // BOTTLE_CATCH_FISH { ACTOR_EN_ICE_HONO, ITEM_BLUE_FIRE, PLAYER_IA_BOTTLE_FIRE, 0x5D }, // BOTTLE_CATCH_BLUE_FIRE { ACTOR_EN_INSECT, ITEM_BUG, PLAYER_IA_BOTTLE_BUG, 0x7A }, // BOTTLE_CATCH_BUGS + { ACTOR_EN_POH, ITEM_POE, PLAYER_IA_BOTTLE_POE, 0x97 }, + { ACTOR_EN_PO_FIELD, ITEM_BIG_POE, PLAYER_IA_BOTTLE_BIG_POE, 0xF9 }, }; void Player_Action_SwingBottle(Player* this, PlayState* play) { @@ -14712,7 +14714,13 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { } } - if (GameInteractor_Should(VB_BOTTLE_ACTOR, i < ARRAY_COUNT(sBottleCatchInfo), this->interactRangeActor)) { + // If the catch is a small field Poe (as opposed to a Big Poe), catch a graveyard Poe instead + if (catchInfo->actorId == ACTOR_EN_PO_FIELD && this->interactRangeActor->params == 0) { + i--; + catchInfo--; + } + + if (GameInteractor_Should(VB_BOTTLE_ACTOR, i + 1 <= BOTTLE_CATCH_BUGS, this->interactRangeActor)) { // 1 is added because `sBottleCatchInfo` does not have an entry for `BOTTLE_CATCH_NONE` this->av1.bottleCatchType = i + 1; From 6cdea077413328c966f40041f8b739843928fbc9 Mon Sep 17 00:00:00 2001 From: lil David <1337lilDavid@gmail.com> Date: Mon, 25 Nov 2024 10:36:38 -0600 Subject: [PATCH 5/5] Extra Underwater Actions --- soh/soh/Enhancements/Mods/lilDavid.cpp | 55 +++++++++++++++++++ .../game-interactor/GameInteractor.h | 29 ++++++++++ soh/src/code/z_parameter.c | 26 +++++++-- .../actors/ovl_player_actor/z_player.c | 35 +++++++++--- 4 files changed, 132 insertions(+), 13 deletions(-) diff --git a/soh/soh/Enhancements/Mods/lilDavid.cpp b/soh/soh/Enhancements/Mods/lilDavid.cpp index b8869cf4e17..c3c057219ec 100644 --- a/soh/soh/Enhancements/Mods/lilDavid.cpp +++ b/soh/soh/Enhancements/Mods/lilDavid.cpp @@ -18,6 +18,7 @@ extern "C" { void func_809B45E0(EnArrow*, PlayState*); void func_809B4640(EnArrow*, PlayState*); void func_80ADFE80(EnPoh*, PlayState*); + int func_808332B8(Player*); } #define AUTHOR "lilDavid" @@ -123,6 +124,55 @@ static void OnConfigurationChanged() { }); + // Extra Underwater Actions + + COND_VB_SHOULD(VB_PLAYER_OPEN_CHEST_OR_LIFT_OBJECT, CVarGetInteger(CVAR("EnhancedIronBoots"), 0), { + Player* player = GET_PLAYER(gPlayState); + Input* sControlInput = &gPlayState->state.input[0]; + + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && + !(player->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) && + player->stateFlags2 & PLAYER_STATE2_UNDERWATER && + player->getItemId != GI_NONE) + { + *should = true; + } + }) + + COND_VB_SHOULD(VB_PLAYER_SHOW_OPEN_GRAB_OR_DROP_DO_ACTION, CVarGetInteger(CVAR("EnhancedIronBoots"), 0), { + Player* player = GET_PLAYER(gPlayState); + int inWaterWithoutBoots = func_808332B8(player); + + if ( + (!(player->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || (player->heldActor == NULL)) && + (player->interactRangeActor != NULL) && + (player->getItemId < 0 && !inWaterWithoutBoots) + ) { + *should = true; + } + }) + + COND_VB_SHOULD(VB_PLAYER_BE_ABLE_TO_USE_ITEM_UNDERWATER, CVarGetInteger(CVAR("EnhancedIronBoots"), 0), { + s8 itemAction = va_arg(args, s8); + + if (Player_ActionToMeleeWeapon(itemAction) != 0 || itemAction == PLAYER_IA_BOMBCHU) + *should = true; + }); + + COND_VB_SHOULD(VB_DISABLE_B_BUTTON_UNDERWATER, CVarGetInteger(CVAR("EnhancedIronBoots"), 0), { + if (Player_GetEnvironmentalHazard(gPlayState) != 2) + return; + + *should = false; + }); + + COND_VB_SHOULD(VB_DISABLE_C_BUTTON_UNDERWATER, CVarGetInteger(CVAR("EnhancedIronBoots"), 0), { + s16 index = va_arg(args, s16); + + if (gSaveContext.equips.buttonItems[index] == ITEM_BOMBCHU) + *should = false; + }); + // Catch Poes with a Bottle COND_VB_SHOULD(VB_BOTTLE_ACTOR, CVarGetInteger(CVAR("MMPoeBottling"), 0), { @@ -189,6 +239,11 @@ static void DrawMenu() { } UIWidgets::Tooltip("Catch Poes by swinging an empty bottle at them instead of from a text box like you can in Majora's Mask."); + if (UIWidgets::PaddedEnhancementCheckbox("Extra Underwater Actions", CVAR("EnhancedIronBoots"))) { + OnConfigurationChanged(); + } + UIWidgets::Tooltip("Allows opening chests and using your sword and Bombchus when underwater with Iron Boots"); + UIWidgets::EnhancementCheckbox("Visual Small Key Display", CVAR("VisualSmallKeys.Enabled")); UIWidgets::Tooltip("Displays Small Key count using multiple icons rather than a numeric counter"); const bool disableKeySpacing = !CVarGetInteger(CVAR("VisualSmallKeys.Enabled"), 0); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index d4cdda128be..1b624065ab7 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -483,6 +483,35 @@ typedef enum { // Opt: *Actor VB_BOTTLE_ACTOR, + /*** Extra Underwater Actions ***/ + /* Vanilla condition: + ``` + CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && + !(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) && + !(this->stateFlags2 & PLAYER_STATE2_UNDERWATER) + ``` + */ + VB_PLAYER_OPEN_CHEST_OR_LIFT_OBJECT, + /* Vanilla condition: + ``` + (!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || (heldActor == NULL)) && + (interactRangeActor != NULL) && + ( + (!sp1C && (this->getItemId == GI_NONE)) || + (this->getItemId < 0 && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)) + ) + ``` + */ + VB_PLAYER_SHOW_OPEN_GRAB_OR_DROP_DO_ACTION, + // Vanilla condition: (itemAction == PLAYER_IA_HOOKSHOT) || (itemAction == PLAYER_IA_LONGSHOT) + // Opt: s32 (itemAction) + VB_PLAYER_BE_ABLE_TO_USE_ITEM_UNDERWATER, + // Vanilla condition: true + VB_DISABLE_B_BUTTON_UNDERWATER, + // Vanilla condition: (gSaveContext.equips.buttonItems[i] != ITEM_HOOKSHOT) && (gSaveContext.equips.buttonItems[i] != ITEM_LONGSHOT) + // Opt: s16 (button index) + VB_DISABLE_C_BUTTON_UNDERWATER, + /*** Catch Poes With a Bottle ***/ // Vanilla condition: true // Opt: *EnPoh diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 777ec1e8085..47d17c1a574 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -967,11 +967,19 @@ void func_80083108(PlayState* play) { gSaveContext.buttonStatus[3] = gSaveContext.buttonStatus[5] = gSaveContext.buttonStatus[6] = gSaveContext.buttonStatus[7] = gSaveContext.buttonStatus[8] = BTN_DISABLED; } else if ((Player_GetEnvironmentalHazard(play) >= 2) && (Player_GetEnvironmentalHazard(play) < 5)) { - if (gSaveContext.buttonStatus[0] != BTN_DISABLED) { - sp28 = 1; - } + if (GameInteractor_Should(VB_DISABLE_B_BUTTON_UNDERWATER, true)) { + if (gSaveContext.buttonStatus[0] != BTN_DISABLED) { + sp28 = 1; + } - gSaveContext.buttonStatus[0] = BTN_DISABLED; + gSaveContext.buttonStatus[0] = BTN_DISABLED; + } else { + if (gSaveContext.buttonStatus[0] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[0] = BTN_ENABLED; + } for (i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) { if ((gSaveContext.equips.buttonItems[i] >= ITEM_SHIELD_DEKU) && @@ -983,8 +991,14 @@ void func_80083108(PlayState* play) { gSaveContext.buttonStatus[BUTTON_STATUS_INDEX(i)] = BTN_ENABLED; } else if (Player_GetEnvironmentalHazard(play) == 2) { - if ((gSaveContext.equips.buttonItems[i] != ITEM_HOOKSHOT) && - (gSaveContext.equips.buttonItems[i] != ITEM_LONGSHOT)) { + if (GameInteractor_Should( + VB_DISABLE_C_BUTTON_UNDERWATER, + ( + (gSaveContext.equips.buttonItems[i] != ITEM_HOOKSHOT) && + (gSaveContext.equips.buttonItems[i] != ITEM_LONGSHOT) + ), + i + )) { if (gSaveContext.buttonStatus[BUTTON_STATUS_INDEX(i)] == BTN_ENABLED) { sp28 = 1; } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 95900cf51dc..4c07bb81dba 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -3432,7 +3432,11 @@ void Player_UseItem(PlayState* play, Player* this, s32 item) { if ((itemAction == PLAYER_IA_NONE) || !(this->stateFlags1 & PLAYER_STATE1_IN_WATER) || ((this->actor.bgCheckFlags & 1) && - ((itemAction == PLAYER_IA_HOOKSHOT) || (itemAction == PLAYER_IA_LONGSHOT)))) { + GameInteractor_Should( + VB_PLAYER_BE_ABLE_TO_USE_ITEM_UNDERWATER, + (itemAction == PLAYER_IA_HOOKSHOT) || (itemAction == PLAYER_IA_LONGSHOT), + itemAction + ))) { if ((play->bombchuBowlingStatus == 0) && (((itemAction == PLAYER_IA_DEKU_STICK) && (AMMO(ITEM_STICK) == 0)) || @@ -7337,8 +7341,16 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) { this->getItemId = GI_NONE; this->getItemEntry = (GetItemEntry)GET_ITEM_NONE; } - } else if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && !(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) && - !(this->stateFlags2 & PLAYER_STATE2_UNDERWATER)) { + } else if ( + GameInteractor_Should( + VB_PLAYER_OPEN_CHEST_OR_LIFT_OBJECT, + ( + CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && + !(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) && + !(this->stateFlags2 & PLAYER_STATE2_UNDERWATER) + ) + ) + ) { if (this->getItemId != GI_NONE) { GetItemEntry giEntry; if (this->getItemEntry.objectId == OBJECT_INVALID) { @@ -10975,10 +10987,19 @@ void Player_UpdateInterface(PlayState* play, Player* this) { (!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || ((heldActor != NULL) && (heldActor->id == ACTOR_EN_RU1)))) { doAction = DO_ACTION_OPEN; - } else if ((!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || (heldActor == NULL)) && - (interactRangeActor != NULL) && - ((!sp1C && (this->getItemId == GI_NONE)) || - (this->getItemId < 0 && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)))) { + } else if ( + GameInteractor_Should( + VB_PLAYER_SHOW_OPEN_GRAB_OR_DROP_DO_ACTION, + ( + (!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || (heldActor == NULL)) && + (interactRangeActor != NULL) && + ( + (!sp1C && (this->getItemId == GI_NONE)) || + (this->getItemId < 0 && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER)) + ) + ) + ) + ) { if (this->getItemId < 0) { doAction = DO_ACTION_OPEN; } else if ((interactRangeActor->id == ACTOR_BG_TOKI_SWD) && LINK_IS_ADULT) {