From 7480cd3c34d6b903a0f19aeb5527cc241020c1b8 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Wed, 1 Apr 2026 18:11:49 +0200 Subject: [PATCH 1/3] Implement Quick Transform from Rando --- files.cmake | 1 + include/d/actor/d_a_alink.h | 5 ++ include/d/d_meter2_draw.h | 6 ++ src/d/actor/d_a_alink_quicktransform.cpp | 74 +++++++++++++++++++++++ src/dusk/imgui/ImGuiMenuGame.cpp | 1 + src/f_ap/f_ap_game.cpp | 77 ++++++++++++++++++++++++ 6 files changed, 164 insertions(+) create mode 100644 src/d/actor/d_a_alink_quicktransform.cpp diff --git a/files.cmake b/files.cmake index 8504aecb95..aeb068b583 100644 --- a/files.cmake +++ b/files.cmake @@ -1332,6 +1332,7 @@ set(DOLPHIN_FILES set(DUSK_FILES include/dusk/endian_gx.hpp + src/d/actor/d_a_alink_quicktransform.cpp src/dusk/asserts.cpp src/dusk/logging.cpp src/dusk/stubs.cpp diff --git a/include/d/actor/d_a_alink.h b/include/d/actor/d_a_alink.h index a4d38efc43..93668c550a 100644 --- a/include/d/actor/d_a_alink.h +++ b/include/d/actor/d_a_alink.h @@ -4547,6 +4547,10 @@ public: /* 0x03848 */ cXyz* field_0x3848; /* 0x0384C */ cXyz* field_0x384c; /* 0x03850 */ daAlink_procFunc mpProcFunc; + +#if TARGET_PC + void handleQuickTransform(); +#endif }; // Size: 0x385C class daAlinkHIO_data_c : public JORReflexible { @@ -8428,6 +8432,7 @@ inline daAlink_c* daAlink_getAlinkActorClass() { #if TARGET_PC namespace dusk::tweaks { extern bool FastIronBoots; + extern bool QuickTransform; } #endif diff --git a/include/d/d_meter2_draw.h b/include/d/d_meter2_draw.h index c11bd680bb..10cded803d 100644 --- a/include/d/d_meter2_draw.h +++ b/include/d/d_meter2_draw.h @@ -148,6 +148,12 @@ public: void setEmphasisB(u8 param_0) { field_0x762 = param_0; } u8 getInsideObjCheck() { return field_0x772; } +#if TARGET_PC + constexpr f32 getButtonZAlpha() const { + return mButtonZAlpha; + } +#endif + private: /* 0x004 */ item_params mItemParams[4]; /* 0x074 */ JKRExpHeap* heap; diff --git a/src/d/actor/d_a_alink_quicktransform.cpp b/src/d/actor/d_a_alink_quicktransform.cpp new file mode 100644 index 0000000000..f193651bae --- /dev/null +++ b/src/d/actor/d_a_alink_quicktransform.cpp @@ -0,0 +1,74 @@ +#include "d/actor/d_a_alink.h" +#include "d/actor/d_a_midna.h" +#include "d/d_meter2.h" +#include "d/d_meter2_draw.h" +#include "d/d_meter2_info.h" + +bool dusk::tweaks::QuickTransform = false; + +void daAlink_c::handleQuickTransform() { + if (!dusk::tweaks::QuickTransform) { + return; + } + + // Ensure that link is not in a cutscene. + if (checkEventRun()) { + return; + } + + // Check to see if Link has the ability to transform. + if (!dComIfGs_isEventBit(dSv_event_flag_c::M_077)) { + return; + } + + // Make sure Link isn't riding anything (horse, boar, etc.) + if (checkRide()) { + return; + } + + switch (mProcID) { + // Make sure Link is not underwater or talking to someone. + case PROC_TALK: + case PROC_SWIM_UP: + case PROC_SWIM_DIVE: + return; + // If Link is targeting or pulling a chain, we don't want to remove the ability to use items in combat accidently. + case PROC_ATN_ACTOR_MOVE: + case PROC_ATN_ACTOR_WAIT: + case PROC_WOLF_ATN_AC_MOVE: + break; + default: + // Disable the input that was just pressed, as sometimes it could cause items to be used or Wolf Link to dig. + mDoCPd_c::getCpadInfo(PAD_1).mPressedButtonFlags = 0; + break; + } + + // Ensure there is a proper pointer to the mMeterClass and mpMeterDraw structs in g_meter2_info. + const auto meterClassPtr = g_meter2_info.getMeterClass(); + if (!meterClassPtr) { + return; + } + + const auto meterDrawPtr = meterClassPtr->getMeterDrawPtr(); + if (!meterDrawPtr) { + return; + } + + // Ensure that the Z Button is not dimmed + if (meterDrawPtr->getButtonZAlpha() != 1.f) { + return; + } + + // The game will crash if trying to quick transform while holding the Ball and Chain + if (mEquipItem == dItemNo_IRONBALL_e) { + return; + } + + // Use the game's default checks for if the player can currently transform + if (!m_midnaActor->checkMetamorphoseEnableBase()) { + return; + } + + OSReport("Running quick transform!"); + procCoMetamorphoseInit(); +} \ No newline at end of file diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 7b90dcb5b5..835fed0f7c 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -78,6 +78,7 @@ namespace dusk { if (ImGui::BeginMenu("Tweaks")) { ImGui::MenuItem("Fast iron boots", nullptr, &tweaks::FastIronBoots); + ImGui::MenuItem("Quick Transform (R+Y)", nullptr, &tweaks::QuickTransform); ImGui::EndMenu(); } diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index 520d85d20f..e97e97148c 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -721,6 +721,67 @@ void fapGm_After() { fopCamM_Management(); } +#if TARGET_PC +// Analog L is currently not being used, so commented out +// static f32 prevFrameAnalogL = 0.f; +static f32 prevFrameAnalogR = 0.f; + +static bool DuskCheckButtonCombo(u32 combo, bool checkAnalog) { + const auto& padInfo = mDoCPd_c::getCpadInfo(PAD_1); + + // Get the buttons that are currently held and were pressed this frame + uint32_t buttonsHeld = padInfo.mButtonFlags; + uint32_t buttonsPressedThisFrame = padInfo.mPressedButtonFlags; + + if (checkAnalog) { + // Get the threshold for the analog buttons + constexpr float analogThreshold = 0.7f; // 70% + + // Check if L is included in the button combo + // Analog L is currently not being used, so commented out + /* + if ( combo & PadInputs::Button_L ) + { + // Check if analog L is at 70% or more + if ( padInfo->mTriggerLeft >= analogThreshold ) + { + // Manually set the bit for L being pressed + buttonsHeld |= PadInputs::Button_L; + + // If prevFrameAnalogL is less than 70%, then 70% was reached this frame + if ( prevFrameAnalogL < analogThreshold ) + { + buttonsPressedThisFrame |= PadInputs::Button_L; + } + } + } + */ + + // Check if R is included in the button combo + if (combo & PAD_TRIGGER_R) { + // Check if analog R is at 70% or more + if (padInfo.mTriggerRight >= analogThreshold) { + // Manually set the bit for R being pressed + buttonsHeld |= PAD_TRIGGER_R; + + // If prevFrameAnalogR is less than 70%, then 70% was reached this frame + if (prevFrameAnalogR < analogThreshold) { + buttonsPressedThisFrame |= PAD_TRIGGER_R; + } + } + } + } + + // Check if the button combo is held + if ((buttonsHeld & combo) != combo) { + return false; + } + + // Check if at least one button in the combo was pressed this frame + return buttonsPressedThisFrame & combo; +} +#endif + void fapGm_Execute() { static u32 sExecCount = 0; if (sExecCount < 10 || (sExecCount % 300 == 0)) { @@ -732,8 +793,24 @@ void fapGm_Execute() { JUTDbPrint::getManager()->setCharColor(g_HIO.mColor); #endif +#if TARGET_PC + const auto& padInfo = mDoCPd_c::getCpadInfo(PAD_1); + if (DuskCheckButtonCombo(PAD_TRIGGER_R | PAD_BUTTON_Y, true)) { + if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) { + dynamic_cast(link)->handleQuickTransform(); + } + } +#endif + fpcM_Management(NULL, fapGm_After); cCt_Counter(0); + +#if TARGET_PC + // Main code has ran, so update previous frame variables + // Analog L is currently not being used, so commented out + // prevFrameAnalogL = padInfo.mTriggerLeft; + prevFrameAnalogR = padInfo.mTriggerRight; +#endif } fapGm_HIO_c g_HIO; From c6eb705950e61dac3cd7dd09aa7c5d839739937c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 2 Apr 2026 23:45:22 +0200 Subject: [PATCH 2/3] Just use mDoCPd_c for the input check --- src/f_ap/f_ap_game.cpp | 71 +----------------------------------------- 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index e97e97148c..1cbbb7449f 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -721,67 +721,6 @@ void fapGm_After() { fopCamM_Management(); } -#if TARGET_PC -// Analog L is currently not being used, so commented out -// static f32 prevFrameAnalogL = 0.f; -static f32 prevFrameAnalogR = 0.f; - -static bool DuskCheckButtonCombo(u32 combo, bool checkAnalog) { - const auto& padInfo = mDoCPd_c::getCpadInfo(PAD_1); - - // Get the buttons that are currently held and were pressed this frame - uint32_t buttonsHeld = padInfo.mButtonFlags; - uint32_t buttonsPressedThisFrame = padInfo.mPressedButtonFlags; - - if (checkAnalog) { - // Get the threshold for the analog buttons - constexpr float analogThreshold = 0.7f; // 70% - - // Check if L is included in the button combo - // Analog L is currently not being used, so commented out - /* - if ( combo & PadInputs::Button_L ) - { - // Check if analog L is at 70% or more - if ( padInfo->mTriggerLeft >= analogThreshold ) - { - // Manually set the bit for L being pressed - buttonsHeld |= PadInputs::Button_L; - - // If prevFrameAnalogL is less than 70%, then 70% was reached this frame - if ( prevFrameAnalogL < analogThreshold ) - { - buttonsPressedThisFrame |= PadInputs::Button_L; - } - } - } - */ - - // Check if R is included in the button combo - if (combo & PAD_TRIGGER_R) { - // Check if analog R is at 70% or more - if (padInfo.mTriggerRight >= analogThreshold) { - // Manually set the bit for R being pressed - buttonsHeld |= PAD_TRIGGER_R; - - // If prevFrameAnalogR is less than 70%, then 70% was reached this frame - if (prevFrameAnalogR < analogThreshold) { - buttonsPressedThisFrame |= PAD_TRIGGER_R; - } - } - } - } - - // Check if the button combo is held - if ((buttonsHeld & combo) != combo) { - return false; - } - - // Check if at least one button in the combo was pressed this frame - return buttonsPressedThisFrame & combo; -} -#endif - void fapGm_Execute() { static u32 sExecCount = 0; if (sExecCount < 10 || (sExecCount % 300 == 0)) { @@ -794,8 +733,7 @@ void fapGm_Execute() { #endif #if TARGET_PC - const auto& padInfo = mDoCPd_c::getCpadInfo(PAD_1); - if (DuskCheckButtonCombo(PAD_TRIGGER_R | PAD_BUTTON_Y, true)) { + if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigY(PAD_1)) { if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) { dynamic_cast(link)->handleQuickTransform(); } @@ -804,13 +742,6 @@ void fapGm_Execute() { fpcM_Management(NULL, fapGm_After); cCt_Counter(0); - -#if TARGET_PC - // Main code has ran, so update previous frame variables - // Analog L is currently not being used, so commented out - // prevFrameAnalogL = padInfo.mTriggerLeft; - prevFrameAnalogR = padInfo.mTriggerRight; -#endif } fapGm_HIO_c g_HIO; From ea6a3578879c03301cce0927735577a1260a8fb8 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 2 Apr 2026 23:45:35 +0200 Subject: [PATCH 3/3] Rename d_a_alink_quicktransform.cpp to d_a_alink_dusk.cpp --- files.cmake | 2 +- .../actor/{d_a_alink_quicktransform.cpp => d_a_alink_dusk.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/d/actor/{d_a_alink_quicktransform.cpp => d_a_alink_dusk.cpp} (100%) diff --git a/files.cmake b/files.cmake index 6c04d9a22e..26cae0f1a9 100644 --- a/files.cmake +++ b/files.cmake @@ -1332,7 +1332,7 @@ set(DOLPHIN_FILES set(DUSK_FILES include/dusk/endian_gx.hpp - src/d/actor/d_a_alink_quicktransform.cpp + src/d/actor/d_a_alink_dusk.cpp src/dusk/asserts.cpp src/dusk/logging.cpp src/dusk/layout.cpp diff --git a/src/d/actor/d_a_alink_quicktransform.cpp b/src/d/actor/d_a_alink_dusk.cpp similarity index 100% rename from src/d/actor/d_a_alink_quicktransform.cpp rename to src/d/actor/d_a_alink_dusk.cpp