fix bugs with new quickcast mode

This commit is contained in:
avitasia
2025-12-09 19:51:39 -08:00
parent 9ba7fd77de
commit 2950d91a72
7 changed files with 89 additions and 27 deletions
+6
View File
@@ -271,4 +271,10 @@ namespace Nampower {
void SetNameplateDistance(float distance) {
*reinterpret_cast<float *>(Offsets::NameplateDistance) = distance * distance;
}
bool IsTargetingTerrainSpell() {
const auto s_needTargets = *reinterpret_cast<uint32_t *>(Offsets::SpellNeedsTargets);
return (s_needTargets & game::SpellCastTargetFlags::TARGET_FLAG_SOURCE_LOCATION) ||
(s_needTargets & game::SpellCastTargetFlags::TARGET_FLAG_DEST_LOCATION);
}
}
+2
View File
@@ -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);
+3
View File
@@ -124,6 +124,7 @@ namespace Nampower {
std::unique_ptr<hadesmem::PatchDetour<FastCallPacketHandlerT>> gSpellNonMeleeDmgLogHandlerDetour;
std::unique_ptr<hadesmem::PatchDetour<CGPlayer_C_OnAttackIconPressedT>> gCGPlayer_C_OnAttackIconPressedDetour;
std::unique_ptr<hadesmem::PatchDetour<CGActionBar_UseActionT>> gCGActionBar_UseActionDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraRemovedT>> gCGUnit_C_OnAuraRemovedDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraAddedT>> gCGUnit_C_OnAuraAddedDetour;
std::unique_ptr<hadesmem::PatchDetour<CGUnit_C_OnAuraAddedStackT>> 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<SetCVarT>(process, Offsets::Script_SetCVar, &Script_SetCVarHook);
gCGSpellBook_CastSpellDetour = createHook<CGSpellBook_CastSpellT>(process, Offsets::CGSpellBook_CastSpell, &CGSpellBook_CastSpellHook);
gCGActionBar_UseActionDetour = createHook<CGActionBar_UseActionT>(process, Offsets::CGActionBar_UseAction, &CGActionBar_UseActionHook);
gCastDetour = createHook<CastSpellT>(process, Offsets::Spell_C_CastSpell, &Spell_C_CastSpellHook);
gSendCastDetour = createHook<SendCastT>(process, Offsets::SendCast, &SendCastHook);
gCancelSpellDetour = createHook<CancelSpellT>(process, Offsets::CancelSpell, &CancelSpellHook);
+2 -1
View File
@@ -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);
+4
View File
@@ -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,
+70 -26
View File
@@ -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<uint32_t *>(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<uint32_t *>(
static_cast<uint32_t>(Offsets::CGSpellBook_mKnownSpells) + spellSlot * 4);
} else if (bookType == 1) {
spellId = *reinterpret_cast<uint32_t *>(
static_cast<uint32_t>(Offsets::CGSpellBook_mKnownPetSpells) + spellSlot * 4);
} else {
// call original
cgSpellBookCastSpell(spellSlot, bookType, target);
uint32_t spellId = 0;
if (bookType == 0) {
spellId = *reinterpret_cast<uint32_t *>(
static_cast<uint32_t>(Offsets::CGSpellBook_mKnownSpells) + spellSlot * 4);
} else if (bookType == 1) {
spellId = *reinterpret_cast<uint32_t *>(
static_cast<uint32_t>(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<GetSpellIdFromActionT>(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<CGActionBar_UseActionT>();
return useAction(actionSlot, param_2, param_3);
}
bool
+2
View File
@@ -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);