add GetSpellRangeData, restrict some functions/events in PVP

This commit is contained in:
avitasia
2026-03-10 12:53:18 -07:00
parent a82e8d8be4
commit 88dc717c91
13 changed files with 14273 additions and 14161 deletions
+1
View File
@@ -198,6 +198,7 @@ This includes functions for:
- Inventory helpers (GetTrinketCooldown, GetTrinkets, GetAmmo)
- Aura duration tracking, cancel helpers, and aura visibility checks (GetPlayerAuraDuration, CancelPlayerAuraSlot, CancelPlayerAuraSpellId, IsAuraHidden)
- Spell duration lookup (GetSpellDuration) - returns channel duration for channeling spells and the first aura effect duration for non-channeling spells
- Spell range lookup (GetSpellRangeData) - returns minRange, maxRange, flags, name for a SpellRange DBC entry by rangeIndex
- Player movement state queries (PlayerIsMoving, PlayerIsRooted, PlayerIsSwimming)
- File and script helpers (WriteCustomFile, ReadCustomFile, CustomFileExists, ImportFile, ExportFile, ExecuteCustomLuaFile)
- Encrypted login helpers (EncryptPassword, EncryptedServerLogin)
+25
View File
@@ -22,6 +22,7 @@ For custom events, see [EVENTS.md](EVENTS.md). For installation, configuration,
- [GetSpellModifiers](#getspellmodifiersspellid-modifiertype)
- [GetSpellPower](#getspellpowermode)
- [GetSpellDuration](#getspelldurationspellid-ignoremodifiers)
- [GetSpellRangeData](#getspellrangedatarangeindex)
- [GetUnitData](#getunitdataunittoken-copy)
- [GetUnitField](#getunitfieldunittoken-fieldname-copy)
- [GetUnitGUID](#getunitguidunittoken)
@@ -708,6 +709,30 @@ local duration = GetSpellDuration(spellId)
local baseDuration = GetSpellDuration(spellId, 1)
```
#### GetSpellRangeData(rangeIndex)
Returns range data for a SpellRange DBC entry by its index (the `rangeIndex` field from SpellRec).
**Parameters:**
- `rangeIndex` (number): The SpellRange DBC index to look up.
**Returns:**
- `minRange` (number) - Base minimum range in yards (before combat reach is applied).
- `maxRange` (number) - Base maximum range in yards (before combat reach is applied).
- `flags` (number) - Either `0` (ranged) or `1` (melee). `1` is only currently used by `Combat Range` spells.
- `name` (string) - Localized name of the range entry (e.g. `"Combat Range"`, `"Self Only"`).
- `nil` - If the rangeIndex is out of bounds or the entry is missing.
**Notes on flags and combat reach:**
The returned `minRange`/`maxRange` are the raw DBC values. The game adds additional range based on the combat reach of the caster and target as well as 2.667 range for leeway if either the player or target is moving or pvp flagged. `Combat Range` spells also depend on the scale of the target and add an additional 1.333333 range.
**Examples:**
```lua
local rangeIndex = GetSpellRecField(spellId, "rangeIndex")
local minRange, maxRange, flags, name = GetSpellRangeData(rangeIndex)
local isMelee = flags == 1
```
#### GetUnitData(unitToken, [copy])
Returns a Lua table reference containing all unit fields for the specified unit. This provides access to low-level unit data like health, mana, stats, auras, resistances, and more.
+14154 -14151
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -157,6 +157,10 @@ namespace Nampower {
}
}
if (!isSelf && isBuff && game::UnitIsPvpFlagged(unit) && game::PlayerCanAttackUnit(unit)) {
return;
}
bool isHidden = IsHiddenAura(unitFields, slot, spellId, state);
static char format[] = "%s%d%d%d%d%d%d";
char *guidStr = ConvertGuidToString(isSelf ? playerGuid : unitGuid);
+7
View File
@@ -355,6 +355,13 @@ namespace game {
return canAttackFn(unit1, unit2);
}
bool PlayerCanAttackUnit(uintptr_t *unit) {
auto playerGuid = ClntObjMgrGetActivePlayerGuid();
if (!playerGuid) return false;
auto playerUnit = GetObjectPtr(playerGuid);
return playerUnit && UnitCanAttackUnit(playerUnit, unit);
}
uint32_t UnitGetMountDisplayId(uintptr_t *unit) {
if (!unit) {
return 0;
+2
View File
@@ -2082,6 +2082,8 @@ namespace game {
bool UnitCanAttackUnit(uintptr_t *unit1, uintptr_t *unit2);
bool PlayerCanAttackUnit(uintptr_t *unit);
uint64_t UnitGetTargetGuid(uintptr_t *unit);
uint64_t UnitGetOwnerGuid(uintptr_t *unit);
+9 -6
View File
@@ -102,7 +102,7 @@ namespace Nampower {
CastQueue gCastHistory = CastQueue(30);
std::unique_ptr<hadesmem::PatchDetour<SysMsgInitializeT> > gSysMsgInitDetour;
std::unique_ptr<hadesmem::PatchDetour<WowSysMessageOutputInitializeT> > gSysMsgInitDetour;
std::unique_ptr<hadesmem::PatchDetour<LoadScriptFunctionsT> > gLoadScriptFunctionsDetour;
std::unique_ptr<hadesmem::PatchDetour<FrameScript_CreateEventsT> > gCreateEventsDetour;
std::unique_ptr<hadesmem::PatchDetour<FramescriptSetEventCountT> > gSetEventCountDetour;
@@ -1613,7 +1613,7 @@ namespace Nampower {
}
void SysMsgInitializeHook(hadesmem::PatchDetourBase *detour) {
auto const sysMsgInitialize = detour->GetTrampolineT<SysMsgInitializeT>();
auto const sysMsgInitialize = detour->GetTrampolineT<WowSysMessageOutputInitializeT>();
sysMsgInitialize();
std::call_once(initHooksFlag, []() {
loadConfig();
@@ -1971,6 +1971,9 @@ namespace Nampower {
char getSpellDuration[] = "GetSpellDuration";
RegisterLuaFunction(getSpellDuration, reinterpret_cast<uintptr_t *>(Script_GetSpellDuration));
char getSpellRangeData[] = "GetSpellRangeData";
RegisterLuaFunction(getSpellRangeData, reinterpret_cast<uintptr_t *>(Script_GetSpellRangeData));
char learnTalentRank[] = "LearnTalentRank";
RegisterLuaFunction(learnTalentRank, reinterpret_cast<uintptr_t *>(Script_LearnTalentRank));
@@ -2047,12 +2050,12 @@ namespace Nampower {
void load() {
std::call_once(loadFlag, []() {
// hook SysMsgInitialize (bootstrap hook)
// hook WowSysMessageOutput::Initialize (bootstrap hook)
const hadesmem::Process process(::GetCurrentProcessId());
auto const sysMsgInitOrig = hadesmem::detail::AliasCast<SysMsgInitializeT>(
Offsets::SysMsgInitialize);
gSysMsgInitDetour = std::make_unique<hadesmem::PatchDetour<SysMsgInitializeT> >(
auto const sysMsgInitOrig = hadesmem::detail::AliasCast<WowSysMessageOutputInitializeT>(
Offsets::WowSysMessageOutputInitialize);
gSysMsgInitDetour = std::make_unique<hadesmem::PatchDetour<WowSysMessageOutputInitializeT> >(
process,
sysMsgInitOrig,
&SysMsgInitializeHook);
+3 -3
View File
@@ -25,8 +25,8 @@ namespace Nampower {
constexpr uint32_t DISENCHANT_QUALITY_BLUE = 0x02; // Rare
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
constexpr uint32_t MAJOR_VERSION = 3;
constexpr uint32_t MINOR_VERSION = 4;
constexpr uint32_t MAJOR_VERSION = 4;
constexpr uint32_t MINOR_VERSION = 0;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
@@ -169,7 +169,7 @@ namespace Nampower {
int clearCooldowns);
using SpellVisualsInitializeT = void (__stdcall *)(void);
using SysMsgInitializeT = void (__fastcall *)(void);
using WowSysMessageOutputInitializeT = void (__fastcall *)(void);
using PlaySpellVisual = void (__stdcall *)(int **param_1, void *param_2, int param_3, void **param_4);
using CGUnit_C_ClearCastingSpellT = void (__thiscall *)(uintptr_t *unit, uint32_t param_1, int param_2,
+12
View File
@@ -390,6 +390,12 @@ namespace Nampower {
return 1;
}
// Block data for enemy PvP-flagged units
if (game::UnitIsPvpFlagged(unit) && game::PlayerCanAttackUnit(unit)) {
lua_pushnil(luaState);
return 1;
}
// Get unit fields (offset 68 from unit pointer)
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
if (!unitFields) {
@@ -475,6 +481,12 @@ namespace Nampower {
return 1;
}
// Block data for enemy PvP-flagged units
if (game::UnitIsPvpFlagged(unit) && game::PlayerCanAttackUnit(unit)) {
lua_pushnil(luaState);
return 1;
}
// Get unit fields (offset 68 from unit pointer)
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
if (!unitFields) {
+4 -1
View File
@@ -215,7 +215,7 @@ enum class Offsets : std::uint32_t {
RangeCheckSelected = 0x6E4440,
SpellVisualsInitialize = 0x006ec0e0,
SysMsgInitialize = 0x0044cd10,
WowSysMessageOutputInitialize = 0x00409040,
UnitCombatParamFormat = 0x0084333c,
IntIntParamFormat = 0X00843342,
@@ -259,6 +259,9 @@ enum class Offsets : std::uint32_t {
CGItem_C_GetInventoryArt = 0x005D88B0,
CGItem_C_ItemIsQuestOrSoulbound = 0x005DA2C0,
SpellRangeDBMaxId = 0x00c0d7a0,
SpellRangeDBArray = 0x00c0d79c,
SpellIconDBMaxId = 0x00c0d7f0,
SpellIconDBArray = 0x00c0d7ec,
+49
View File
@@ -724,4 +724,53 @@ namespace Nampower {
return 1;
}
// SpellRange DBC record layout:
// +0x00 id (uint32)
// +0x04 minRange (float)
// +0x08 maxRange (float)
// +0x0c flags (uint32)
// +0x10 name (char* x8 locales + uint32 mask = 36 bytes)
uint32_t Script_GetSpellRangeData(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetSpellRangeData(rangeIndex)");
return 0;
}
auto const rangeIndex = static_cast<int32_t>(lua_tonumber(luaState, 1));
int32_t maxRangeId = *reinterpret_cast<int32_t *>(Offsets::SpellRangeDBMaxId);
uintptr_t *rangeArray = *reinterpret_cast<uintptr_t **>(Offsets::SpellRangeDBArray);
if (rangeIndex < 0 || rangeIndex > maxRangeId) {
lua_pushnil(luaState);
return 1;
}
uintptr_t spellRange = reinterpret_cast<uintptr_t *>(rangeArray)[rangeIndex];
if (spellRange == 0) {
lua_pushnil(luaState);
return 1;
}
float minRange = *reinterpret_cast<float *>(spellRange + 0x04);
float maxRange = *reinterpret_cast<float *>(spellRange + 0x08);
uint32_t flags = *reinterpret_cast<uint32_t *>(spellRange + 0x0c);
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
const char *name = *reinterpret_cast<const char **>(spellRange + 0x10 + language * 4);
if (!name || name[0] == '\0') {
// fall back to enUS (index 0)
name = *reinterpret_cast<const char **>(spellRange + 0x10);
}
lua_pushnumber(luaState, minRange);
lua_pushnumber(luaState, maxRange);
lua_pushnumber(luaState, flags);
lua_pushstring(luaState, name ? const_cast<char *>(name) : const_cast<char *>(""));
return 4;
}
}
+2
View File
@@ -38,5 +38,7 @@ namespace Nampower {
uint32_t Script_GetSpellDuration(uintptr_t *luaState);
uint32_t Script_GetSpellRangeData(uintptr_t *luaState);
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState, uint32_t *slot, uint32_t *type);
}
+1
View File
@@ -206,6 +206,7 @@ namespace Nampower {
if (!guid || *guid == 0) return;
auto names = GetUnitNames(guid);
TriggerUnitEvents(names, eventCode);
}