fix/improve target logic in SPELL_AURA_CAST events

This commit is contained in:
avitasia
2026-01-10 12:45:30 -08:00
parent 9004208b7d
commit 0fbcf6e6c8
6 changed files with 7453 additions and 7347 deletions
+7242 -7239
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -235,6 +235,26 @@ namespace game {
return true;
}
char *UnitGetName(uintptr_t *unit) {
if (!unit) {
return nullptr;
}
using CGUnitGetNameT = char *(__thiscall *)(uintptr_t *this_ptr, uint32_t flag);
auto const GetUnitName = reinterpret_cast<CGUnitGetNameT>(Offsets::CGUnitGetUnitName);
return GetUnitName(unit, 0);
}
bool UnitCanAttackUnit(uintptr_t *unit1, uintptr_t *unit2) {
if (unit1 == nullptr || unit2 == nullptr) {
return false;
}
using CGUnitCanAttackT = bool (__thiscall *)(uintptr_t *this_ptr, uintptr_t *unit);
auto const canAttackFn = reinterpret_cast<CGUnitCanAttackT>(Offsets::CGUnitCanAttack);
return canAttackFn(unit1, unit2);
}
uint32_t UnitGetMountDisplayId(uintptr_t *unit) {
if (!unit) {
return 0;
+70 -64
View File
@@ -969,70 +969,72 @@ namespace game {
};
enum SpellTarget {
TARGET_NONE = 1,
TARGET_UNIT_CASTER = 2,
TARGET_UNIT_ENEMY_NEAR_CASTER = 3,
TARGET_UNIT_FRIEND_NEAR_CASTER = 4,
TARGET_UNIT_NEAR_CASTER = 5,
TARGET_UNIT_CASTER_PET = 6,
TARGET_UNIT_ENEMY = 7,
TARGET_ENUM_UNITS_SCRIPT_AOE_AT_SRC_LOC = 8,
TARGET_ENUM_UNITS_SCRIPT_AOE_AT_DEST_LOC = 9,
TARGET_LOCATION_CASTER_HOME_BIND = 10,
TARGET_LOCATION_CASTER_DIVINE_BIND_NYI = 11,
TARGET_PLAYER_NYI = 12,
TARGET_PLAYER_NEAR_CASTER_NYI = 13,
TARGET_PLAYER_ENEMY_NYI = 14,
TARGET_PLAYER_FRIEND_NYI = 15,
TARGET_ENUM_UNITS_ENEMY_AOE_AT_SRC_LOC = 16,
TARGET_ENUM_UNITS_ENEMY_AOE_AT_DEST_LOC = 17,
TARGET_LOCATION_DATABASE = 18,
TARGET_LOCATION_CASTER_DEST = 19,
TARGET_UNK_19 = 20,
TARGET_ENUM_UNITS_PARTY_WITHIN_CASTER_RANGE = 21,
TARGET_UNIT_FRIEND = 22,
TARGET_LOCATION_CASTER_SRC = 23,
TARGET_GAMEOBJECT = 24,
TARGET_ENUM_UNITS_ENEMY_IN_CONE_24 = 25,
TARGET_UNIT = 26,
TARGET_LOCKED = 27,
TARGET_UNIT_CASTER_MASTER = 28,
TARGET_ENUM_UNITS_ENEMY_AOE_AT_DYNOBJ_LOC = 29,
TARGET_ENUM_UNITS_FRIEND_AOE_AT_DYNOBJ_LOC = 30,
TARGET_ENUM_UNITS_FRIEND_AOE_AT_SRC_LOC = 31,
TARGET_ENUM_UNITS_FRIEND_AOE_AT_DEST_LOC = 32,
TARGET_LOCATION_UNIT_MINION_POSITION = 33,
TARGET_ENUM_UNITS_PARTY_AOE_AT_SRC_LOC = 34,
TARGET_ENUM_UNITS_PARTY_AOE_AT_DEST_LOC = 35,
TARGET_UNIT_PARTY = 36,
TARGET_ENUM_UNITS_ENEMY_WITHIN_CASTER_RANGE = 37, // TODO: only used with dest-effects - reinvestigate naming
TARGET_UNIT_FRIEND_AND_PARTY = 38,
TARGET_UNIT_SCRIPT_NEAR_CASTER = 39,
TARGET_LOCATION_CASTER_FISHING_SPOT = 40,
TARGET_GAMEOBJECT_SCRIPT_NEAR_CASTER = 41,
TARGET_LOCATION_CASTER_FRONT_RIGHT = 42,
TARGET_LOCATION_CASTER_BACK_RIGHT = 43,
TARGET_LOCATION_CASTER_BACK_LEFT = 44,
TARGET_LOCATION_CASTER_FRONT_LEFT = 45,
TARGET_UNIT_FRIEND_CHAIN_HEAL = 46,
TARGET_LOCATION_SCRIPT_NEAR_CASTER = 47,
TARGET_LOCATION_CASTER_FRONT = 48,
TARGET_LOCATION_CASTER_BACK = 49,
TARGET_LOCATION_CASTER_LEFT = 50,
TARGET_LOCATION_CASTER_RIGHT = 51,
TARGET_ENUM_GAMEOBJECTS_SCRIPT_AOE_AT_SRC_LOC = 52,
TARGET_ENUM_GAMEOBJECTS_SCRIPT_AOE_AT_DEST_LOC = 53,
TARGET_LOCATION_CASTER_TARGET_POSITION = 54,
TARGET_ENUM_UNITS_ENEMY_IN_CONE_54 = 55,
TARGET_LOCATION_CASTER_FRONT_LEAP = 56,
TARGET_ENUM_UNITS_RAID_WITHIN_CASTER_RANGE = 57,
TARGET_UNIT_RAID = 58,
TARGET_UNIT_RAID_NEAR_CASTER = 59,
TARGET_ENUM_UNITS_FRIEND_IN_CONE = 60,
TARGET_ENUM_UNITS_SCRIPT_IN_CONE_60 = 61,
TARGET_UNIT_RAID_AND_CLASS = 62,
TARGET_PLAYER_RAID_NYI = 63,
TARGET_LOCATION_UNIT_POSITION = 64,
TARGET_NONE = 0,
TARGET_UNIT_CASTER = 1,
TARGET_UNIT_ENEMY_NEAR_CASTER = 2,
TARGET_UNIT_FRIEND_NEAR_CASTER = 3,
TARGET_UNIT_NEAR_CASTER = 4,
TARGET_UNIT_CASTER_PET = 5,
TARGET_UNIT_ENEMY = 6,
TARGET_ENUM_UNITS_SCRIPT_AOE_AT_SRC_LOC = 7,
TARGET_ENUM_UNITS_SCRIPT_AOE_AT_DEST_LOC = 8,
TARGET_LOCATION_CASTER_HOME_BIND = 9,
TARGET_LOCATION_CASTER_DIVINE_BIND_NYI = 10,
TARGET_PLAYER_NYI = 11,
TARGET_PLAYER_NEAR_CASTER_NYI = 12,
TARGET_PLAYER_ENEMY_NYI = 13,
TARGET_PLAYER_FRIEND_NYI = 14,
TARGET_ENUM_UNITS_ENEMY_AOE_AT_SRC_LOC = 15,
TARGET_ENUM_UNITS_ENEMY_AOE_AT_DEST_LOC = 16,
TARGET_LOCATION_DATABASE = 17,
TARGET_LOCATION_CASTER_DEST = 18,
TARGET_UNK_19 = 19,
TARGET_ENUM_UNITS_PARTY_WITHIN_CASTER_RANGE = 20,
TARGET_UNIT_FRIEND = 21,
TARGET_LOCATION_CASTER_SRC = 22,
TARGET_GAMEOBJECT = 23,
TARGET_ENUM_UNITS_ENEMY_IN_CONE_24 = 24,
TARGET_UNIT = 25,
TARGET_LOCKED = 26,
TARGET_UNIT_CASTER_MASTER = 27,
TARGET_ENUM_UNITS_ENEMY_AOE_AT_DYNOBJ_LOC = 28,
TARGET_ENUM_UNITS_FRIEND_AOE_AT_DYNOBJ_LOC = 29,
TARGET_ENUM_UNITS_FRIEND_AOE_AT_SRC_LOC = 30,
TARGET_ENUM_UNITS_FRIEND_AOE_AT_DEST_LOC = 31,
TARGET_LOCATION_UNIT_MINION_POSITION = 32,
TARGET_ENUM_UNITS_PARTY_AOE_AT_SRC_LOC = 33,
TARGET_ENUM_UNITS_PARTY_AOE_AT_DEST_LOC = 34,
TARGET_UNIT_PARTY = 35,
TARGET_ENUM_UNITS_ENEMY_WITHIN_CASTER_RANGE = 36, // TODO: only used with dest-effects - reinvestigate naming
TARGET_UNIT_FRIEND_AND_PARTY = 37,
TARGET_UNIT_SCRIPT_NEAR_CASTER = 38,
TARGET_LOCATION_CASTER_FISHING_SPOT = 39,
TARGET_GAMEOBJECT_SCRIPT_NEAR_CASTER = 40,
TARGET_LOCATION_CASTER_FRONT_RIGHT = 41,
TARGET_LOCATION_CASTER_BACK_RIGHT = 42,
TARGET_LOCATION_CASTER_BACK_LEFT = 43,
TARGET_LOCATION_CASTER_FRONT_LEFT = 44,
TARGET_UNIT_FRIEND_CHAIN_HEAL = 45,
TARGET_LOCATION_SCRIPT_NEAR_CASTER = 46,
TARGET_LOCATION_CASTER_FRONT = 47,
TARGET_LOCATION_CASTER_BACK = 48,
TARGET_LOCATION_CASTER_LEFT = 49,
TARGET_LOCATION_CASTER_RIGHT = 50,
TARGET_ENUM_GAMEOBJECTS_SCRIPT_AOE_AT_SRC_LOC = 51,
TARGET_ENUM_GAMEOBJECTS_SCRIPT_AOE_AT_DEST_LOC = 52,
TARGET_LOCATION_CASTER_TARGET_POSITION = 53,
TARGET_ENUM_UNITS_ENEMY_IN_CONE_54 = 54,
TARGET_LOCATION_CASTER_FRONT_LEAP = 55,
TARGET_ENUM_UNITS_RAID_WITHIN_CASTER_RANGE = 56,
TARGET_UNIT_RAID = 57,
TARGET_UNIT_RAID_NEAR_CASTER = 58,
TARGET_ENUM_UNITS_FRIEND_IN_CONE = 59,
TARGET_ENUM_UNITS_SCRIPT_IN_CONE_60 = 60,
TARGET_UNIT_RAID_AND_CLASS = 61,
TARGET_PLAYER_RAID_NYI = 62,
TARGET_LOCATION_UNIT_POSITION = 63,
MAX_SPELL_TARGETS
};
// SpellEntry::Targets
@@ -1674,6 +1676,10 @@ namespace game {
uint64_t UnitGetGuid(uintptr_t *unit);
char *UnitGetName(uintptr_t *unit);
bool UnitCanAttackUnit(uintptr_t *unit1, uintptr_t *unit2);
uint64_t UnitGetTargetGuid(uintptr_t *unit);
bool UnitIsPvpFlagged(uintptr_t *unit);
+1 -1
View File
@@ -36,7 +36,7 @@ namespace Nampower {
constexpr uint32_t MAJOR_VERSION = 2;
constexpr uint32_t MINOR_VERSION = 23;
constexpr uint32_t PATCH_VERSION = 4;
constexpr uint32_t PATCH_VERSION = 5;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
constexpr int32_t LUA_GLOBALSINDEX = -10001;
+2
View File
@@ -200,6 +200,8 @@ enum class Offsets : std::uint32_t {
CGUnit_C_OnAuraAdded = 0x006123f0,
CGUnit_C_OnAuraAddedStack = 0x0062b800,
UnitCombatLogUnitDead = 0x0062c160,
CGUnitGetUnitName = 0x00609210,
CGUnitCanAttack = 0x00606980,
CGPlayer_C_OnAttackIconPressed = 0X006131A0,
+118 -43
View File
@@ -9,6 +9,7 @@
namespace Nampower {
auto const APPLY_BUFFER_TO_GCD = false; // gcd issue seems fixed for now
constexpr int MAX_ALLOWED_SPELL_TARGETS = 100;
void SetReleaseAction(uint32_t input) {
uint32_t activeControl = *reinterpret_cast<uint32_t *>(Offsets::CGInputControlGetActive);
@@ -902,18 +903,24 @@ namespace Nampower {
return false;
}
void TriggerAuraCastEvent(const game::SpellRec *spell,
uint64_t casterGuid,
uint64_t targetGuid,
uint64_t activePlayerGuid,
bool castByActivePlayer) {
void triggerAuraCastEventOnTarget(const game::SpellRec *spell,
int spellEffectIndex,
char *casterGuidStr,
char *targetGuidStr, // if provided use this instead of creating one
uint64_t targetGuid,
uint64_t activePlayerGuid,
uint32_t effectType,
uint32_t duration) {
static char format[] = "%d%s%s%d%d%d%d%d%d";
auto eventToTrigger = game::AURA_CAST_ON_OTHER;
if (targetGuid == activePlayerGuid) {
eventToTrigger = game::AURA_CAST_ON_SELF;
}
// ignore modifiers if not cast by active player
auto duration = game::GetSpellDuration(spell, !castByActivePlayer);
auto auraName = spell->EffectApplyAuraName[spellEffectIndex];
auto effectAmplitude = spell->EffectAmplitude[spellEffectIndex];
auto effectMiscValue = spell->EffectMiscValue[spellEffectIndex];
auto *targetUnit = game::ClntObjMgrObjectPtr(
static_cast<game::TypeMask>(game::TYPEMASK_PLAYER | game::TYPEMASK_UNIT), targetGuid);
@@ -921,9 +928,54 @@ namespace Nampower {
bool targetIsBuffCapped = game::UnitIsBuffCapped(targetUnit);
bool targetIsDebuffCapped = game::UnitIsDebuffCapped(targetUnit);
bool createdGuidStr = false;
if (!targetGuidStr) {
if (targetGuid > 0) {
targetGuidStr = ConvertGuidToString(targetGuid);
createdGuidStr = true;
}
}
auto auraCapStatus = static_cast<uint32_t>(targetIsBuffCapped) |
(static_cast<uint32_t>(targetIsDebuffCapped) << 1);
reinterpret_cast<int (__cdecl *)(int eventCode,
char *fmt,
uint32_t spellIdParam,
char *casterGuidStrParam,
char *targetGuidStrParam,
uint32_t effectParam,
uint32_t auraNameParam,
uint32_t effectAmplitudeParam,
uint32_t effectMiscValueParam,
uint32_t durationParam,
uint32_t auraCapStatusParam)>(Offsets::SignalEventParam)(
eventToTrigger,
format,
spell->Id,
casterGuidStr,
targetGuidStr,
effectType,
auraName,
effectAmplitude,
effectMiscValue,
duration,
auraCapStatus);
if (createdGuidStr) {
delete [] targetGuidStr;
}
}
void TriggerAuraCastEvent(const game::SpellRec *spell,
uint64_t casterGuid,
uint64_t targetGuids[MAX_ALLOWED_SPELL_TARGETS],
uint8_t numTargets,
uint64_t activePlayerGuid,
bool castByActivePlayer) {
// ignore modifiers if not cast by active player
auto duration = game::GetSpellDuration(spell, !castByActivePlayer);
char *casterGuidStr = ConvertGuidToString(casterGuid);
char *targetGuidStr = ConvertGuidToString(targetGuid);
char format[] = "%d%s%s%d%d%d%d%d%d";
for (int i = 0; i < 3; ++i) {
auto const effectType = spell->Effect[i];
@@ -934,35 +986,59 @@ namespace Nampower {
case game::SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
case game::SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
case game::SPELL_EFFECT_APPLY_AREA_AURA_PET: {
auto auraName = spell->EffectApplyAuraName[i];
auto effectAmplitude = spell->EffectAmplitude[i];
auto effectMiscValue = spell->EffectMiscValue[i];
auto const implicitTargetA = spell->EffectImplicitTargetA[i];
auto const implicitTargetB = spell->EffectImplicitTargetB[i];
auto auraCapStatus = static_cast<uint32_t>(targetIsBuffCapped) |
(static_cast<uint32_t>(targetIsDebuffCapped) << 1);
if (implicitTargetA == game::TARGET_UNIT_CASTER) {
triggerAuraCastEventOnTarget(spell, i, casterGuidStr, casterGuidStr, casterGuid,
activePlayerGuid,
effectType, duration);
break;
}
// handle no targets
if (numTargets == 0 || implicitTargetA == game::TARGET_NONE) {
triggerAuraCastEventOnTarget(spell, i, casterGuidStr, nullptr, 0, activePlayerGuid,
effectType, duration);
} else if ((implicitTargetA >= game::TARGET_UNIT_ENEMY_NEAR_CASTER && implicitTargetA <=
game::TARGET_UNIT_ENEMY) ||
(implicitTargetA >= game::TARGET_PLAYER_NYI && implicitTargetA <=
game::TARGET_PLAYER_FRIEND_NYI) ||
implicitTargetA == game::TARGET_UNIT_FRIEND ||
implicitTargetA == game::TARGET_UNIT ||
implicitTargetA == game::TARGET_UNIT_CASTER_MASTER) {
// trigger on first target
triggerAuraCastEventOnTarget(spell, i, casterGuidStr, nullptr, targetGuids[0], activePlayerGuid,
effectType, duration);
} else if (implicitTargetA == game::TARGET_ENUM_UNITS_ENEMY_AOE_AT_SRC_LOC ||
implicitTargetA == game::TARGET_ENUM_UNITS_ENEMY_AOE_AT_DEST_LOC ||
implicitTargetA == game::TARGET_ENUM_UNITS_ENEMY_IN_CONE_24 ||
implicitTargetA == game::TARGET_ENUM_UNITS_ENEMY_AOE_AT_DYNOBJ_LOC ||
implicitTargetA == game::TARGET_ENUM_UNITS_ENEMY_WITHIN_CASTER_RANGE ||
implicitTargetA == game::TARGET_ENUM_UNITS_ENEMY_IN_CONE_54 ||
implicitTargetB == game::TARGET_ENUM_UNITS_ENEMY_AOE_AT_SRC_LOC ||
implicitTargetB == game::TARGET_ENUM_UNITS_ENEMY_AOE_AT_DEST_LOC ||
implicitTargetB == game::TARGET_ENUM_UNITS_ENEMY_IN_CONE_24 ||
implicitTargetB == game::TARGET_ENUM_UNITS_ENEMY_AOE_AT_DYNOBJ_LOC ||
implicitTargetB == game::TARGET_ENUM_UNITS_ENEMY_WITHIN_CASTER_RANGE ||
implicitTargetB == game::TARGET_ENUM_UNITS_ENEMY_IN_CONE_54) {
// trigger on all targets except caster
for (int t = 0; t < numTargets; ++t) {
if (targetGuids[t] != casterGuid) {
triggerAuraCastEventOnTarget(spell, i, casterGuidStr, nullptr, targetGuids[t],
activePlayerGuid,
effectType, duration);
}
}
} else {
// trigger on all targets (probably some exceptions but good enough for now)
for (int t = 0; t < numTargets; ++t) {
triggerAuraCastEventOnTarget(spell, i, casterGuidStr, nullptr, targetGuids[t],
activePlayerGuid,
effectType, duration);
}
}
reinterpret_cast<int (__cdecl *)(int eventCode,
char *fmt,
uint32_t spellIdParam,
char *casterGuidStrParam,
char *targetGuidStrParam,
uint32_t effectParam,
uint32_t auraNameParam,
uint32_t effectAmplitudeParam,
uint32_t effectMiscValueParam,
uint32_t durationParam,
uint32_t auraCapStatusParam)>(Offsets::SignalEventParam)(
eventToTrigger,
format,
spell->Id,
casterGuidStr,
targetGuidStr,
effectType,
auraName,
effectAmplitude,
effectMiscValue,
duration,
auraCapStatus);
break;
}
default:
@@ -971,7 +1047,6 @@ namespace Nampower {
}
delete[] casterGuidStr;
delete[] targetGuidStr;
}
void
@@ -986,10 +1061,11 @@ namespace Nampower {
uint8_t numTargets;
spellData->Get(numTargets);
uint64_t targetGuid;
for (int i = 0; i < numTargets; ++i) {
// spell go uses the last target in the list, I think target list is usually just length 1
spellData->Get(targetGuid);
static uint64_t targetGuids[MAX_ALLOWED_SPELL_TARGETS]; // don't allocate new array each time
// each target corresponds to a spell effect, should never have more than 3
for (int i = 0; i < numTargets && i < MAX_ALLOWED_SPELL_TARGETS; ++i) {
targetGuids[i] = 0;
spellData->Get(targetGuids[i]);
}
// reset read pointer
@@ -1045,7 +1121,7 @@ namespace Nampower {
// itemId = game::GetItemId(item);
// }
// }
TriggerAuraCastEvent(spell, *casterGUID, targetGuid, activePlayerGuid, castByActivePlayer);
TriggerAuraCastEvent(spell, *casterGUID, targetGuids, numTargets, activePlayerGuid, castByActivePlayer);
}
}
@@ -1073,7 +1149,6 @@ namespace Nampower {
}
void SendCastHook(hadesmem::PatchDetourBase *detour, game::SpellCast *cast, char unk) {
auto const sendCast = detour->GetTrampolineT<SendCastT>();
sendCast(cast, unk);