diff --git a/nampower/helper.cpp b/nampower/helper.cpp index a3bba89..1ae090f 100644 --- a/nampower/helper.cpp +++ b/nampower/helper.cpp @@ -271,4 +271,10 @@ namespace Nampower { void SetNameplateDistance(float distance) { *reinterpret_cast(Offsets::NameplateDistance) = distance * distance; } + + bool IsTargetingTerrainSpell() { + const auto s_needTargets = *reinterpret_cast(Offsets::SpellNeedsTargets); + return (s_needTargets & game::SpellCastTargetFlags::TARGET_FLAG_SOURCE_LOCATION) || + (s_needTargets & game::SpellCastTargetFlags::TARGET_FLAG_DEST_LOCATION); + } } diff --git a/nampower/helper.hpp b/nampower/helper.hpp index 2dd6268..9475d7b 100644 --- a/nampower/helper.hpp +++ b/nampower/helper.hpp @@ -23,6 +23,8 @@ namespace Nampower { bool SpellIsAttackTradeskillOrEnchant(const game::SpellRec *spell); + bool IsTargetingTerrainSpell(); + uint32_t GetGcdOrCooldownForSpell(uint32_t spellId); uint32_t GetRemainingGcdOrCooldownForSpell(uint32_t spellId); diff --git a/nampower/main.cpp b/nampower/main.cpp index a9fc474..e5e7437 100644 --- a/nampower/main.cpp +++ b/nampower/main.cpp @@ -124,6 +124,7 @@ namespace Nampower { std::unique_ptr> gSpellNonMeleeDmgLogHandlerDetour; std::unique_ptr> gCGPlayer_C_OnAttackIconPressedDetour; + std::unique_ptr> gCGActionBar_UseActionDetour; std::unique_ptr> gCGUnit_C_OnAuraRemovedDetour; std::unique_ptr> gCGUnit_C_OnAuraAddedDetour; std::unique_ptr> gCGUnit_C_OnAuraAddedStackDetour; @@ -1060,6 +1061,7 @@ namespace Nampower { loadUserVar("NP_RetryServerRejectedSpells"); loadUserVar("NP_QuickcastTargetingSpells"); + loadUserVar("NP_QuickcastOnDoubleCast"); loadUserVar("NP_ReplaceMatchingNonGcdCategory"); loadUserVar("NP_OptimizeBufferUsingPacketTimings"); @@ -1103,6 +1105,7 @@ namespace Nampower { gSetCVarDetour = createHook(process, Offsets::Script_SetCVar, &Script_SetCVarHook); gCGSpellBook_CastSpellDetour = createHook(process, Offsets::CGSpellBook_CastSpell, &CGSpellBook_CastSpellHook); + gCGActionBar_UseActionDetour = createHook(process, Offsets::CGActionBar_UseAction, &CGActionBar_UseActionHook); gCastDetour = createHook(process, Offsets::Spell_C_CastSpell, &Spell_C_CastSpellHook); gSendCastDetour = createHook(process, Offsets::SendCast, &SendCastHook); gCancelSpellDetour = createHook(process, Offsets::CancelSpell, &CancelSpellHook); diff --git a/nampower/main.hpp b/nampower/main.hpp index 5ba7aef..ad769af 100644 --- a/nampower/main.hpp +++ b/nampower/main.hpp @@ -31,7 +31,7 @@ namespace Nampower { constexpr uint32_t MAJOR_VERSION = 2; constexpr uint32_t MINOR_VERSION = 15; - constexpr uint32_t PATCH_VERSION = 0; + constexpr uint32_t PATCH_VERSION = 1; constexpr int32_t LUA_REGISTRYINDEX = -10000; constexpr int32_t LUA_GLOBALSINDEX = -10001; @@ -100,6 +100,7 @@ namespace Nampower { using Spell_C_GetSpellModifiersT = void (__fastcall *)(const game::SpellRec *spellRec, int *returnVal, game::SpellModOp modOp); using CGPlayer_C_OnAttackIconPressedT = int (__fastcall *)(uintptr_t *this_ptr, void *dummy_edx, uint64_t guid); + using CGActionBar_UseActionT = void (__fastcall *)(uint32_t param_1, int param_2, int param_3); using GetSpellSlotAndTypeT = uint32_t (__fastcall *)(const char *, uint32_t *); using GetSpellSlotFromLuaT = uint32_t (__fastcall *)(int param_1, uint32_t *slot, uint32_t *type); diff --git a/nampower/offsets.hpp b/nampower/offsets.hpp index 3c02dc3..311dfff 100644 --- a/nampower/offsets.hpp +++ b/nampower/offsets.hpp @@ -64,6 +64,10 @@ enum class Offsets : std::uint32_t { CGSpellBook_mKnownPetSpells = 0XB6F098, CGSpellBook_CastSpell = 0x004b3300, + CGActionBar_UseAction = 0x004E5EE0, + CGActionBar_mSlotActions = 0X004E5F52, + GetSpellIdFromAction = 0x004E5A50, + IsSpellInRangeOfUnit = 0X004E56F0, CancelSpell = 0x6E4940, CancelAutoRepeatSpell = 0X006EA080, diff --git a/nampower/spellcast.cpp b/nampower/spellcast.cpp index 9aa690c..abd17e3 100644 --- a/nampower/spellcast.cpp +++ b/nampower/spellcast.cpp @@ -359,42 +359,86 @@ namespace Nampower { // check for double cast to trigger quickcast on targeting spells if (gUserSettings.quickcastOnDoubleCast) { - // check if needs_targets set - auto s_needTargets = *reinterpret_cast(Offsets::SpellNeedsTargets); - if (s_needTargets & game::SpellCastTargetFlags::TARGET_FLAG_SOURCE_LOCATION || - s_needTargets & game::SpellCastTargetFlags::TARGET_FLAG_DEST_LOCATION) { + auto currentTime = GetTime(); - uint32_t spellId = 0; - if (bookType == 0) { - spellId = *reinterpret_cast( - static_cast(Offsets::CGSpellBook_mKnownSpells) + spellSlot * 4); - } else if (bookType == 1) { - spellId = *reinterpret_cast( - static_cast(Offsets::CGSpellBook_mKnownPetSpells) + spellSlot * 4); - } else { - // call original - cgSpellBookCastSpell(spellSlot, bookType, target); + uint32_t spellId = 0; + if (bookType == 0) { + spellId = *reinterpret_cast( + static_cast(Offsets::CGSpellBook_mKnownSpells) + spellSlot * 4); + } else if (bookType == 1) { + spellId = *reinterpret_cast( + static_cast(Offsets::CGSpellBook_mKnownPetSpells) + spellSlot * 4); + } else { + // call original + cgSpellBookCastSpell(spellSlot, bookType, target); + return; + } + + if (IsTargetingTerrainSpell() && spellId > 0) { + if (gCastData.channeling || EffectiveCastEndMs() >= currentTime) { + // if we are already casting block action as that will interrupt return; } - if (spellId > 0) { - // check if aoe spell targeting is active - if (gLastCastData.attemptSpellId == spellId){ - auto spell = game::GetSpellInfo(spellId); - auto spellName = game::GetSpellName(spellId); + // check if aoe spell targeting is active + if (gLastCastData.attemptSpellId == spellId) { + auto spell = game::GetSpellInfo(spellId); + auto spellName = game::GetSpellName(spellId); - // don't mess with summon guardian - if (spell->Effect[0] != game::SPELL_EFFECT_SUMMON_GUARDIAN) { - DEBUG_LOG("Double cast detected for targeting spell " << spellName << ", triggering quickcast"); - TriggerQuickcast(); - return; // don't cast again + // don't mess with summon guardian + if (spell->Effect[0] != game::SPELL_EFFECT_SUMMON_GUARDIAN) { + DEBUG_LOG( + "CastSpell double cast detected for targeting spell " << spellName << + ", triggering quickcast"); + TriggerQuickcast(); + return; // don't cast again + } + } + } + } + cgSpellBookCastSpell(spellSlot, bookType, target); + } + + void CGActionBar_UseActionHook(hadesmem::PatchDetourBase *detour, uint32_t actionSlot, int param_2, int param_3) { + if (-1 > actionSlot && actionSlot < 120) { + uint32_t refValue = 0; + + typedef uint32_t (__fastcall *GetSpellIdFromActionT)(uint32_t actionSlot, uint32_t *refParam); + auto getSpellIdFromAction = reinterpret_cast(Offsets::GetSpellIdFromAction); + + uint32_t spellId = getSpellIdFromAction(actionSlot, &refValue); + if (spellId > 0) { + // check for double cast to trigger quickcast on targeting spells + if (gUserSettings.quickcastOnDoubleCast) { + auto currentTime = GetTime(); + + if (IsTargetingTerrainSpell() && spellId > 0) { + if (gCastData.channeling || EffectiveCastEndMs() >= currentTime) { + // if we are already casting block action as that will interrupt + return; + } + + // check if aoe spell targeting is active + if (gLastCastData.attemptSpellId == spellId) { + auto spell = game::GetSpellInfo(spellId); + auto spellName = game::GetSpellName(spellId); + + // don't mess with summon guardian + if (spell->Effect[0] != game::SPELL_EFFECT_SUMMON_GUARDIAN) { + DEBUG_LOG( + "Use action double cast detected for targeting spell " << spellName << + ", triggering quickcast"); + TriggerQuickcast(); + return; // don't cast again + } } } } } - - cgSpellBookCastSpell(spellSlot, bookType, target); } + + auto const useAction = detour->GetTrampolineT(); + return useAction(actionSlot, param_2, param_3); } bool diff --git a/nampower/spellcast.hpp b/nampower/spellcast.hpp index 265e598..834897c 100644 --- a/nampower/spellcast.hpp +++ b/nampower/spellcast.hpp @@ -19,6 +19,8 @@ namespace Nampower { void CGSpellBook_CastSpellHook(hadesmem::PatchDetourBase *detour, uint32_t spellSlot, int bookType, uint64_t target); + void CGActionBar_UseActionHook(hadesmem::PatchDetourBase *detour, uint32_t actionSlot, int param_2, int param_3); + bool Spell_C_CastSpellHook(hadesmem::PatchDetourBase *detour, uint32_t *casterUnit, uint32_t spellId, uintptr_t *item, std::uint64_t guid);