add new convenience functions for inventory and queue bugfix

This commit is contained in:
avitasia
2025-12-15 12:05:12 -08:00
parent 12c6c5b90b
commit b381cd5511
13 changed files with 1613 additions and 424 deletions
+225
View File
@@ -151,6 +151,231 @@ local delay = GetItemStatsField(19019, "delay")
print("Weapon speed: " .. (delay / 1000) .. " seconds") -- 1.9 seconds
```
#### FindPlayerItemSlot(itemId or itemName)
Searches the player's inventory for an item by ID or name and returns its location.
**Parameters:**
- `itemId` (number): The item ID to search for, OR
- `itemName` (string): The item name to search for (case-insensitive)
**Returns:**
- 1st param (number or nil): Bag index where the item was found
- `nil` = Equipped item (check 2nd param for equipment slot 0-18)
- `0` = Inventory pack
- `1-4` = Regular bags
- `-1` = Bank item slots
- `5-9` = Bank bags
- `-2` = Keyring
- 2nd param (number): Slot number within the bag (or equipment slot if 1st param is nil)
- For equipped items: 0-18 (equipment slots are 0-indexed)
- For bag 0, -1, -2: Returns **relative slot position** (1-indexed, 0-based within bag + 1)
- Bag 0: slots 1-16 (corresponding to absolute slots 23-38)
- Bag -1: slots 1-24 (corresponding to absolute bank slots 39-62)
- Bag -2: slots 1-16 (corresponding to absolute keyring slots 81-96)
- For regular bags (1-4) and bank bags (5-9): Returns 1-indexed slot within the bag
- Returns `nil,nil` if the item is not found
**Examples:**
```lua
-- Find Thunderfury in player inventory
local bag, slot = FindPlayerItemSlot(19019)
if bag then
print("Found in bag " .. bag .. " slot " .. slot)
if bag == -1 or (bag >= 5 and bag <= 9) then
print("Item is in bank")
end
elseif bag == nil and slot then
print("Item is equipped in slot " .. slot)
else
print("Item not found")
end
-- Find item by name (uses cache for performance after first lookup)
local bag, slot = FindPlayerItemSlot("Hearthstone")
if slot then
if bag == nil then
print("Hearthstone is equipped in slot " .. slot)
elseif bag == 0 then
print("Hearthstone is in inventory pack slot " .. slot .. " (1-16)")
elseif bag == -1 then
print("Hearthstone is in bank slot " .. slot .. " (1-24)")
elseif bag == -2 then
print("Hearthstone is in keyring slot " .. slot .. " (1-16)")
else
print("Hearthstone is in bag " .. bag .. " slot " .. slot)
end
end
```
#### GetEquippedItems(unitToken)
Returns a table containing all equipped items for the specified unit.
**Parameters:**
- `unitToken` (string): Can be a standard unit token ("player", "target", "pet", etc.) or a GUID string
**Returns:**
- A Lua table with equipment slot indices as keys (0-18) and item info tables as values
- Returns nil if the unit cannot be found or inspected
For the player, item info includes:
- `itemId`: The item's ID
- `stackCount`: Number of items in the stack
- `duration`: Item duration in milliseconds
- `spellCharges`: Table of spell charges (indices 1-5)
- `flags`: Item flags
- `permanentEnchantId`: Permanent enchantment ID
- `tempEnchantId`: Temporary enchantment ID
- `tempEnchantmentTimeLeftMs`: Time remaining on temp enchant in milliseconds
- `tempEnchantmentCharges`: Charges remaining on temp enchant
- `durability`: Current durability
- `maxDurability`: Maximum durability
For other inspected units (limited data):
- `itemId`: The item's ID
- `permanentEnchantId`: Permanent enchantment ID
- `tempEnchantId`: Temporary enchantment ID
**Examples:**
```lua
-- Get all equipped items for your target
local items = GetEquippedItems("target")
if items then
for slot, itemInfo in pairs(items) do
print("Slot " .. slot .. ": Item ID " .. itemInfo.itemId)
if itemInfo.permanentEnchantId and itemInfo.permanentEnchantId > 0 then
print(" Permanent enchant: " .. itemInfo.permanentEnchantId)
end
end
end
-- Check player's weapon durability
local items = GetEquippedItems("player")
if items and items[15] then -- slot 15 is main hand
local weapon = items[15]
print("Weapon durability: " .. weapon.durability .. "/" .. weapon.maxDurability)
end
```
#### GetEquippedItem(unitToken, slot)
Returns item info for a specific equipment slot on the specified unit.
**Parameters:**
- `unitToken` (string): Can be a standard unit token ("player", "target", "pet", etc.) or a GUID string
- `slot` (number): Equipment slot number (0-18)
- 1 = Head, 2 = Neck, 3 = Shoulder, 4 = Shirt, 5 = Chest
- 6 = Waist, 7 = Legs, 8 = Feet, 9 = Wrist, 10 = Hands
- 11 = Finger 1, 12 = Finger 2, 13 = Trinket 1, 14 = Trinket 2
- 15 = Back, 16 = Main Hand, 17 = Off Hand, 18 = Ranged, 19 = Tabard
**Returns:**
- A Lua table containing the item info (same fields as GetEquippedItems)
- Returns nil if the slot is empty, unit cannot be found, or unit cannot be inspected
**Examples:**
```lua
-- Check target's main hand weapon
local weapon = GetEquippedItem("target", 16)
if weapon then
print("Target has weapon: " .. weapon.itemId)
else
print("Target has no main hand weapon")
end
-- Check your own helmet
local helm = GetEquippedItem("player", 1)
if helm and helm.durability then
local durabilityPercent = (helm.durability / helm.maxDurability) * 100
print("Helmet durability: " .. string.format("%.1f%%", durabilityPercent))
end
```
#### GetBagItems()
Returns a nested table containing all items in all bags (including bank if open).
**Returns:**
- A Lua table with bag indices as keys and bag contents as values
- Each bag contains **1-indexed** slot numbers as keys and item info tables as values
- Bag indices:
- 0 = Inventory pack (16 slots)
- 1-4 = Regular bags
- -1 = Bank item slots (24 slots, only if bank is open)
- 5-9 = Bank bags (only if bank is open)
- -2 = Keyring
Item info table fields (same as GetEquippedItems for player):
- `itemId`, `stackCount`, `duration`, `spellCharges`, `flags`
- `permanentEnchantId`, `tempEnchantId`, `tempEnchantmentTimeLeftMs`, `tempEnchantmentCharges`
- `durability`, `maxDurability`
**Examples:**
```lua
-- Get all items in all bags
local allItems = GetBagItems()
for bagIndex, bagContents in pairs(allItems) do
print("Bag " .. bagIndex .. ":")
for slot, itemInfo in pairs(bagContents) do
print(" Slot " .. slot .. ": " .. itemInfo.itemId .. " (x" .. itemInfo.stackCount .. ")")
end
end
-- Count total number of a specific item
local function CountItem(itemId)
local total = 0
local allItems = GetBagItems()
for bagIndex, bagContents in pairs(allItems) do
for slot, itemInfo in pairs(bagContents) do
if itemInfo.itemId == itemId then
total = total + itemInfo.stackCount
end
end
end
return total
end
local soulShardCount = CountItem(6265)
print("Soul Shards: " .. soulShardCount)
```
#### GetBagItem(bagIndex, slot)
Returns item info for a specific slot in a specific bag.
**Parameters:**
- `bagIndex` (number): The bag to check
- 0 = Inventory pack
- 1-4 = Regular bags
- -1 = Bank item slots or buyback slots
- 5-9 = Bank bags (requires bank to be open)
- -2 = Keyring
- `slot` (number): **1-indexed** slot number within the bag
**Returns:**
- A Lua table containing the item info (same fields as GetBagItems)
- Returns nil if the slot is empty or invalid
**Examples:**
```lua
-- Get item in first slot of first bag
local item = GetBagItem(1, 1)
if item then
print("Item ID: " .. item.itemId)
print("Stack count: " .. item.stackCount)
else
print("Slot is empty")
end
-- Check durability of an item in inventory pack
local item = GetBagItem(0, 1)
if item and item.durability then
print("Durability: " .. item.durability .. "/" .. item.maxDurability)
end
-- Check if a specific bank slot has an item (bank must be open)
local bankItem = GetBagItem(-1, 1)
if bankItem then
print("Bank slot 1 contains: " .. bankItem.itemId)
end
```
#### GetSpellRec(spellId)
Returns a Lua table containing all fields for the spell's `SpellRec` record (including localized `name` and `rank`). Returns nil if the spell cannot be found.
+10 -2
View File
@@ -103,9 +103,17 @@ namespace game {
}
uint32_t GetItemId(CGItem_C *item) {
uintptr_t *itemInfo = item->m_itemInfo;
return item->object.m_obj->m_entryID;
}
return *reinterpret_cast<uint32_t *>(itemInfo + 3); // item id offset
uintptr_t *GetObjectVFTable(uintptr_t *unit) {
// Dereference once to get vtable pointer
return *reinterpret_cast<uintptr_t **>(unit);
}
uintptr_t *GetPlayerInventoryPtr(uintptr_t *playerUnit) {
// Access inventory at byte offset 0x1d38
return playerUnit + 0x74E;
}
const char *GetSpellName(uint32_t spellId) {
+526 -358
View File
@@ -186,13 +186,141 @@ namespace game {
MAX_ITEM_FLAG = 32768
};
typedef enum OBJECT_TYPE_MASK {
TYPE_OBJECT = 1,
TYPE_ITEM = 2,
TYPE_CONTAINER = 4,
TYPE_UNIT = 8,
TYPE_PLAYER = 16,
TYPE_GAMEOBJECT = 32,
TYPE_DYNAMICOBJECT = 64,
TYPE_CORPSE = 128,
TYPE_AIGROUP = 256,
TYPE_AREATRIGGER = 512
} OBJECT_TYPE_MASK;
struct CGObjectData {
uint64_t m_guid;
OBJECT_TYPE_MASK m_type;
int m_entryID;
float m_scale;
uint32_t pad;
};
struct ObjectFields {
uint64_t guid;
uint32_t type;
uint32_t entry;
uint32_t scaleX;
uint32_t padding;
};
struct ItemEnchantment {
int id;
int duration;
int charges;
};
struct CGObject {
void *vftable;
uintptr_t *m_data;
CGObjectData *m_obj;
};
struct ItemFields {
uint64_t owner;
uint64_t contained;
uint64_t creator;
uint64_t giftCreator;
uint32_t stackCount;
uint32_t duration;
uint32_t spellCharges[5];
uint32_t flags;
ItemEnchantment permEnchantmentSlot;
ItemEnchantment tempEnchantmentSlot;
ItemEnchantment maxInspectedEnchantmentSlot;
ItemEnchantment propEnchantmentSlot0;
ItemEnchantment propEnchantmentSlot1;
ItemEnchantment propEnchantmentSlot2;
ItemEnchantment propEnchantmentSlot3;
uint32_t propertySeed;
uint32_t randomPropertiesId;
uint32_t itemTextId;
uint32_t durability;
uint32_t maxDurability;
};
struct CGItem_C {
uint32_t m_unk;
uint32_t m_flags;
uintptr_t *m_itemInfo;
uint32_t m_expirationTime;
uint32_t m_enchantmentExpiration[5];
uintptr_t *m_soundsRec;
CGObject object;
int32_t field1;
int32_t field2;
int32_t field3;
int32_t field4;
int32_t field5;
int32_t field6;
int32_t field7;
int32_t field8;
int32_t field9;
int32_t field10;
int32_t field11;
int32_t field12;
int32_t field13;
int32_t field14;
int32_t field15;
int32_t field16;
int32_t field17;
int32_t field18;
int32_t field19;
int32_t field20;
int32_t field21;
int32_t field22;
int32_t field23;
int32_t field24;
int32_t field25;
int32_t field26;
int32_t field27;
int32_t field28;
int32_t field29;
int32_t field30;
int32_t field31;
int32_t field32;
int32_t field33;
int32_t field34;
int32_t field35;
int32_t field36;
int32_t field37;
int32_t field38;
int32_t field39;
int32_t field40;
int32_t field41;
int32_t field42;
int32_t field43;
int32_t field44;
int32_t field45;
int32_t field46;
int32_t field47;
int32_t field48;
int32_t field49;
int32_t field50;
int32_t field51;
int32_t field52;
int32_t field53;
int32_t field54;
int32_t field55;
int32_t field56;
int32_t field57;
int32_t field58;
int32_t field59;
int32_t field60;
int32_t field61;
int32_t field62;
int32_t field63;
int32_t field64;
int32_t field65;
int32_t field66;
ItemFields *itemFields;
};
struct __declspec(align(4)) ItemStats_C {
@@ -254,6 +382,15 @@ namespace game {
};
#pragma pack(pop)
struct CGItem {
uintptr_t *unkPtr;
uint32_t unk;
uint32_t itemId;
uint32_t permanentEnchantId;
uint32_t tempEnchantId;
};
enum SpellEffects {
SPELL_EFFECT_NONE = 0,
SPELL_EFFECT_INSTAKILL = 1,
@@ -396,121 +533,121 @@ namespace game {
enum SpellCastResult : std::uint8_t {
SPELL_FAILED_AFFECTING_COMBAT = 0, // 0x0
SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 1, // 0x1
SPELL_FAILED_ALREADY_AT_FULL_MANA = 2, // 0x2
SPELL_FAILED_ALREADY_BEING_TAMED = 3, // 0x3
SPELL_FAILED_ALREADY_HAVE_CHARM = 4, // 0x4
SPELL_FAILED_ALREADY_HAVE_SUMMON = 5, // 0x5
SPELL_FAILED_ALREADY_OPEN = 6, // 0x6
SPELL_FAILED_AFFECTING_COMBAT = 0, // 0x0
SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 1, // 0x1
SPELL_FAILED_ALREADY_AT_FULL_MANA = 2, // 0x2
SPELL_FAILED_ALREADY_BEING_TAMED = 3, // 0x3
SPELL_FAILED_ALREADY_HAVE_CHARM = 4, // 0x4
SPELL_FAILED_ALREADY_HAVE_SUMMON = 5, // 0x5
SPELL_FAILED_ALREADY_OPEN = 6, // 0x6
SPELL_FAILED_MORE_POWERFUL_SPELL_ACTIVE = 7, // 0x7
SPELL_FAILED_BAD_IMPLICIT_TARGETS = 9, // 0x9
SPELL_FAILED_BAD_TARGETS = 10, // 0xA
SPELL_FAILED_CANT_BE_CHARMED = 11, // 0xB
SPELL_FAILED_CANT_BE_DISENCHANTED = 12, // 0xC
SPELL_FAILED_CANT_BE_PROSPECTED = 13, // 0xD
SPELL_FAILED_CANT_CAST_ON_TAPPED = 14, // 0xE
SPELL_FAILED_BAD_IMPLICIT_TARGETS = 9, // 0x9
SPELL_FAILED_BAD_TARGETS = 10, // 0xA
SPELL_FAILED_CANT_BE_CHARMED = 11, // 0xB
SPELL_FAILED_CANT_BE_DISENCHANTED = 12, // 0xC
SPELL_FAILED_CANT_BE_PROSPECTED = 13, // 0xD
SPELL_FAILED_CANT_CAST_ON_TAPPED = 14, // 0xE
SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 15, // 0xF
SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 16, // 0x10
SPELL_FAILED_CANT_TOO_CLOSE_TO_ENEMY = 17, // 0x11
SPELL_FAILED_CANT_DO_THAT_YET = 18, // 0x12
SPELL_FAILED_CASTER_DEAD = 19, // 0x13
SPELL_FAILED_CHARMED = 20, // 0x14
SPELL_FAILED_CHEST_IN_USE = 21, // 0x15
SPELL_FAILED_CONFUSED = 22, // 0x16
SPELL_FAILED_DONT_REPORT = 23, // 0x17
SPELL_FAILED_EQUIPPED_ITEM = 24, // 0x18
SPELL_FAILED_EQUIPPED_ITEM_CLASS = 25, // 0x19
SPELL_FAILED_CANT_TOO_CLOSE_TO_ENEMY = 17, // 0x11
SPELL_FAILED_CANT_DO_THAT_YET = 18, // 0x12
SPELL_FAILED_CASTER_DEAD = 19, // 0x13
SPELL_FAILED_CHARMED = 20, // 0x14
SPELL_FAILED_CHEST_IN_USE = 21, // 0x15
SPELL_FAILED_CONFUSED = 22, // 0x16
SPELL_FAILED_DONT_REPORT = 23, // 0x17
SPELL_FAILED_EQUIPPED_ITEM = 24, // 0x18
SPELL_FAILED_EQUIPPED_ITEM_CLASS = 25, // 0x19
SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 26, // 0x1A
SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 27, // 0x1B
SPELL_FAILED_ERROR = 28, // 0x1C
SPELL_FAILED_FIZZLE = 29, // 0x1D
SPELL_FAILED_FLEEING = 30, // 0x1E
SPELL_FAILED_FOOD_LOWLEVEL = 31, // 0x1F
SPELL_FAILED_HIGHLEVEL = 32, // 0x20
SPELL_FAILED_IMMUNE = 34, // 0x22
SPELL_FAILED_INTERRUPTED = 35, // 0x23
SPELL_FAILED_INTERRUPTED_COMBAT = 36, // 0x24
SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 37, // 0x25
SPELL_FAILED_ITEM_GONE = 38, // 0x26
SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 27, // 0x1B
SPELL_FAILED_ERROR = 28, // 0x1C
SPELL_FAILED_FIZZLE = 29, // 0x1D
SPELL_FAILED_FLEEING = 30, // 0x1E
SPELL_FAILED_FOOD_LOWLEVEL = 31, // 0x1F
SPELL_FAILED_HIGHLEVEL = 32, // 0x20
SPELL_FAILED_IMMUNE = 34, // 0x22
SPELL_FAILED_INTERRUPTED = 35, // 0x23
SPELL_FAILED_INTERRUPTED_COMBAT = 36, // 0x24
SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 37, // 0x25
SPELL_FAILED_ITEM_GONE = 38, // 0x26
SPELL_FAILED_ENCHANT_NOT_EXISTING_ITEM = 39, // 0x27
SPELL_FAILED_ITEM_NOT_READY = 40, // 0x28
SPELL_FAILED_LEVEL_REQUIREMENT = 41, // 0x29
SPELL_FAILED_LINE_OF_SIGHT = 42, // 0x2A
SPELL_FAILED_LOWLEVEL = 43, // 0x2B
SPELL_FAILED_SKILL_NOT_HIGH_ENOUGH = 44, // 0x2C
SPELL_FAILED_MAINHAND_EMPTY = 45, // 0x2D
SPELL_FAILED_MOVING = 46, // 0x2E
SPELL_FAILED_NEED_AMMO = 47, // 0x2F
SPELL_FAILED_NEED_REQUIRES_SOMETHING = 48, // 0x30
SPELL_FAILED_NEED_EXOTIC_AMMO = 49, // 0x31
SPELL_FAILED_NOPATH = 50, // 0x32
SPELL_FAILED_NOT_BEHIND = 51, // 0x33
SPELL_FAILED_NOT_FISHABLE = 52, // 0x34
SPELL_FAILED_NOT_HERE = 53, // 0x35
SPELL_FAILED_NOT_INFRONT = 54, // 0x36
SPELL_FAILED_NOT_IN_CONTROL = 55, // 0x37
SPELL_FAILED_NOT_KNOWN = 56, // 0x38
SPELL_FAILED_NOT_MOUNTED = 57, // 0x39
SPELL_FAILED_NOT_ON_TAXI = 58, // 0x3A
SPELL_FAILED_NOT_ON_TRANSPORT = 59, // 0x3B
SPELL_FAILED_NOT_READY = 60, // 0x3C
SPELL_FAILED_NOT_SHAPESHIFT = 61, // 0x3D
SPELL_FAILED_NOT_STANDING = 62, // 0x3E
SPELL_FAILED_NOT_TRADEABLE = 63, // 0x3F
SPELL_FAILED_NOT_TRADING = 64, // 0x40
SPELL_FAILED_NOT_UNSHEATHED = 65, // 0x41
SPELL_FAILED_NOT_WHILE_GHOST = 66, // 0x42
SPELL_FAILED_NO_AMMO = 67, // 0x43
SPELL_FAILED_NO_CHARGES_REMAIN = 68, // 0x44
SPELL_FAILED_NO_CHAMPION = 69, // 0x45
SPELL_FAILED_NO_COMBO_POINTS = 70, // 0x46
SPELL_FAILED_NO_DUELING = 71, // 0x47
SPELL_FAILED_NO_ENDURANCE = 72, // 0x48
SPELL_FAILED_NO_FISH = 73, // 0x49
SPELL_FAILED_ITEM_NOT_READY = 40, // 0x28
SPELL_FAILED_LEVEL_REQUIREMENT = 41, // 0x29
SPELL_FAILED_LINE_OF_SIGHT = 42, // 0x2A
SPELL_FAILED_LOWLEVEL = 43, // 0x2B
SPELL_FAILED_SKILL_NOT_HIGH_ENOUGH = 44, // 0x2C
SPELL_FAILED_MAINHAND_EMPTY = 45, // 0x2D
SPELL_FAILED_MOVING = 46, // 0x2E
SPELL_FAILED_NEED_AMMO = 47, // 0x2F
SPELL_FAILED_NEED_REQUIRES_SOMETHING = 48, // 0x30
SPELL_FAILED_NEED_EXOTIC_AMMO = 49, // 0x31
SPELL_FAILED_NOPATH = 50, // 0x32
SPELL_FAILED_NOT_BEHIND = 51, // 0x33
SPELL_FAILED_NOT_FISHABLE = 52, // 0x34
SPELL_FAILED_NOT_HERE = 53, // 0x35
SPELL_FAILED_NOT_INFRONT = 54, // 0x36
SPELL_FAILED_NOT_IN_CONTROL = 55, // 0x37
SPELL_FAILED_NOT_KNOWN = 56, // 0x38
SPELL_FAILED_NOT_MOUNTED = 57, // 0x39
SPELL_FAILED_NOT_ON_TAXI = 58, // 0x3A
SPELL_FAILED_NOT_ON_TRANSPORT = 59, // 0x3B
SPELL_FAILED_NOT_READY = 60, // 0x3C
SPELL_FAILED_NOT_SHAPESHIFT = 61, // 0x3D
SPELL_FAILED_NOT_STANDING = 62, // 0x3E
SPELL_FAILED_NOT_TRADEABLE = 63, // 0x3F
SPELL_FAILED_NOT_TRADING = 64, // 0x40
SPELL_FAILED_NOT_UNSHEATHED = 65, // 0x41
SPELL_FAILED_NOT_WHILE_GHOST = 66, // 0x42
SPELL_FAILED_NO_AMMO = 67, // 0x43
SPELL_FAILED_NO_CHARGES_REMAIN = 68, // 0x44
SPELL_FAILED_NO_CHAMPION = 69, // 0x45
SPELL_FAILED_NO_COMBO_POINTS = 70, // 0x46
SPELL_FAILED_NO_DUELING = 71, // 0x47
SPELL_FAILED_NO_ENDURANCE = 72, // 0x48
SPELL_FAILED_NO_FISH = 73, // 0x49
SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 74, // 0x4A
SPELL_FAILED_NO_MOUNTS_ALLOWED = 75, // 0x4B
SPELL_FAILED_NO_PET = 76, // 0x4C
SPELL_FAILED_NO_POWER = 77, // 0x4D
SPELL_FAILED_NOTHING_TO_DISPEL = 78, // 0x4E
SPELL_FAILED_NOTHING_TO_STEAL = 79, // 0x4F
SPELL_FAILED_ONLY_ABOVEWATER = 80, // 0x50
SPELL_FAILED_ONLY_DAYTIME = 81, // 0x51
SPELL_FAILED_ONLY_INDOORS = 82, // 0x52
SPELL_FAILED_ONLY_MOUNTED = 83, // 0x53
SPELL_FAILED_ONLY_NIGHTTIME = 84, // 0x54
SPELL_FAILED_ONLY_OUTDOORS = 85, // 0x55
SPELL_FAILED_ONLY_SHAPESHIFT = 86, // 0x56
SPELL_FAILED_ONLY_STEALTHED = 87, // 0x57
SPELL_FAILED_ONLY_UNDERWATER = 88, // 0x58
SPELL_FAILED_OUT_OF_RANGE = 89, // 0x59
SPELL_FAILED_PACIFIED = 90, // 0x5A
SPELL_FAILED_POSSESSED = 91, // 0x5B
SPELL_FAILED_REQUIRES_AREA = 93, // 0x5D
SPELL_FAILED_REQUIRES_SPELL_FOCUS = 94, // 0x5E
SPELL_FAILED_ROOTED = 95, // 0x5F
SPELL_FAILED_SILENCED = 96, // 0x60
SPELL_FAILED_SPELL_IN_PROGRESS = 97, // 0x61
SPELL_FAILED_SPELL_LEARNED = 98, // 0x62
SPELL_FAILED_SPELL_UNAVAILABLE = 99, // 0x63
SPELL_FAILED_STUNNED = 100, // 0x64
SPELL_FAILED_TARGETS_DEAD = 101, // 0x65
SPELL_FAILED_TARGET_AFFECTING_COMBAT = 102, // 0x66
SPELL_FAILED_TARGET_AURASTATE = 103, // 0x67
SPELL_FAILED_TARGET_DUELING = 104, // 0x68
SPELL_FAILED_TARGET_ENEMY = 105, // 0x69
SPELL_FAILED_TARGET_ENRAGED = 106, // 0x6A
SPELL_FAILED_TARGET_FRIENDLY = 107, // 0x6B
SPELL_FAILED_TARGET_IN_COMBAT = 108, // 0x6C
SPELL_FAILED_TARGET_IS_PLAYER = 109, // 0x6D
SPELL_FAILED_TARGET_NOT_DEAD = 110, // 0x6E
SPELL_FAILED_TARGET_NOT_IN_PARTY = 111, // 0x6F
SPELL_FAILED_TARGET_NOT_LOOTED = 112, // 0x70
SPELL_FAILED_TARGET_NOT_PLAYER = 113, // 0x71
SPELL_FAILED_TARGET_NO_POCKETS = 114, // 0x72
SPELL_FAILED_TARGET_NO_WEAPONS = 115, // 0x73
SPELL_FAILED_TARGET_UNSKINNABLE = 116, // 0x74
SPELL_FAILED_THIRST_SATIATED = 117, // 0x75
SPELL_FAILED_NO_MOUNTS_ALLOWED = 75, // 0x4B
SPELL_FAILED_NO_PET = 76, // 0x4C
SPELL_FAILED_NO_POWER = 77, // 0x4D
SPELL_FAILED_NOTHING_TO_DISPEL = 78, // 0x4E
SPELL_FAILED_NOTHING_TO_STEAL = 79, // 0x4F
SPELL_FAILED_ONLY_ABOVEWATER = 80, // 0x50
SPELL_FAILED_ONLY_DAYTIME = 81, // 0x51
SPELL_FAILED_ONLY_INDOORS = 82, // 0x52
SPELL_FAILED_ONLY_MOUNTED = 83, // 0x53
SPELL_FAILED_ONLY_NIGHTTIME = 84, // 0x54
SPELL_FAILED_ONLY_OUTDOORS = 85, // 0x55
SPELL_FAILED_ONLY_SHAPESHIFT = 86, // 0x56
SPELL_FAILED_ONLY_STEALTHED = 87, // 0x57
SPELL_FAILED_ONLY_UNDERWATER = 88, // 0x58
SPELL_FAILED_OUT_OF_RANGE = 89, // 0x59
SPELL_FAILED_PACIFIED = 90, // 0x5A
SPELL_FAILED_POSSESSED = 91, // 0x5B
SPELL_FAILED_REQUIRES_AREA = 93, // 0x5D
SPELL_FAILED_REQUIRES_SPELL_FOCUS = 94, // 0x5E
SPELL_FAILED_ROOTED = 95, // 0x5F
SPELL_FAILED_SILENCED = 96, // 0x60
SPELL_FAILED_SPELL_IN_PROGRESS = 97, // 0x61
SPELL_FAILED_SPELL_LEARNED = 98, // 0x62
SPELL_FAILED_SPELL_UNAVAILABLE = 99, // 0x63
SPELL_FAILED_STUNNED = 100, // 0x64
SPELL_FAILED_TARGETS_DEAD = 101, // 0x65
SPELL_FAILED_TARGET_AFFECTING_COMBAT = 102, // 0x66
SPELL_FAILED_TARGET_AURASTATE = 103, // 0x67
SPELL_FAILED_TARGET_DUELING = 104, // 0x68
SPELL_FAILED_TARGET_ENEMY = 105, // 0x69
SPELL_FAILED_TARGET_ENRAGED = 106, // 0x6A
SPELL_FAILED_TARGET_FRIENDLY = 107, // 0x6B
SPELL_FAILED_TARGET_IN_COMBAT = 108, // 0x6C
SPELL_FAILED_TARGET_IS_PLAYER = 109, // 0x6D
SPELL_FAILED_TARGET_NOT_DEAD = 110, // 0x6E
SPELL_FAILED_TARGET_NOT_IN_PARTY = 111, // 0x6F
SPELL_FAILED_TARGET_NOT_LOOTED = 112, // 0x70
SPELL_FAILED_TARGET_NOT_PLAYER = 113, // 0x71
SPELL_FAILED_TARGET_NO_POCKETS = 114, // 0x72
SPELL_FAILED_TARGET_NO_WEAPONS = 115, // 0x73
SPELL_FAILED_TARGET_UNSKINNABLE = 116, // 0x74
SPELL_FAILED_THIRST_SATIATED = 117, // 0x75
SPELL_FAILED_TOO_CLOSE = 118
};
@@ -550,140 +687,161 @@ namespace game {
};
enum SpellAttributesEx {
SPELL_ATTR_EX_DISMISS_PET_FIRST = 0x00000001, // 0 For spells without this flag client doesn't allow to summon pet if caster has a pet
SPELL_ATTR_EX_USE_ALL_MANA = 0x00000002, // 1 Use all power (Only paladin Lay of Hands and Bunyanize)
SPELL_ATTR_EX_IS_CHANNELED = 0x00000004, // 2
SPELL_ATTR_EX_NO_REDIRECTION = 0x00000008, // 3
SPELL_ATTR_EX_NO_SKILL_INCREASE = 0x00000010, // 4 Only assigned to stealth spells for some reason
SPELL_ATTR_EX_ALLOW_WHILE_STEALTHED = 0x00000020, // 5 Does not break stealth
SPELL_ATTR_EX_IS_SELF_CHANNELED = 0x00000040, // 6
SPELL_ATTR_EX_NO_REFLECTION = 0x00000080, // 7
SPELL_ATTR_EX_ONLY_PEACEFUL_TARGETS = 0x00000100, // 8 Target must not be in combat
SPELL_ATTR_EX_INITIATES_COMBAT = 0x00000200, // 9 Enables Auto-Attack
SPELL_ATTR_EX_NO_THREAT = 0x00000400, // 10
SPELL_ATTR_EX_AURA_UNIQUE = 0x00000800, // 11
SPELL_ATTR_EX_FAILURE_BREAKS_STEALTH = 0x00001000, // 12
SPELL_ATTR_EX_TOGGLE_FARSIGHT = 0x00002000, // 13
SPELL_ATTR_EX_TRACK_TARGET_IN_CHANNEL = 0x00004000, // 14 Client automatically forces player to face target when channeling
SPELL_ATTR_EX_IMMUNITY_PURGES_EFFECT = 0x00008000, // 15 Remove auras on immunity
SPELL_ATTR_EX_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS = 0x00010000, // 16 Aura that provides immunity prevents positive effects too
SPELL_ATTR_EX_NO_AUTOCAST_AI = 0x00020000, // 17
SPELL_ATTR_EX_PREVENTS_ANIM = 0x00040000, // 18 Stun, polymorph, daze, sleep
SPELL_ATTR_EX_EXCLUDE_CASTER = 0x00080000, // 19
SPELL_ATTR_EX_FINISHING_MOVE_DAMAGE = 0x00100000, // 20 Uses combo points
SPELL_ATTR_EX_THREAT_ONLY_ON_MISS = 0x00200000, // 21
SPELL_ATTR_EX_FINISHING_MOVE_DURATION = 0x00400000, // 22 Uses combo points (in 4.x not required combo point target selected)
SPELL_ATTR_EX_IGNORE_CASTER_AND_TARGET_RESTRICTIONS = 0x00800000, // 23 Skips all cast checks, moved to AttributesEx3 after 1.10 (100% correlation)
SPELL_ATTR_EX_SPECIAL_SKILLUP = 0x01000000, // 24 Only fishing spells
SPELL_ATTR_EX_UNK25 = 0x02000000, // 25 Different in vanilla
SPELL_ATTR_EX_REQUIRE_ALL_TARGETS = 0x04000000, // 26
SPELL_ATTR_EX_DISCOUNT_POWER_ON_MISS = 0x08000000, // 27 All these spells refund power on parry or deflect
SPELL_ATTR_EX_NO_AURA_ICON = 0x10000000, // 28 Client doesn't display these spells in aura bar
SPELL_ATTR_EX_NAME_IN_CHANNEL_BAR = 0x20000000, // 29 Spell name is displayed in cast bar instead of 'channeling' text
SPELL_ATTR_EX_COMBO_ON_BLOCK = 0x40000000, // 30 Overpower
SPELL_ATTR_EX_CAST_WHEN_LEARNED = 0x80000000 // 31
SPELL_ATTR_EX_DISMISS_PET_FIRST = 0x00000001,
// 0 For spells without this flag client doesn't allow to summon pet if caster has a pet
SPELL_ATTR_EX_USE_ALL_MANA = 0x00000002, // 1 Use all power (Only paladin Lay of Hands and Bunyanize)
SPELL_ATTR_EX_IS_CHANNELED = 0x00000004, // 2
SPELL_ATTR_EX_NO_REDIRECTION = 0x00000008, // 3
SPELL_ATTR_EX_NO_SKILL_INCREASE = 0x00000010, // 4 Only assigned to stealth spells for some reason
SPELL_ATTR_EX_ALLOW_WHILE_STEALTHED = 0x00000020, // 5 Does not break stealth
SPELL_ATTR_EX_IS_SELF_CHANNELED = 0x00000040, // 6
SPELL_ATTR_EX_NO_REFLECTION = 0x00000080, // 7
SPELL_ATTR_EX_ONLY_PEACEFUL_TARGETS = 0x00000100, // 8 Target must not be in combat
SPELL_ATTR_EX_INITIATES_COMBAT = 0x00000200, // 9 Enables Auto-Attack
SPELL_ATTR_EX_NO_THREAT = 0x00000400, // 10
SPELL_ATTR_EX_AURA_UNIQUE = 0x00000800, // 11
SPELL_ATTR_EX_FAILURE_BREAKS_STEALTH = 0x00001000, // 12
SPELL_ATTR_EX_TOGGLE_FARSIGHT = 0x00002000, // 13
SPELL_ATTR_EX_TRACK_TARGET_IN_CHANNEL = 0x00004000,
// 14 Client automatically forces player to face target when channeling
SPELL_ATTR_EX_IMMUNITY_PURGES_EFFECT = 0x00008000, // 15 Remove auras on immunity
SPELL_ATTR_EX_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS = 0x00010000,
// 16 Aura that provides immunity prevents positive effects too
SPELL_ATTR_EX_NO_AUTOCAST_AI = 0x00020000, // 17
SPELL_ATTR_EX_PREVENTS_ANIM = 0x00040000, // 18 Stun, polymorph, daze, sleep
SPELL_ATTR_EX_EXCLUDE_CASTER = 0x00080000, // 19
SPELL_ATTR_EX_FINISHING_MOVE_DAMAGE = 0x00100000, // 20 Uses combo points
SPELL_ATTR_EX_THREAT_ONLY_ON_MISS = 0x00200000, // 21
SPELL_ATTR_EX_FINISHING_MOVE_DURATION = 0x00400000,
// 22 Uses combo points (in 4.x not required combo point target selected)
SPELL_ATTR_EX_IGNORE_CASTER_AND_TARGET_RESTRICTIONS = 0x00800000,
// 23 Skips all cast checks, moved to AttributesEx3 after 1.10 (100% correlation)
SPELL_ATTR_EX_SPECIAL_SKILLUP = 0x01000000, // 24 Only fishing spells
SPELL_ATTR_EX_UNK25 = 0x02000000, // 25 Different in vanilla
SPELL_ATTR_EX_REQUIRE_ALL_TARGETS = 0x04000000, // 26
SPELL_ATTR_EX_DISCOUNT_POWER_ON_MISS = 0x08000000, // 27 All these spells refund power on parry or deflect
SPELL_ATTR_EX_NO_AURA_ICON = 0x10000000, // 28 Client doesn't display these spells in aura bar
SPELL_ATTR_EX_NAME_IN_CHANNEL_BAR = 0x20000000,
// 29 Spell name is displayed in cast bar instead of 'channeling' text
SPELL_ATTR_EX_COMBO_ON_BLOCK = 0x40000000, // 30 Overpower
SPELL_ATTR_EX_CAST_WHEN_LEARNED = 0x80000000 // 31
};
enum SpellAttributesEx2 {
SPELL_ATTR_EX2_ALLOW_DEAD_TARGET = 0x00000001, // 0 Can target dead unit or corpse
SPELL_ATTR_EX2_NO_SHAPESHIFT_UI = 0x00000002, // 1
SPELL_ATTR_EX2_IGNORE_LINE_OF_SIGHT = 0x00000004, // 2
SPELL_ATTR_EX2_ALLOW_LOW_LEVEL_BUFF = 0x00000008, // 3
SPELL_ATTR_EX2_USE_SHAPESHIFT_BAR = 0x00000010, // 4 Client displays icon in stance bar when learned, even if not shapeshift
SPELL_ATTR_EX2_AUTO_REPEAT = 0x00000020, // 5
SPELL_ATTR_EX2_CANNOT_CAST_ON_TAPPED = 0x00000040, // 6 Target must be tapped by caster
SPELL_ATTR_EX2_DO_NOT_REPORT_SPELL_FAILURE = 0x00000080, // 7
SPELL_ATTR_EX2_UNK8 = 0x00000100, // 8 Unused
SPELL_ATTR_EX2_UNK9 = 0x00000200, // 9 Unused
SPELL_ATTR_EX2_SPECIAL_TAMING_FLAG = 0x00000400, // 10
SPELL_ATTR_EX2_NO_TARGET_PER_SECOND_COSTS = 0x00000800, // 11
SPELL_ATTR_EX2_CHAIN_FROM_CASTER = 0x00001000, // 12
SPELL_ATTR_EX2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // 13
SPELL_ATTR_EX2_ALLOW_WHILE_INVISIBLE = 0x00004000, // 14
SPELL_ATTR_EX2_ENABLE_AFTER_PARRY = 0x00008000, // 15 Deprecated in patch 1.8 and moved to CasterAuraState
SPELL_ATTR_EX2_NO_ACTIVE_PETS = 0x00010000, // 16
SPELL_ATTR_EX2_DO_NOT_RESET_COMBAT_TIMERS = 0x00020000, // 17 Don't reset timers for melee autoattacks (swings) or ranged autoattacks (autoshoots)
SPELL_ATTR_EX2_REQ_DEAD_PET = 0x00040000, // 18 Only Revive pet has it
SPELL_ATTR_EX2_ALLOW_WHILE_NOT_SHAPESHIFTED = 0x00080000, // 19 Does not necessary need shapeshift (pre-3.x not have passive spells with this attribute)
SPELL_ATTR_EX2_INITIATE_COMBAT_POST_CAST = 0x00100000, // 20 Client will send CMSG_ATTACK_SWING after SMSG_SPELL_GO
SPELL_ATTR_EX2_FAIL_ON_ALL_TARGETS_IMMUNE = 0x00200000, // 21 For ice blocks, pala immunity buffs, priest absorb shields
SPELL_ATTR_EX2_NO_INITIAL_THREAT = 0x00400000, // 22
SPELL_ATTR_EX2_PROC_COOLDOWN_ON_FAILURE = 0x00800000, // 23
SPELL_ATTR_EX2_ITEM_CAST_WITH_OWNER_SKILL = 0x01000000, // 24 NYI
SPELL_ATTR_EX2_DONT_BLOCK_MANA_REGEN = 0x02000000, // 25
SPELL_ATTR_EX2_NO_SCHOOL_IMMUNITIES = 0x04000000, // 26
SPELL_ATTR_EX2_IGNORE_WEAPONSKILL = 0x08000000, // 27 NYI (only fishing has it)
SPELL_ATTR_EX2_NOT_AN_ACTION = 0x10000000, // 28
SPELL_ATTR_EX2_CANT_CRIT = 0x20000000, // 29
SPELL_ATTR_EX2_ACTIVE_THREAT = 0x40000000, // 30 Caster is put in combat for 5.5 seconds on cast at enemy unit
SPELL_ATTR_EX2_RETAIN_ITEM_CAST = 0x80000000 // 31 Food or Drink Buff (like Well Fed)
SPELL_ATTR_EX2_ALLOW_DEAD_TARGET = 0x00000001, // 0 Can target dead unit or corpse
SPELL_ATTR_EX2_NO_SHAPESHIFT_UI = 0x00000002, // 1
SPELL_ATTR_EX2_IGNORE_LINE_OF_SIGHT = 0x00000004, // 2
SPELL_ATTR_EX2_ALLOW_LOW_LEVEL_BUFF = 0x00000008, // 3
SPELL_ATTR_EX2_USE_SHAPESHIFT_BAR = 0x00000010,
// 4 Client displays icon in stance bar when learned, even if not shapeshift
SPELL_ATTR_EX2_AUTO_REPEAT = 0x00000020, // 5
SPELL_ATTR_EX2_CANNOT_CAST_ON_TAPPED = 0x00000040, // 6 Target must be tapped by caster
SPELL_ATTR_EX2_DO_NOT_REPORT_SPELL_FAILURE = 0x00000080, // 7
SPELL_ATTR_EX2_UNK8 = 0x00000100, // 8 Unused
SPELL_ATTR_EX2_UNK9 = 0x00000200, // 9 Unused
SPELL_ATTR_EX2_SPECIAL_TAMING_FLAG = 0x00000400, // 10
SPELL_ATTR_EX2_NO_TARGET_PER_SECOND_COSTS = 0x00000800, // 11
SPELL_ATTR_EX2_CHAIN_FROM_CASTER = 0x00001000, // 12
SPELL_ATTR_EX2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // 13
SPELL_ATTR_EX2_ALLOW_WHILE_INVISIBLE = 0x00004000, // 14
SPELL_ATTR_EX2_ENABLE_AFTER_PARRY = 0x00008000, // 15 Deprecated in patch 1.8 and moved to CasterAuraState
SPELL_ATTR_EX2_NO_ACTIVE_PETS = 0x00010000, // 16
SPELL_ATTR_EX2_DO_NOT_RESET_COMBAT_TIMERS = 0x00020000,
// 17 Don't reset timers for melee autoattacks (swings) or ranged autoattacks (autoshoots)
SPELL_ATTR_EX2_REQ_DEAD_PET = 0x00040000, // 18 Only Revive pet has it
SPELL_ATTR_EX2_ALLOW_WHILE_NOT_SHAPESHIFTED = 0x00080000,
// 19 Does not necessary need shapeshift (pre-3.x not have passive spells with this attribute)
SPELL_ATTR_EX2_INITIATE_COMBAT_POST_CAST = 0x00100000,
// 20 Client will send CMSG_ATTACK_SWING after SMSG_SPELL_GO
SPELL_ATTR_EX2_FAIL_ON_ALL_TARGETS_IMMUNE = 0x00200000,
// 21 For ice blocks, pala immunity buffs, priest absorb shields
SPELL_ATTR_EX2_NO_INITIAL_THREAT = 0x00400000, // 22
SPELL_ATTR_EX2_PROC_COOLDOWN_ON_FAILURE = 0x00800000, // 23
SPELL_ATTR_EX2_ITEM_CAST_WITH_OWNER_SKILL = 0x01000000, // 24 NYI
SPELL_ATTR_EX2_DONT_BLOCK_MANA_REGEN = 0x02000000, // 25
SPELL_ATTR_EX2_NO_SCHOOL_IMMUNITIES = 0x04000000, // 26
SPELL_ATTR_EX2_IGNORE_WEAPONSKILL = 0x08000000, // 27 NYI (only fishing has it)
SPELL_ATTR_EX2_NOT_AN_ACTION = 0x10000000, // 28
SPELL_ATTR_EX2_CANT_CRIT = 0x20000000, // 29
SPELL_ATTR_EX2_ACTIVE_THREAT = 0x40000000, // 30 Caster is put in combat for 5.5 seconds on cast at enemy unit
SPELL_ATTR_EX2_RETAIN_ITEM_CAST = 0x80000000 // 31 Food or Drink Buff (like Well Fed)
};
enum SpellAttributesEx3 {
SPELL_ATTR_EX3_PVP_ENABLING = 0x00000001, // 0 Spell landed counts as hostile action against enemy even if it doesn't trigger combat state, propagates PvP flags
SPELL_ATTR_EX3_NO_PROC_EQUIP_REQUIREMENT = 0x00000002, // 1
SPELL_ATTR_EX3_NO_CASTING_BAR_TEXT = 0x00000004, // 2
SPELL_ATTR_EX3_COMPLETELY_BLOCKED = 0x00000008, // 3 All effects prevented on block
SPELL_ATTR_EX3_NO_RES_TIMER = 0x00000010, // 4 Corpse reclaim delay does not apply to accepting resurrection (only Rebirth has it)
SPELL_ATTR_EX3_NO_DURABILITY_LOSS = 0x00000020, // 5
SPELL_ATTR_EX3_NO_AVOIDANCE = 0x00000040, // 6 Persistent Area Aura not removed on leaving radius
SPELL_ATTR_EX3_DOT_STACKING_RULE = 0x00000080, // 7 Create a separate (de)buff stack for each caster
SPELL_ATTR_EX3_ONLY_ON_PLAYER = 0x00000100, // 8 Can target only players
SPELL_ATTR_EX3_NOT_A_PROC = 0x00000200, // 9 Aura periodic trigger is not evaluated as triggered
SPELL_ATTR_EX3_REQUIRES_MAIN_HAND_WEAPON = 0x00000400, // 10
SPELL_ATTR_EX3_ONLY_BATTLEGROUNDS = 0x00000800, // 11
SPELL_ATTR_EX3_ONLY_ON_GHOSTS = 0x00001000, // 12
SPELL_ATTR_EX3_HIDE_CHANNEL_BAR = 0x00002000, // 13 Client will not display channeling bar
SPELL_ATTR_EX3_HIDE_IN_RAID_FILTER = 0x00004000, // 14 Only "Honorless Target" has this flag
SPELL_ATTR_EX3_NORMAL_RANGED_ATTACK = 0x00008000, // 15 Spells with this attribute are processed as ranged attacks in client
SPELL_ATTR_EX3_SUPPRESS_CASTER_PROCS = 0x00010000, // 16
SPELL_ATTR_EX3_SUPPRESS_TARGET_PROCS = 0x00020000, // 17
SPELL_ATTR_EX3_ALWAYS_HIT = 0x00040000, // 18 Spell should always hit its target
SPELL_ATTR_EX3_INSTANT_TARGET_PROCS = 0x00080000, // 19 Related to spell batching
SPELL_ATTR_EX3_ALLOW_AURA_WHILE_DEAD = 0x00100000, // 20 Death persistent spells
SPELL_ATTR_EX3_ONLY_PROC_OUTDOORS = 0x00200000, // 21
SPELL_ATTR_EX3_CASTING_CANCELS_AUTOREPEAT = 0x00400000, // 22 NYI (only Shoot with Wand has it)
SPELL_ATTR_EX3_NO_DAMAGE_HISTORY = 0x00800000, // 23 NYI
SPELL_ATTR_EX3_REQUIRES_OFFHAND_WEAPON = 0x01000000, // 24
SPELL_ATTR_EX3_TREAT_AS_PERIODIC = 0x02000000, // 25 Does not cause spell pushback
SPELL_ATTR_EX3_CAN_PROC_FROM_PROCS = 0x04000000, // 26 Auras with this attribute can proc off procced spells (periodic triggers etc)
SPELL_ATTR_EX3_ONLY_PROC_ON_CASTER = 0x08000000, // 27
SPELL_ATTR_EX3_IGNORE_CASTER_AND_TARGET_RESTRICTIONS = 0x10000000, // 28 Skips all cast checks, moved from AttributesEx after 1.10 (100% correlation)
SPELL_ATTR_EX3_IGNORE_CASTER_MODIFIERS = 0x20000000, // 29
SPELL_ATTR_EX3_DO_NOT_DISPLAY_RANGE = 0x40000000, // 30
SPELL_ATTR_EX3_NOT_ON_AOE_IMMUNE = 0x80000000 // 31
SPELL_ATTR_EX3_PVP_ENABLING = 0x00000001,
// 0 Spell landed counts as hostile action against enemy even if it doesn't trigger combat state, propagates PvP flags
SPELL_ATTR_EX3_NO_PROC_EQUIP_REQUIREMENT = 0x00000002, // 1
SPELL_ATTR_EX3_NO_CASTING_BAR_TEXT = 0x00000004, // 2
SPELL_ATTR_EX3_COMPLETELY_BLOCKED = 0x00000008, // 3 All effects prevented on block
SPELL_ATTR_EX3_NO_RES_TIMER = 0x00000010,
// 4 Corpse reclaim delay does not apply to accepting resurrection (only Rebirth has it)
SPELL_ATTR_EX3_NO_DURABILITY_LOSS = 0x00000020, // 5
SPELL_ATTR_EX3_NO_AVOIDANCE = 0x00000040, // 6 Persistent Area Aura not removed on leaving radius
SPELL_ATTR_EX3_DOT_STACKING_RULE = 0x00000080, // 7 Create a separate (de)buff stack for each caster
SPELL_ATTR_EX3_ONLY_ON_PLAYER = 0x00000100, // 8 Can target only players
SPELL_ATTR_EX3_NOT_A_PROC = 0x00000200, // 9 Aura periodic trigger is not evaluated as triggered
SPELL_ATTR_EX3_REQUIRES_MAIN_HAND_WEAPON = 0x00000400, // 10
SPELL_ATTR_EX3_ONLY_BATTLEGROUNDS = 0x00000800, // 11
SPELL_ATTR_EX3_ONLY_ON_GHOSTS = 0x00001000, // 12
SPELL_ATTR_EX3_HIDE_CHANNEL_BAR = 0x00002000, // 13 Client will not display channeling bar
SPELL_ATTR_EX3_HIDE_IN_RAID_FILTER = 0x00004000, // 14 Only "Honorless Target" has this flag
SPELL_ATTR_EX3_NORMAL_RANGED_ATTACK = 0x00008000,
// 15 Spells with this attribute are processed as ranged attacks in client
SPELL_ATTR_EX3_SUPPRESS_CASTER_PROCS = 0x00010000, // 16
SPELL_ATTR_EX3_SUPPRESS_TARGET_PROCS = 0x00020000, // 17
SPELL_ATTR_EX3_ALWAYS_HIT = 0x00040000, // 18 Spell should always hit its target
SPELL_ATTR_EX3_INSTANT_TARGET_PROCS = 0x00080000, // 19 Related to spell batching
SPELL_ATTR_EX3_ALLOW_AURA_WHILE_DEAD = 0x00100000, // 20 Death persistent spells
SPELL_ATTR_EX3_ONLY_PROC_OUTDOORS = 0x00200000, // 21
SPELL_ATTR_EX3_CASTING_CANCELS_AUTOREPEAT = 0x00400000, // 22 NYI (only Shoot with Wand has it)
SPELL_ATTR_EX3_NO_DAMAGE_HISTORY = 0x00800000, // 23 NYI
SPELL_ATTR_EX3_REQUIRES_OFFHAND_WEAPON = 0x01000000, // 24
SPELL_ATTR_EX3_TREAT_AS_PERIODIC = 0x02000000, // 25 Does not cause spell pushback
SPELL_ATTR_EX3_CAN_PROC_FROM_PROCS = 0x04000000,
// 26 Auras with this attribute can proc off procced spells (periodic triggers etc)
SPELL_ATTR_EX3_ONLY_PROC_ON_CASTER = 0x08000000, // 27
SPELL_ATTR_EX3_IGNORE_CASTER_AND_TARGET_RESTRICTIONS = 0x10000000,
// 28 Skips all cast checks, moved from AttributesEx after 1.10 (100% correlation)
SPELL_ATTR_EX3_IGNORE_CASTER_MODIFIERS = 0x20000000, // 29
SPELL_ATTR_EX3_DO_NOT_DISPLAY_RANGE = 0x40000000, // 30
SPELL_ATTR_EX3_NOT_ON_AOE_IMMUNE = 0x80000000 // 31
};
enum SpellAttributesEx4 {
SPELL_ATTR_EX4_IGNORE_RESISTANCES = 0x00000001, // 0 From TC 3.3.5, but not present in 1.12 native DBCs. Add it with spell_mod to prevent a spell from being resisted.
SPELL_ATTR_EX4_CLASS_TRIGGER_ONLY_ON_TARGET = 0x00000002, // 1
SPELL_ATTR_EX4_AURA_EXPIRES_OFFLINE = 0x00000004, // 2 Aura continues to expire while player is offline
SPELL_ATTR_EX4_NO_HELPFUL_THREAT = 0x00000008, // 3
SPELL_ATTR_EX4_NO_HARMFUL_THREAT = 0x00000010, // 4
SPELL_ATTR_EX4_ALLOW_CLIENT_TARGETING = 0x00000020, // 5 NYI
SPELL_ATTR_EX4_CANNOT_BE_STOLEN = 0x00000040, // 6 Unused
SPELL_ATTR_EX4_CAN_CAST_WHILE_CASTING = 0x00000080, // 7 NYI (does not seem to work client side either)
SPELL_ATTR_EX4_IGNORE_DAMAGE_TAKEN_MODIFIERS = 0x00000100, // 8
SPELL_ATTR_EX4_COMBAT_FEEDBACK_WHEN_USABLE = 0x00000200, // 9 Initially disabled / Trigger activate from event (Execute, Riposte, Deep Freeze...)
SPELL_ATTR_EX4_IGNORE_RESISTANCES = 0x00000001,
// 0 From TC 3.3.5, but not present in 1.12 native DBCs. Add it with spell_mod to prevent a spell from being resisted.
SPELL_ATTR_EX4_CLASS_TRIGGER_ONLY_ON_TARGET = 0x00000002, // 1
SPELL_ATTR_EX4_AURA_EXPIRES_OFFLINE = 0x00000004, // 2 Aura continues to expire while player is offline
SPELL_ATTR_EX4_NO_HELPFUL_THREAT = 0x00000008, // 3
SPELL_ATTR_EX4_NO_HARMFUL_THREAT = 0x00000010, // 4
SPELL_ATTR_EX4_ALLOW_CLIENT_TARGETING = 0x00000020, // 5 NYI
SPELL_ATTR_EX4_CANNOT_BE_STOLEN = 0x00000040, // 6 Unused
SPELL_ATTR_EX4_CAN_CAST_WHILE_CASTING = 0x00000080, // 7 NYI (does not seem to work client side either)
SPELL_ATTR_EX4_IGNORE_DAMAGE_TAKEN_MODIFIERS = 0x00000100, // 8
SPELL_ATTR_EX4_COMBAT_FEEDBACK_WHEN_USABLE = 0x00000200,
// 9 Initially disabled / Trigger activate from event (Execute, Riposte, Deep Freeze...)
};
// Custom flags assigned in the db
// Custom flags assigned in the db
enum SpellAttributesCustom {
SPELL_CUSTOM_NONE = 0x000,
SPELL_CUSTOM_ALLOW_STACK_BETWEEN_CASTER = 0x001, // For example 'Siphon Soul' must be able to stack between the warlocks on a mob
SPELL_CUSTOM_ALLOW_STACK_BETWEEN_CASTER = 0x001,
// For example 'Siphon Soul' must be able to stack between the warlocks on a mob
SPELL_CUSTOM_NEGATIVE = 0x002,
SPELL_CUSTOM_POSITIVE = 0x004,
SPELL_CUSTOM_CHAN_NO_DIST_LIMIT = 0x008,
SPELL_CUSTOM_FIXED_DAMAGE = 0x010, // Not affected by damage/healing done bonus
SPELL_CUSTOM_FIXED_DAMAGE = 0x010, // Not affected by damage/healing done bonus
SPELL_CUSTOM_IGNORE_ARMOR = 0x020,
SPELL_CUSTOM_BEHIND_TARGET = 0x040, // For spells that require the caster to be behind the target
SPELL_CUSTOM_FACE_TARGET = 0x080, // For spells that require the target to be in front of the caster
SPELL_CUSTOM_SINGLE_TARGET_AURA = 0x100, // Aura applied by spell can only be on 1 target at a time
SPELL_CUSTOM_AURA_APPLY_BREAKS_STEALTH = 0x200, // Stealth is removed when this aura is applied
SPELL_CUSTOM_NOT_REMOVED_ON_EVADE = 0x400, // Aura persists after creature evades
SPELL_CUSTOM_SEND_CHANNEL_VISUAL = 0x800, // Will periodically send the channeling spell visual kit
SPELL_CUSTOM_SEPARATE_AURA_PER_CASTER = 0x1000, // Each caster has his own aura slot, instead of replacing others
SPELL_CUSTOM_BEHIND_TARGET = 0x040, // For spells that require the caster to be behind the target
SPELL_CUSTOM_FACE_TARGET = 0x080, // For spells that require the target to be in front of the caster
SPELL_CUSTOM_SINGLE_TARGET_AURA = 0x100, // Aura applied by spell can only be on 1 target at a time
SPELL_CUSTOM_AURA_APPLY_BREAKS_STEALTH = 0x200, // Stealth is removed when this aura is applied
SPELL_CUSTOM_NOT_REMOVED_ON_EVADE = 0x400, // Aura persists after creature evades
SPELL_CUSTOM_SEND_CHANNEL_VISUAL = 0x800, // Will periodically send the channeling spell visual kit
SPELL_CUSTOM_SEPARATE_AURA_PER_CASTER = 0x1000,
// Each caster has his own aura slot, instead of replacing others
};
enum SpellTarget {
TARGET_NONE = 1,
TARGET_UNIT_CASTER = 2,
@@ -752,26 +910,25 @@ namespace game {
};
// SpellEntry::Targets
enum SpellCastTargetFlags
{
TARGET_FLAG_SELF = 0x00000000,
TARGET_FLAG_UNUSED1 = 0x00000001, // not used in any spells (can be set dynamically)
TARGET_FLAG_UNIT = 0x00000002, // pguid
TARGET_FLAG_UNUSED2 = 0x00000004, // not used in any spells (can be set dynamically)
TARGET_FLAG_UNUSED3 = 0x00000008, // not used in any spells (can be set dynamically)
TARGET_FLAG_ITEM = 0x00000010, // pguid
TARGET_FLAG_SOURCE_LOCATION = 0x00000020, // 3 float
TARGET_FLAG_DEST_LOCATION = 0x00000040, // 3 float
TARGET_FLAG_OBJECT_UNK = 0x00000080, // used in 7 spells only
TARGET_FLAG_UNIT_UNK = 0x00000100, // looks like self target (389 spells)
TARGET_FLAG_PVP_CORPSE = 0x00000200, // pguid
TARGET_FLAG_UNIT_CORPSE = 0x00000400, // 10 spells (gathering professions)
TARGET_FLAG_OBJECT = 0x00000800, // pguid, 0 spells
TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid, 0 spells
TARGET_FLAG_STRING = 0x00002000, // string, 0 spells
TARGET_FLAG_UNK1 = 0x00004000, // 199 spells, opening object/lock
TARGET_FLAG_CORPSE = 0x00008000, // pguid, resurrection spells
TARGET_FLAG_UNK2 = 0x00010000, // pguid, not used in any spells (can be set dynamically)
enum SpellCastTargetFlags {
TARGET_FLAG_SELF = 0x00000000,
TARGET_FLAG_UNUSED1 = 0x00000001, // not used in any spells (can be set dynamically)
TARGET_FLAG_UNIT = 0x00000002, // pguid
TARGET_FLAG_UNUSED2 = 0x00000004, // not used in any spells (can be set dynamically)
TARGET_FLAG_UNUSED3 = 0x00000008, // not used in any spells (can be set dynamically)
TARGET_FLAG_ITEM = 0x00000010, // pguid
TARGET_FLAG_SOURCE_LOCATION = 0x00000020, // 3 float
TARGET_FLAG_DEST_LOCATION = 0x00000040, // 3 float
TARGET_FLAG_OBJECT_UNK = 0x00000080, // used in 7 spells only
TARGET_FLAG_UNIT_UNK = 0x00000100, // looks like self target (389 spells)
TARGET_FLAG_PVP_CORPSE = 0x00000200, // pguid
TARGET_FLAG_UNIT_CORPSE = 0x00000400, // 10 spells (gathering professions)
TARGET_FLAG_OBJECT = 0x00000800, // pguid, 0 spells
TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid, 0 spells
TARGET_FLAG_STRING = 0x00002000, // string, 0 spells
TARGET_FLAG_UNK1 = 0x00004000, // 199 spells, opening object/lock
TARGET_FLAG_CORPSE = 0x00008000, // pguid, resurrection spells
TARGET_FLAG_UNK2 = 0x00010000, // pguid, not used in any spells (can be set dynamically)
};
enum Events : std::uint32_t {
@@ -1188,7 +1345,7 @@ namespace game {
SPELLMOD_CRIT_DAMAGE_BONUS = 15,
SPELLMOD_RESIST_MISS_CHANCE = 16,
SPELLMOD_JUMP_TARGETS = 17,
SPELLMOD_CHANCE_OF_SUCCESS = 18, // Only used with SPELL_AURA_ADD_FLAT_MODIFIER and affects proc spells
SPELLMOD_CHANCE_OF_SUCCESS = 18, // Only used with SPELL_AURA_ADD_FLAT_MODIFIER and affects proc spells
SPELLMOD_ACTIVATION_TIME = 19,
SPELLMOD_EFFECT_PAST_FIRST = 20,
SPELLMOD_CASTING_TIME_OLD = 21,
@@ -1218,139 +1375,146 @@ namespace game {
class CDuration {
public:
char m_DurationIndex; //0x0000
__int32 m_Duration; //0x0004
char unknown[4]; //0x0008
__int32 m_Duration2; //0x000C
char m_DurationIndex; //0x0000
__int32 m_Duration; //0x0004
char unknown[4]; //0x0008
__int32 m_Duration2; //0x000C
__int32 GetDuration() {
return ((m_Duration / 1000) / 60);
}
};//Size=0x0010
}; //Size=0x0010
class CSpellCastingTime {
public:
__int32 m_CastingTimeIndex; //0x0000
__int32 m_CastTime; //0x0004
char m_0x0008[4]; //0x0008
__int32 m_CastTime2; //0x000C
};//Size=0x0010
__int32 m_CastingTimeIndex; //0x0000
__int32 m_CastTime; //0x0004
char m_0x0008[4]; //0x0008
__int32 m_CastTime2; //0x000C
}; //Size=0x0010
enum UnitFlags {
UNIT_FLAG_NONE = 0x00000000,
UNIT_FLAG_UNK_0 = 0x00000001, // Movement checks disabled, likely paired with loss of client control packet.
UNIT_FLAG_SPAWNING = 0x00000002, // not attackable
UNIT_FLAG_UNK_0 = 0x00000001, // Movement checks disabled, likely paired with loss of client control packet.
UNIT_FLAG_SPAWNING = 0x00000002, // not attackable
UNIT_FLAG_DISABLE_MOVE = 0x00000004,
UNIT_FLAG_PLAYER_CONTROLLED = 0x00000008, // players, pets, totems, guardians, companions, charms, any units associated with players
UNIT_FLAG_PET_RENAME = 0x00000010, // Old pet rename: moved to UNIT_FIELD_BYTES_2,2 in TBC+
UNIT_FLAG_PET_ABANDON = 0x00000020, // Old pet abandon: moved to UNIT_FIELD_BYTES_2,2 in TBC+
UNIT_FLAG_PLAYER_CONTROLLED = 0x00000008,
// players, pets, totems, guardians, companions, charms, any units associated with players
UNIT_FLAG_PET_RENAME = 0x00000010, // Old pet rename: moved to UNIT_FIELD_BYTES_2,2 in TBC+
UNIT_FLAG_PET_ABANDON = 0x00000020, // Old pet abandon: moved to UNIT_FIELD_BYTES_2,2 in TBC+
UNIT_FLAG_UNK_6 = 0x00000040,
UNIT_FLAG_IMMUNE_TO_PLAYER = 0x00000100, // Target is immune to players
UNIT_FLAG_IMMUNE_TO_NPC = 0x00000200, // Target is immune to creatures
UNIT_FLAG_IMMUNE_TO_PLAYER = 0x00000100, // Target is immune to players
UNIT_FLAG_IMMUNE_TO_NPC = 0x00000200, // Target is immune to creatures
UNIT_FLAG_PVP = 0x00001000,
UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
UNIT_FLAG_UNK_14 = 0x00004000,
UNIT_FLAG_USE_SWIM_ANIMATION = 0x00008000,
UNIT_FLAG_NON_ATTACKABLE_2 = 0x00010000, // removes attackable icon, if on yourself, cannot assist self but can cast TARGET_UNIT_CASTER spells - added by SPELL_AURA_MOD_UNATTACKABLE
UNIT_FLAG_NON_ATTACKABLE_2 = 0x00010000,
// removes attackable icon, if on yourself, cannot assist self but can cast TARGET_UNIT_CASTER spells - added by SPELL_AURA_MOD_UNATTACKABLE
UNIT_FLAG_PACIFIED = 0x00020000,
UNIT_FLAG_STUNNED = 0x00040000, // Unit is a subject to stun, turn and strafe movement disabled
UNIT_FLAG_STUNNED = 0x00040000, // Unit is a subject to stun, turn and strafe movement disabled
UNIT_FLAG_IN_COMBAT = 0x00080000,
UNIT_FLAG_TAXI_FLIGHT = 0x00100000, // Unit is on taxi, paired with a duplicate loss of client control packet (likely a legacy serverside hack). Disables any spellcasts not allowed in taxi flight client-side.
UNIT_FLAG_CONFUSED = 0x00400000, // Unit is a subject to confused movement, movement checks disabled, paired with loss of client control packet.
UNIT_FLAG_FLEEING = 0x00800000, // Unit is a subject to fleeing movement, movement checks disabled, paired with loss of client control packet.
UNIT_FLAG_POSSESSED = 0x01000000, // Unit is under remote control by another unit, movement checks disabled, paired with loss of client control packet. New master is allowed to use melee attack and can't select this unit via mouse in the world (as if it was own character).
UNIT_FLAG_TAXI_FLIGHT = 0x00100000,
// Unit is on taxi, paired with a duplicate loss of client control packet (likely a legacy serverside hack). Disables any spellcasts not allowed in taxi flight client-side.
UNIT_FLAG_CONFUSED = 0x00400000,
// Unit is a subject to confused movement, movement checks disabled, paired with loss of client control packet.
UNIT_FLAG_FLEEING = 0x00800000,
// Unit is a subject to fleeing movement, movement checks disabled, paired with loss of client control packet.
UNIT_FLAG_POSSESSED = 0x01000000,
// Unit is under remote control by another unit, movement checks disabled, paired with loss of client control packet. New master is allowed to use melee attack and can't select this unit via mouse in the world (as if it was own character).
UNIT_FLAG_NOT_SELECTABLE = 0x02000000,
UNIT_FLAG_SKINNABLE = 0x04000000,
UNIT_FLAG_AURAS_VISIBLE = 0x08000000, // magic detect
UNIT_FLAG_AURAS_VISIBLE = 0x08000000, // magic detect
UNIT_FLAG_SHEATHE = 0x40000000,
UNIT_FLAG_IMMUNE = 0x80000000, // Immune to damage
UNIT_FLAG_IMMUNE = 0x80000000, // Immune to damage
// [-ZERO] TBC enumerations [?]
UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080, // ?? (UNIT_FLAG_PLAYER_CONTROLLED | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE
UNIT_FLAG_LOOTING = 0x00000400, // loot animation
UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
UNIT_FLAG_DISARMED = 0x00200000, // disable melee spells casting..., "Required melee weapon" added to melee spells tooltip.
UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080,
// ?? (UNIT_FLAG_PLAYER_CONTROLLED | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE
UNIT_FLAG_LOOTING = 0x00000400, // loot animation
UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
UNIT_FLAG_DISARMED = 0x00200000,
// disable melee spells casting..., "Required melee weapon" added to melee spells tooltip.
UNIT_FLAG_UNK_28 = 0x10000000,
UNIT_FLAG_UNK_29 = 0x20000000, // used in Feing Death spell
UNIT_FLAG_UNK_29 = 0x20000000, // used in Feing Death spell
};
typedef struct UnitFields {
uint64_t charm; // Size:2
uint64_t summon; // Size:2
uint64_t charmedBy; // Size:2
uint64_t summonedBy; // Size:2
uint64_t createdBy; // Size:2
uint64_t target; // Size:2
uint64_t persuaded; // Size:2
uint64_t channelObject; // Size:2
uint32_t health; // Size:1
uint32_t power1; // Size:1
uint32_t power2; // Size:1
uint32_t power3; // Size:1
uint32_t power4; // Size:1
uint32_t power5; // Size:1
uint32_t maxHealth; // Size:1
uint32_t maxPower1; // Size:1
uint32_t maxPower2; // Size:1
uint32_t maxPower3; // Size:1
uint32_t maxPower4; // Size:1
uint32_t maxPower5; // Size:1
uint32_t level; // Size:1
uint32_t factionTemplate; // Size:1
uint32_t bytes0; // Size:1
uint32_t virtualItemDisplay[3]; // Size:3
uint32_t virtualItemInfo[6]; // Size:6
uint32_t flags; // Size:1
uint32_t aura[48]; // Size:48
uint32_t auraFlags[6]; // Size:6
uint8_t auraLevels[48]; // Size:48
uint8_t auraApplications[48]; // Size:48
uint32_t auraState; // Size:1
uint32_t baseAttackTime; // Size:1
uint32_t offhandAttackTime; // Size:1
uint32_t rangedAttackTime; // Size:1
float boundingRadius; // Size:1
float combatReach; // Size:1
uint32_t displayId; // Size:1
uint32_t nativeDisplayId; // Size:1
uint32_t mountDisplayId; // Size:1
float minDamage; // Size:1
float maxDamage; // Size:1
float minOffhandDamage; // Size:1
float maxOffhandDamage; // Size:1
uint32_t bytes1; // Size:1
uint32_t petNumber; // Size:1
uint32_t petNameTimestamp; // Size:1
uint32_t petExperience; // Size:1
uint32_t petNextLevelExp; // Size:1
uint32_t dynamicFlags; // Size:1
uint32_t channelSpell; // Size:1
float modCastSpeed; // Size:1 (Float in 1.12+)
uint32_t createdBySpell; // Size:1
uint32_t npcFlags; // Size:1
uint32_t npcEmoteState; // Size:1
uint32_t trainingPoints; // Size:1
uint32_t stat0; // Size:1
uint32_t stat1; // Size:1
uint32_t stat2; // Size:1
uint32_t stat3; // Size:1
uint32_t stat4; // Size:1
uint32_t resistances[7]; // Size:7
uint32_t baseMana; // Size:1
uint32_t baseHealth; // Size:1
uint32_t bytes2; // Size:1
uint32_t attackPower; // Size:1
uint32_t attackPowerMods; // Size:1
float attackPowerMultiplier; // Size:1
uint32_t rangedAttackPower; // Size:1
uint32_t rangedAttackPowerMods; // Size:1
float rangedAttackPowerMultiplier; // Size:1
float minRangedDamage; // Size:1
float maxRangedDamage; // Size:1
float powerCostModifier[7]; // Size:7
float powerCostMultiplier[7]; // Size:7
uint64_t charm; // Size:2
uint64_t summon; // Size:2
uint64_t charmedBy; // Size:2
uint64_t summonedBy; // Size:2
uint64_t createdBy; // Size:2
uint64_t target; // Size:2
uint64_t persuaded; // Size:2
uint64_t channelObject; // Size:2
uint32_t health; // Size:1
uint32_t power1; // Size:1
uint32_t power2; // Size:1
uint32_t power3; // Size:1
uint32_t power4; // Size:1
uint32_t power5; // Size:1
uint32_t maxHealth; // Size:1
uint32_t maxPower1; // Size:1
uint32_t maxPower2; // Size:1
uint32_t maxPower3; // Size:1
uint32_t maxPower4; // Size:1
uint32_t maxPower5; // Size:1
uint32_t level; // Size:1
uint32_t factionTemplate; // Size:1
uint32_t bytes0; // Size:1
uint32_t virtualItemDisplay[3]; // Size:3
uint32_t virtualItemInfo[6]; // Size:6
uint32_t flags; // Size:1
uint32_t aura[48]; // Size:48
uint32_t auraFlags[6]; // Size:6
uint8_t auraLevels[48]; // Size:48
uint8_t auraApplications[48]; // Size:48
uint32_t auraState; // Size:1
uint32_t baseAttackTime; // Size:1
uint32_t offhandAttackTime; // Size:1
uint32_t rangedAttackTime; // Size:1
float boundingRadius; // Size:1
float combatReach; // Size:1
uint32_t displayId; // Size:1
uint32_t nativeDisplayId; // Size:1
uint32_t mountDisplayId; // Size:1
float minDamage; // Size:1
float maxDamage; // Size:1
float minOffhandDamage; // Size:1
float maxOffhandDamage; // Size:1
uint32_t bytes1; // Size:1
uint32_t petNumber; // Size:1
uint32_t petNameTimestamp; // Size:1
uint32_t petExperience; // Size:1
uint32_t petNextLevelExp; // Size:1
uint32_t dynamicFlags; // Size:1
uint32_t channelSpell; // Size:1
float modCastSpeed; // Size:1 (Float in 1.12+)
uint32_t createdBySpell; // Size:1
uint32_t npcFlags; // Size:1
uint32_t npcEmoteState; // Size:1
uint32_t trainingPoints; // Size:1
uint32_t stat0; // Size:1
uint32_t stat1; // Size:1
uint32_t stat2; // Size:1
uint32_t stat3; // Size:1
uint32_t stat4; // Size:1
uint32_t resistances[7]; // Size:7
uint32_t baseMana; // Size:1
uint32_t baseHealth; // Size:1
uint32_t bytes2; // Size:1
uint32_t attackPower; // Size:1
uint32_t attackPowerMods; // Size:1
float attackPowerMultiplier; // Size:1
uint32_t rangedAttackPower; // Size:1
uint32_t rangedAttackPowerMods; // Size:1
float rangedAttackPowerMultiplier; // Size:1
float minRangedDamage; // Size:1
float maxRangedDamage; // Size:1
float powerCostModifier[7]; // Size:7
float powerCostMultiplier[7]; // Size:7
} UnitFields;
uintptr_t *GetObjectPtr(std::uint64_t guid);
@@ -1367,6 +1531,10 @@ namespace game {
uint32_t GetItemId(CGItem_C *item);
uintptr_t *GetObjectVFTable(uintptr_t *unit);
uintptr_t *GetPlayerInventoryPtr(uintptr_t *playerUnit);
const char *GetSpellName(uint32_t spellId);
std::uint64_t ClntObjMgrGetActivePlayerGuid();
+41
View File
@@ -13,6 +13,20 @@
#include <string>
namespace Nampower {
// Lua function pointers
lua_errorT lua_error = reinterpret_cast<lua_errorT>(Offsets::lua_error);
lua_gettopT lua_gettop = reinterpret_cast<lua_gettopT>(Offsets::lua_gettop);
lua_isstringT lua_isstring = reinterpret_cast<lua_isstringT>(Offsets::lua_isstring);
lua_isnumberT lua_isnumber = reinterpret_cast<lua_isnumberT>(Offsets::lua_isnumber);
lua_tostringT lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
lua_tonumberT lua_tonumber = reinterpret_cast<lua_tonumberT>(Offsets::lua_tonumber);
lua_pushnumberT lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
lua_pushstringT lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
lua_pushbooleanT lua_pushboolean = reinterpret_cast<lua_pushbooleanT>(Offsets::lua_pushboolean);
lua_pushnilT lua_pushnil = reinterpret_cast<lua_pushnilT>(Offsets::lua_pushnil);
lua_newtableT lua_newtable = reinterpret_cast<lua_newtableT>(Offsets::lua_newtable);
lua_settableT lua_settable = reinterpret_cast<lua_settableT>(Offsets::lua_settable);
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType) {
struct CachedEntry {
@@ -263,6 +277,33 @@ namespace Nampower {
return guidStr;
}
uint64_t GetUnitGuidFromString(const char *unitToken) {
if (!unitToken) {
return 0;
}
// Check if it's a GUID string (starts with "0x" or "0X")
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
return std::stoull(unitToken, nullptr, 16);
} else {
// Get GUID from unit token
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
return getGUIDFromName(unitToken);
}
}
uint64_t GetUnitGuidFromLuaParam(uintptr_t *luaState, int paramIndex) {
if (lua_isnumber(luaState, paramIndex)) {
// Parameter is a GUID number
return static_cast<uint64_t>(lua_tonumber(luaState, paramIndex));
} else if (lua_isstring(luaState, paramIndex)) {
// Parameter is a unit token or GUID string
const char *unitToken = lua_tostring(luaState, paramIndex);
return GetUnitGuidFromString(unitToken);
}
return 0;
}
float GetNameplateDistance() {
auto const distanceSquared = *reinterpret_cast<float *>(Offsets::NameplateDistance);
return sqrtf(distanceSquared);
+19
View File
@@ -5,8 +5,23 @@
#pragma once
#include "game.hpp"
#include "main.hpp"
namespace Nampower {
// Lua function pointers
extern lua_errorT lua_error;
extern lua_gettopT lua_gettop;
extern lua_isstringT lua_isstring;
extern lua_isnumberT lua_isnumber;
extern lua_tostringT lua_tostring;
extern lua_tonumberT lua_tonumber;
extern lua_pushnumberT lua_pushnumber;
extern lua_pushstringT lua_pushstring;
extern lua_pushbooleanT lua_pushboolean;
extern lua_pushnilT lua_pushnil;
extern lua_newtableT lua_newtable;
extern lua_settableT lua_settable;
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType);
uint32_t GetSpellIdFromSpellName(const char *spellName);
@@ -35,6 +50,10 @@ namespace Nampower {
char *ConvertGuidToString(uint64_t guid);
uint64_t GetUnitGuidFromLuaParam(uintptr_t *luaState, int paramIndex);
uint64_t GetUnitGuidFromString(const char *unitToken);
float GetNameplateDistance();
void SetNameplateDistance(float distance);
+742 -8
View File
@@ -5,6 +5,7 @@
#include "items.hpp"
#include "offsets.hpp"
#include "logging.hpp"
#include "helper.hpp"
#include <fstream>
#include <sstream>
@@ -12,6 +13,8 @@
#include <unordered_map>
#include <unordered_set>
#include "dbc_fields.hpp"
namespace Nampower {
// Global dictionary to store itemId -> ItemStats_C mappings
static std::unordered_map<uint32_t, game::ItemStats_C *> itemStatsCache;
@@ -19,6 +22,9 @@ namespace Nampower {
// Track pending async loads (can have multiple at once)
static std::unordered_set<uint32_t> pendingItemIds;
// Cache for itemName -> itemId mappings (case-insensitive)
static std::unordered_map<std::string, uint32_t> itemNameToIdCache;
// Export state tracking
static bool isExporting = false;
static uint32_t currentExportItemId = 0;
@@ -28,6 +34,18 @@ namespace Nampower {
auto const getRow = reinterpret_cast<DBCache_ItemCacheDBGetRowT>(Offsets::DBCache_ItemCacheDBGetRow);
auto const itemCache = reinterpret_cast<void *>(Offsets::ItemDBCache);
// Define string keys as char arrays
static char itemIdKey[] = "itemId";
static char permanentEnchantIdKey[] = "permanentEnchantId";
static char tempEnchantIdKey[] = "tempEnchantId";
static char stackCountKey[] = "stackCount";
static char durationKey[] = "duration";
static char flagsKey[] = "flags";
static char durabilityKey[] = "durability";
static char maxDurabilityKey[] = "maxDurability";
static char tempEnchantmentTimeLeftMsKey[] = "tempEnchantmentTimeLeftMs";
static char tempEnchantmentChargesKey[] = "tempEnchantmentCharges";
std::string escapeJsonString(const char *str) {
if (!str) return "null";
@@ -91,7 +109,7 @@ namespace Nampower {
// Check if already in cache
auto it = itemStatsCache.find(itemId);
if (it != itemStatsCache.end()) {
return false; // Already loaded, no wait needed
return false; // Already loaded, no wait needed
}
uint64_t guid;
@@ -99,17 +117,17 @@ namespace Nampower {
if (itemStats) {
// Item was available immediately, store in cache
itemStatsCache[itemId] = reinterpret_cast<game::ItemStats_C *>(itemStats);
return false; // Loaded immediately, no wait needed
return false; // Loaded immediately, no wait needed
} else {
// Item not immediately available, need to wait for callback
pendingItemIds.insert(itemId);
return true; // Caller should wait for callback
return true; // Caller should wait for callback
}
}
bool ProcessItemExport() {
if (!isExporting) {
return false; // Not currently exporting
return false; // Not currently exporting
}
// Try to load items until we have 20 pending or run out of items
@@ -120,7 +138,7 @@ namespace Nampower {
// Log progress every 1000 items
if (currentExportItemId % 1000 == 0) {
DEBUG_LOG("Progress: " << currentExportItemId << " / " << MAX_EXPORT_ITEM_ID
<< " items requested, " << pendingItemIds.size() << " pending callbacks");
<< " items requested, " << pendingItemIds.size() << " pending callbacks");
}
currentExportItemId++;
@@ -136,7 +154,7 @@ namespace Nampower {
if (currentExportItemId > MAX_EXPORT_ITEM_ID && pendingItemIds.empty()) {
// Fall through to export completion
} else {
return true; // Still exporting, more items to process or waiting for callbacks
return true; // Still exporting, more items to process or waiting for callbacks
}
// Export complete, write to file
@@ -313,10 +331,13 @@ namespace Nampower {
}
isExporting = false;
return false; // Export complete
return false; // Export complete
}
game::ItemStats_C *GetItemStats(uint32_t itemId) {
// Load item if not cached
LoadItem(itemId);
auto it = itemStatsCache.find(itemId);
if (it != itemStatsCache.end()) {
return it->second;
@@ -327,7 +348,9 @@ namespace Nampower {
// Register a function if you want to use this
// Don't want everyone scraping all items.
void ExportAllItems() {
DEBUG_LOG("Starting item export - will fetch items 1 to " << MAX_EXPORT_ITEM_ID << " with up to " << ITEMS_PER_FRAME << " concurrent requests");
DEBUG_LOG(
"Starting item export - will fetch items 1 to " << MAX_EXPORT_ITEM_ID << " with up to " << ITEMS_PER_FRAME
<< " concurrent requests");
// Initialize export state
isExporting = true;
@@ -336,4 +359,715 @@ namespace Nampower {
DEBUG_LOG("Export initialized. ProcessItemExport will be called each frame from processQueues.");
}
// Helper function to convert string to lowercase
std::string ToLowerCase(const char *str) {
std::string result;
if (!str) return result;
while (*str) {
result += static_cast<char>(tolower(*str));
++str;
}
return result;
}
// Helper function to get itemId from itemName using cache
// Returns 0 if not found in cache
uint32_t GetItemIdFromCache(const char *itemName) {
if (!itemName) return 0;
std::string lowerName = ToLowerCase(itemName);
auto it = itemNameToIdCache.find(lowerName);
if (it != itemNameToIdCache.end()) {
return it->second;
}
return 0;
}
// Helper function to add itemName -> itemId mapping to cache
void CacheItemNameToId(const char *itemName, uint32_t itemId) {
if (!itemName || itemId == 0) return;
std::string lowerName = ToLowerCase(itemName);
itemNameToIdCache[lowerName] = itemId;
}
// Helper function to check if an item matches search criteria
bool DoesItemMatch(uint32_t itemId, uint32_t searchItemId, const char *searchItemName) {
if (searchItemId != 0) {
// Searching by ID
return itemId == searchItemId;
} else if (searchItemName) {
// Searching by name
auto itemStats = GetItemStats(itemId);
if (itemStats && itemStats->m_displayName[0]) {
bool matches = _stricmp(itemStats->m_displayName[0], searchItemName) == 0;
// Cache the mapping if we found a match
if (matches) {
CacheItemNameToId(searchItemName, itemId);
}
return matches;
}
}
return false;
}
// Helper function to push item found result to Lua stack
void PushItemFoundResult(uintptr_t *luaState, int32_t bagIndex, uint32_t slot) {
// Adjust slot for special bags (assembly expects relative positions)
uint32_t adjustedSlot = slot;
if (bagIndex == 0) {
adjustedSlot = slot - 0x17; // subtract 23
} else if (bagIndex == -1) {
adjustedSlot = slot - 0x27; // subtract 39
} else if (bagIndex == -2) {
adjustedSlot = slot - 0x51; // subtract 81
}
lua_pushnumber(luaState, static_cast<double>(bagIndex));
lua_pushnumber(luaState, static_cast<double>(adjustedSlot + 1)); // lua is 1 indexed
}
// Helper function to get bag pointer from container
uintptr_t *GetBagPtrFromContainer(uintptr_t *containerPtr) {
auto vftable = game::GetObjectVFTable(containerPtr);
using GetBagPtrT = uintptr_t * (__thiscall *)(uintptr_t *);
auto getBagPtrFunc = reinterpret_cast<GetBagPtrT>(vftable[4]);
return getBagPtrFunc(containerPtr);
}
// Helper function to create basic item info table for CGItem (equipped items from other units)
void CreateBasicItemInfoTable(uintptr_t *luaState, game::CGItem *cgItem) {
if (!cgItem) {
lua_pushnil(luaState);
return;
}
lua_newtable(luaState);
// Add itemId
lua_pushstring(luaState, itemIdKey);
lua_pushnumber(luaState, static_cast<double>(cgItem->itemId));
lua_settable(luaState, -3);
// Add permanentEnchantId
lua_pushstring(luaState, permanentEnchantIdKey);
lua_pushnumber(luaState, static_cast<double>(cgItem->permanentEnchantId));
lua_settable(luaState, -3);
// Add tempEnchantId
lua_pushstring(luaState, tempEnchantIdKey);
lua_pushnumber(luaState, static_cast<double>(cgItem->tempEnchantId));
lua_settable(luaState, -3);
}
// Helper function to create item info table on Lua stack (leaves table on stack)
void CreateItemInfoTable(uintptr_t *luaState, game::CGItem_C *item) {
if (!item || !item->itemFields) {
lua_pushnil(luaState);
return;
}
auto itemId = game::GetItemId(item);
auto itemFields = item->itemFields;
// Create item info table
lua_newtable(luaState);
// Add itemId
lua_pushstring(luaState, itemIdKey);
lua_pushnumber(luaState, static_cast<double>(itemId));
lua_settable(luaState, -3);
// Add stackCount
lua_pushstring(luaState, stackCountKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->stackCount));
lua_settable(luaState, -3);
// Add duration
lua_pushstring(luaState, durationKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->duration));
lua_settable(luaState, -3);
// Add flags
lua_pushstring(luaState, flagsKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->flags));
lua_settable(luaState, -3);
// Add permanent enchantment
lua_pushstring(luaState, permanentEnchantIdKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->permEnchantmentSlot.id));
lua_settable(luaState, -3);
// Add temp enchantment
lua_pushstring(luaState, tempEnchantIdKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->tempEnchantmentSlot.id));
lua_settable(luaState, -3);
// Add temp enchantment time left
lua_pushstring(luaState, tempEnchantmentTimeLeftMsKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->tempEnchantmentSlot.duration));
lua_settable(luaState, -3);
// Add temp enchantment charges
lua_pushstring(luaState, tempEnchantmentChargesKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->tempEnchantmentSlot.charges));
lua_settable(luaState, -3);
// Add durability
lua_pushstring(luaState, durabilityKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->durability));
lua_settable(luaState, -3);
// Add max durability
lua_pushstring(luaState, maxDurabilityKey);
lua_pushnumber(luaState, static_cast<double>(itemFields->maxDurability));
lua_settable(luaState, -3);
}
// Helper function to push bag item to parent table on Lua stack
void PushBagCGItemToTable(uintptr_t *luaState, int32_t bagIndex, uint32_t slot, game::CGItem_C *item) {
if (!item) {
DEBUG_LOG("missing item at slot " << slot);
return;
}
if (!item->itemFields) {
DEBUG_LOG("missing itemfields at slot " << slot << " itemId " << game::GetItemId(item));
return;
}
// Adjust slot for special bags (assembly expects relative positions)
uint32_t adjustedSlot = slot;
if (bagIndex == 0) {
adjustedSlot = slot - 0x17; // subtract 23
} else if (bagIndex == -1) {
adjustedSlot = slot - 0x27; // subtract 39
} else if (bagIndex == -2) {
adjustedSlot = slot - 0x51; // subtract 81
}
// Push slot number as key
lua_pushnumber(luaState, static_cast<double>(adjustedSlot + 1)); // lua is 1 indexed
// Create and push item info table
CreateItemInfoTable(luaState, item);
// Set parent_table[slot] = itemInfo
lua_settable(luaState, -3);
}
uint32_t FindPlayerItemSlot(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
// Get player unit
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto playerUnit = game::GetObjectPtr(playerGuid);
if (!playerUnit) {
lua_error(luaState, "Unable to get player unit");
return 0;
}
uint32_t searchItemId = 0;
const char *searchItemName = nullptr;
// Check if first param is string (item name) or number (item id)
if (lua_isnumber(luaState, 1)) {
searchItemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
} else if (lua_isstring(luaState, 1)) {
searchItemName = lua_tostring(luaState, 1);
// Check cache first to avoid string comparisons
uint32_t cachedItemId = GetItemIdFromCache(searchItemName);
if (cachedItemId != 0) {
searchItemId = cachedItemId;
searchItemName = nullptr; // Use ID instead for faster search
}
} else {
lua_error(luaState, "Usage: FindPlayerItemSlot(itemName) or FindPlayerItemSlot(itemId)");
return 0;
}
auto const getBagItem = reinterpret_cast<CGBag_C_GetItemAtSlotT>(Offsets::CGBag_C_GetItemAtSlot);
auto const getContainerGuid = reinterpret_cast<GetContainerGuidT>(Offsets::GetContainerGuid);
auto inventory = game::GetPlayerInventoryPtr(playerUnit);
// Loop through equipment slots 0->18
for (uint32_t slot = 0; slot <= 18; slot++) {
auto item = getBagItem(inventory, slot);
if (item && DoesItemMatch(game::GetItemId(item), searchItemId, searchItemName)) {
lua_pushnil(luaState); // bagIndex = nil for equipped items
lua_pushnumber(luaState, static_cast<double>(slot+1)); // lua is 1 indexed
return 2;
}
}
// Loop through inventory pack slots 23-38 (bag 0)
for (uint32_t slot = 23; slot <= 38; slot++) {
auto item = getBagItem(inventory, slot);
if (item && DoesItemMatch(game::GetItemId(item), searchItemId, searchItemName)) {
PushItemFoundResult(luaState, 0, slot);
return 2;
}
}
// Loop through bags 1-4
for (int32_t bagIndex = 1; bagIndex <= 4; bagIndex++) {
uint64_t containerGuid = getContainerGuid(bagIndex - 1); // bagIndex 1-4 maps to container 0-3
if (containerGuid == 0) continue;
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
if (!containerPtr) continue;
auto bagPtr = GetBagPtrFromContainer(containerPtr);
if (!bagPtr) continue;
auto bagSize = *bagPtr;
for (uint32_t slot = 0; slot < bagSize; slot++) {
auto item = getBagItem(bagPtr, slot);
if (item && DoesItemMatch(game::GetItemId(item), searchItemId, searchItemName)) {
PushItemFoundResult(luaState, bagIndex, slot);
return 2;
}
}
}
uint64_t bankGuid = *reinterpret_cast<uint64_t *>(Offsets::BankGuid);
if (bankGuid > 0) {
// Loop through bank item slots 39-62 (bag -1)
for (uint32_t slot = 39; slot <= 62; slot++) {
auto item = getBagItem(inventory, slot);
if (item && DoesItemMatch(game::GetItemId(item), searchItemId, searchItemName)) {
PushItemFoundResult(luaState, -1, slot);
return 2;
}
}
// Loop through bank bags 5-9
for (int32_t bagIndex = 5; bagIndex <= 9; bagIndex++) {
uint64_t containerGuid = getContainerGuid(bagIndex - 1); // bagIndex 5-9 maps to container 4-8
if (containerGuid == 0) continue;
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
if (!containerPtr) continue;
auto bagPtr = GetBagPtrFromContainer(containerPtr);
if (!bagPtr) continue;
auto bagSize = *bagPtr;
for (uint32_t slot = 0; slot < bagSize; slot++) {
auto item = getBagItem(bagPtr, slot);
if (item && DoesItemMatch(game::GetItemId(item), searchItemId, searchItemName)) {
PushItemFoundResult(luaState, bagIndex, slot);
return 2;
}
}
}
}
// Loop through keyring slots 81-96 (bag -2)
for (uint32_t slot = 81; slot <= 96; slot++) {
auto item = getBagItem(inventory, slot);
if (item && DoesItemMatch(game::GetItemId(item), searchItemId, searchItemName)) {
PushItemFoundResult(luaState, -2, slot);
return 2;
}
}
// Item not found
lua_pushnil(luaState);
lua_pushnil(luaState);
return 2;
}
uint32_t GetEquippedItems(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
uint64_t guid;
// Check if a parameter was provided
if (lua_gettop(luaState) >= 1) {
guid = GetUnitGuidFromLuaParam(luaState, 1);
if (guid == 0) {
lua_error(luaState, "Usage: GetEquippedItems() or GetEquippedItems(unitStr) or GetEquippedItems(guid)");
return 0;
}
} else {
// No parameter, use player
guid = game::ClntObjMgrGetActivePlayerGuid();
}
auto unit = game::GetObjectPtr(guid);
if (!unit) {
lua_error(luaState, "Unable to get unit");
return 0;
}
auto const canInspectUnit = reinterpret_cast<CanInspectUnitT>(Offsets::CanInspectUnit);
if (!canInspectUnit(unit)) {
lua_error(luaState, "Cannot inspect unit");
return 0;
}
// Create a new Lua table
lua_newtable(luaState);
// Check if this is the active player
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
bool isPlayer = (guid == playerGuid);
if (isPlayer) {
// For player, use getBagItem with inventory to get full item data
auto const getBagItem = reinterpret_cast<CGBag_C_GetItemAtSlotT>(Offsets::CGBag_C_GetItemAtSlot);
auto inventory = game::GetPlayerInventoryPtr(unit);
for (uint32_t slot = 0; slot <= 18; slot++) {
auto item = getBagItem(inventory, slot);
if (item) {
// Equipment slots don't need bagIndex adjustment (pass -999 as a flag)
PushBagCGItemToTable(luaState, -999, slot, item);
}
}
} else {
// For other units, use CGUnit_C_GetEquippedItemAtSlot (limited data)
auto const getEquippedItem = reinterpret_cast<CGUnit_C_GetEquippedItemAtSlotT>(
Offsets::CGUnit_C_GetEquippedItemAtSlot);
for (uint32_t slot = 0; slot <= 18; slot++) {
auto cgItem = getEquippedItem(unit, slot);
if (cgItem) {
// Push slot number as key
lua_pushnumber(luaState, static_cast<double>(slot+1)); // lua is 1 indexed
// Create and push basic item info table
CreateBasicItemInfoTable(luaState, cgItem);
// Set result[slot] = item info table
lua_settable(luaState, -3);
}
}
}
return 1;
}
uint32_t GetEquippedItem(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
// Check for required parameters
if (lua_gettop(luaState) < 2) {
lua_error(luaState, "Usage: GetEquippedItem(unitStr, slot) or GetEquippedItem(guid, slot)");
return 0;
}
// Get unit GUID from first parameter
uint64_t guid;
if (lua_gettop(luaState) >= 1) {
guid = GetUnitGuidFromLuaParam(luaState, 1);
if (guid == 0) {
lua_error(luaState, "Usage: GetEquippedItem(unitStr, slot) or GetEquippedItem(guid, slot)");
return 0;
}
} else {
// No parameter, use player
guid = game::ClntObjMgrGetActivePlayerGuid();
}
// Get slot from second parameter
if (!lua_isnumber(luaState, 2)) {
lua_error(luaState, "Slot must be a number between 1 and 19");
return 0;
}
auto luaSlot = static_cast<uint32_t>(lua_tonumber(luaState, 2));
// Check slot is in valid range
if (luaSlot < 1 || luaSlot > 19) {
lua_error(luaState, "Slot must be between 1 and 19");
return 0;
}
auto unit = game::GetObjectPtr(guid);
if (!unit) {
lua_error(luaState, "Unable to get unit");
return 0;
}
auto const canInspectUnit = reinterpret_cast<CanInspectUnitT>(Offsets::CanInspectUnit);
if (!canInspectUnit(unit)) {
lua_error(luaState, "Cannot inspect unit");
return 0;
}
// Check if this is the active player
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
bool isPlayer = (guid == playerGuid);
if (isPlayer) {
// For player, use getBagItem with inventory to get full item data
auto const getBagItem = reinterpret_cast<CGBag_C_GetItemAtSlotT>(Offsets::CGBag_C_GetItemAtSlot);
auto inventory = game::GetPlayerInventoryPtr(unit);
auto item = getBagItem(inventory, luaSlot-1); // luaSlot is 1-indexed
CreateItemInfoTable(luaState, item);
} else {
// For other units, use CGUnit_C_GetEquippedItemAtSlot (limited data)
auto const getEquippedItem = reinterpret_cast<CGUnit_C_GetEquippedItemAtSlotT>(
Offsets::CGUnit_C_GetEquippedItemAtSlot);
auto cgItem = getEquippedItem(unit, luaSlot - 1); // luaSlot is 1-indexed
CreateBasicItemInfoTable(luaState, cgItem);
}
return 1;
}
// Helper function to convert Lua slot (1-indexed, relative) to absolute slot
uint32_t ConvertLuaSlot(int32_t bagIndex, uint32_t luaSlot) {
// Reverse the adjustments done in PushItemFoundResult/PushBagCGItemToTable
// luaSlot is 1-indexed, so subtract 1 first to get 0-indexed relative slot
uint32_t relativeSlot = luaSlot - 1;
// Add the offset for special bags to get absolute slot
if (bagIndex == 0) {
return relativeSlot + 0x17; // add 23 -> absolute slots 23-38
} else if (bagIndex == -1) {
return relativeSlot + 0x27; // add 39 -> absolute slots 39-62
} else if (bagIndex == -2) {
return relativeSlot + 0x51; // add 81 -> absolute slots 81-96
} else {
// Regular bags (1-4, 5-9): slots are already relative (0-indexed)
return relativeSlot;
}
}
uint32_t GetBagItem(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
// Check for required parameters
if (lua_gettop(luaState) < 2) {
lua_error(luaState, "Usage: GetBagItem(bagIndex, slot)");
return 0;
}
// Get bag index from first parameter
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Bag index must be a number");
return 0;
}
auto bagIndex = static_cast<int32_t>(lua_tonumber(luaState, 1));
// Get slot from second parameter
if (!lua_isnumber(luaState, 2)) {
lua_error(luaState, "Slot must be a number");
return 0;
}
auto luaSlot = static_cast<uint32_t>(lua_tonumber(luaState, 2));
auto slot = ConvertLuaSlot(bagIndex, luaSlot);
auto const getBagItem = reinterpret_cast<CGBag_C_GetItemAtSlotT>(Offsets::CGBag_C_GetItemAtSlot);
auto const getContainerGuid = reinterpret_cast<GetContainerGuidT>(Offsets::GetContainerGuid);
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto player = game::GetObjectPtr(playerGuid);
auto inventory = game::GetPlayerInventoryPtr(player);
game::CGItem_C *item = nullptr;
if (bagIndex == 0) {
// Bag 0: inventory pack slots 23-38 (Lua slots 1-16)
if (slot < 23 || slot > 38) {
lua_error(luaState, "Slot must be between 1 and 16 for bag 1");
return 0;
}
item = getBagItem(inventory, slot);
} else if (bagIndex >= 1 && bagIndex <= 4) {
// Bags 1-4: regular bags
uint64_t containerGuid = getContainerGuid(bagIndex-1); // bagIndex 1-4 maps to container 0-3
if (containerGuid == 0) {
lua_pushnil(luaState);
return 1;
}
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
if (!containerPtr) {
lua_pushnil(luaState);
return 1;
}
auto bagPtr = GetBagPtrFromContainer(containerPtr);
if (!bagPtr) {
lua_pushnil(luaState);
return 1;
}
auto bagSize = *bagPtr;
if (slot >= bagSize) {
lua_error(luaState, "Slot exceeds bag size");
return 0;
}
item = getBagItem(bagPtr, slot);
} else if (bagIndex == -1) {
// Bag -1: bank item slots 39-62 or buyback slots 69-80
uint64_t bankGuid = *reinterpret_cast<uint64_t *>(Offsets::BankGuid);
if (bankGuid == 0 && slot >= 39 && slot <= 62) {
lua_error(luaState, "Bank is not open");
return 0;
}
if ((slot >= 39 && slot <= 62) || (slot >= 69 && slot <= 80)) {
item = getBagItem(inventory, slot);
} else {
lua_error(luaState, "For bag -1, slot must be 1-24 (bank) or 31-42 (buyback) (Lua 1-indexed)");
return 0;
}
} else if (bagIndex >= 4 && bagIndex <= 8) {
// Bank bags 4-8
uint64_t bankGuid = *reinterpret_cast<uint64_t *>(Offsets::BankGuid);
if (bankGuid == 0) {
lua_error(luaState, "Bank is not open");
return 0;
}
uint64_t containerGuid = getContainerGuid(bagIndex);
if (containerGuid == 0) {
lua_pushnil(luaState);
return 1;
}
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
if (!containerPtr) {
lua_pushnil(luaState);
return 1;
}
auto bagPtr = GetBagPtrFromContainer(containerPtr);
if (!bagPtr) {
lua_pushnil(luaState);
return 1;
}
auto bagSize = *bagPtr;
if (slot >= bagSize) {
lua_error(luaState, "Slot exceeds bag size");
return 0;
}
item = getBagItem(bagPtr, slot);
} else if (bagIndex == -2) {
// Bag -2: keyring slots 81-96 (Lua slots 1-16)
if (slot < 81 || slot > 96) {
lua_error(luaState, "For bag -2 (keyring), slot must be 1-16 (Lua 1-indexed)");
return 0;
}
item = getBagItem(inventory, slot);
} else {
lua_error(luaState, "Invalid bag index. Valid values: 0, 1-4, -1, 5-9 (bank), -2 (keyring)");
return 0;
}
CreateItemInfoTable(luaState, item);
return 1;
}
uint32_t GetBagItems(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
auto const getContainerGuid = reinterpret_cast<GetContainerGuidT>(Offsets::GetContainerGuid);
auto const getBagItem = reinterpret_cast<CGBag_C_GetItemAtSlotT>(Offsets::CGBag_C_GetItemAtSlot);
// Create main result table: { [bagIndex] = { [slot] = itemInfo } }
lua_newtable(luaState);
// bag 0 is special
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto player = game::GetObjectPtr(playerGuid);
auto inventory = game::GetPlayerInventoryPtr(player);
// Push bag index 0 as key
lua_pushnumber(luaState, static_cast<double>(0));
// Create bag table for this bag
lua_newtable(luaState);
// look through inventory pack slots 23 -> 38
for (uint32_t slot = 23; slot <= 38; slot++) {
auto item = getBagItem(inventory, slot);
if (item) {
PushBagCGItemToTable(luaState, 0, slot, item);
}
}
// Set result[bagIndex] = bag table
lua_settable(luaState, -3);
// Process regular bags index 1-4
for (int32_t bagIndex = 1; bagIndex <= 4; bagIndex++) {
uint64_t containerGuid = getContainerGuid(bagIndex - 1); // bagIndex 1-4 maps to container 0-3
if (containerGuid == 0) continue;
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
if (!containerPtr) continue;
auto bagPtr = GetBagPtrFromContainer(containerPtr);
if (!bagPtr) continue;
// Push bag index as key
lua_pushnumber(luaState, static_cast<double>(bagIndex));
// Create bag table for this bag
lua_newtable(luaState);
auto bagSize = *bagPtr;
for (uint32_t slot = 0; slot < bagSize; slot++) {
auto item = getBagItem(bagPtr, slot);
if (item) {
PushBagCGItemToTable(luaState, bagIndex, slot, item);
}
}
// Set result[bagIndex] = bag table
lua_settable(luaState, -3);
}
// Check if bank is available
uint64_t bankGuid = *reinterpret_cast<uint64_t *>(Offsets::BankGuid);
if (bankGuid > 0) {
// Process bank bags 5-9
for (int32_t bagIndex = 5; bagIndex <= 9; bagIndex++) {
uint64_t containerGuid = getContainerGuid(bagIndex - 1); // bagIndex 5-9 maps to container 4-8
if (containerGuid == 0) continue;
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
if (!containerPtr) continue;
auto bagPtr = GetBagPtrFromContainer(containerPtr);
if (!bagPtr) continue;
// Push bag index as key
lua_pushnumber(luaState, static_cast<double>(bagIndex));
// Create bag table for this bank bag
lua_newtable(luaState);
auto bagSize = *bagPtr;
for (uint32_t slot = 0; slot < bagSize; slot++) {
auto item = getBagItem(bagPtr, slot);
if (item) {
PushBagCGItemToTable(luaState, bagIndex, slot, item);
}
}
// Set result[bagIndex] = bag table
lua_settable(luaState, -3);
}
}
return 1;
}
}
+6
View File
@@ -15,4 +15,10 @@ namespace Nampower {
bool LoadItem(uint32_t itemId); // Returns true if item needs async load (caller should wait)
bool ProcessItemExport(); // Process one item export per frame, returns true if still exporting
game::ItemStats_C* GetItemStats(uint32_t itemId);
uint32_t FindPlayerItemSlot(uintptr_t *luaState);
uint32_t GetEquippedItems(uintptr_t *luaState);
uint32_t GetEquippedItem(uintptr_t *luaState);
uint32_t GetBagItem(uintptr_t *luaState);
uint32_t GetBagItems(uintptr_t *luaState);
}
+25 -1
View File
@@ -61,6 +61,7 @@ namespace Nampower {
bool gForceQueueCast;
bool gNoQueueCast;
bool gQueuesProcessed;
bool lastCastUsedServerDelay;
@@ -415,7 +416,9 @@ namespace Nampower {
}
bool processQueues() {
if (!gCastData.channeling) {
if (!gCastData.channeling && !gQueuesProcessed) {
gQueuesProcessed = true;
// check for high priority script
if (RunQueuedScript(1)) {
// script ran, stop processing
@@ -496,6 +499,8 @@ namespace Nampower {
}
}
gQueuesProcessed = true;
// Process item export if active (low priority, runs when nothing else is queued)
// ProcessItemExport()
@@ -551,6 +556,8 @@ namespace Nampower {
// process any queued spells/scripts
processQueues();
gQueuesProcessed = false;
return iSceneEnd(ptr);
}
@@ -1274,6 +1281,7 @@ namespace Nampower {
char getItemILevel[] = "GetItemLevel";
RegisterLuaFunction(getItemILevel, reinterpret_cast<uintptr_t *>(Script_GetItemLevel));
// 2.14 additions
char getItemStats[] = "GetItemStats";
RegisterLuaFunction(getItemStats, reinterpret_cast<uintptr_t *>(Script_GetItemStats));
@@ -1294,6 +1302,22 @@ namespace Nampower {
char getSpellModifiers[] = "GetSpellModifiers";
RegisterLuaFunction(getSpellModifiers, reinterpret_cast<uintptr_t *>(Script_GetSpellModifiers));
// 2.16 additions
char findPlayerItemSlot[] = "FindPlayerItemSlot";
RegisterLuaFunction(findPlayerItemSlot, reinterpret_cast<uintptr_t *>(FindPlayerItemSlot));
char getEquippedItems[] = "GetEquippedItems";
RegisterLuaFunction(getEquippedItems, reinterpret_cast<uintptr_t *>(GetEquippedItems));
char getEquippedItem[] = "GetEquippedItem";
RegisterLuaFunction(getEquippedItem, reinterpret_cast<uintptr_t *>(GetEquippedItem));
char getBagItems[] = "GetBagItems";
RegisterLuaFunction(getBagItems, reinterpret_cast<uintptr_t *>(GetBagItems));
char getBagItem[] = "GetBagItem";
RegisterLuaFunction(getBagItem, reinterpret_cast<uintptr_t *>(GetBagItem));
}
std::once_flag loadFlag;
+9 -2
View File
@@ -30,8 +30,8 @@ namespace Nampower {
constexpr uint32_t BUFFER_DECREASE_FREQUENCY = 10000; // time in ms between changes to lower buffer
constexpr uint32_t MAJOR_VERSION = 2;
constexpr uint32_t MINOR_VERSION = 15;
constexpr uint32_t PATCH_VERSION = 1;
constexpr uint32_t MINOR_VERSION = 16;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
constexpr int32_t LUA_GLOBALSINDEX = -10001;
@@ -65,6 +65,7 @@ namespace Nampower {
extern CastQueue gCastHistory;
extern bool gScriptQueued;
extern bool gQueuesProcessed;
using RangeCheckSelectedT = bool (__fastcall *)(uintptr_t *playerUnit, const game::SpellRec *,
std::uint64_t targetGuid, char ignoreErrors);
@@ -129,6 +130,7 @@ namespace Nampower {
using lua_tonumberT = double (__fastcall *)(uintptr_t *, int);
using lua_pushnumberT = void (__fastcall *)(uintptr_t *, double);
using lua_pushstringT = void (__fastcall *)(uintptr_t *, char *);
using lua_pushbooleanT = void (__fastcall *)(uintptr_t *, bool);
using lua_pcallT = int (__fastcall *)(uintptr_t *, int nArgs, int nResults, int errFunction);
using lua_pushnilT = void (__fastcall *)(uintptr_t *);
using lua_errorT = void (__cdecl *)(uintptr_t *, const char *);
@@ -147,6 +149,11 @@ namespace Nampower {
using CGUnit_C_ClearCastingSpellT = void (__thiscall *)(uintptr_t *unit, uint32_t param_1, int param_2,
int param_3);
using CGUnit_C_ClearSpellEffectT = void (__thiscall *)(uintptr_t *unit, uint32_t param_1, int param_2);
using CGUnit_C_GetEquippedItemAtSlotT = game::CGItem * (__thiscall *)(uintptr_t *unit, uint32_t slot);
using CGBag_C_GetItemAtSlotT = game::CGItem_C * (__thiscall *)(uintptr_t *bag, uint32_t slot);
using CGUnit_C_GetBagT = uintptr_t * (__thiscall *)(uintptr_t *unit);
using CanInspectUnitT = bool (__fastcall *)(uintptr_t *unit);
using GetContainerGuidT = uint64_t (__fastcall *)(int32_t bagIndex);
using GetBuffByIndexT = uintptr_t *(__fastcall *)(int index);
+5
View File
@@ -51,6 +51,7 @@ enum class Offsets : std::uint32_t {
SpellNeedsTargets = 0X00CECAC0,
VisualSpellId = 0X00CEAC58,
CasterGuid = 0X00CEAC50,
BankGuid = 0X00BDD038,
GetSpellSlotAndType = 0X004B3950,
GetSpellSlotFromLua = 0X004B3EC0,
OsGetAsyncTimeMs = 0X0042B790,
@@ -181,6 +182,10 @@ enum class Offsets : std::uint32_t {
CGUnit_C_ClearCastingSpell = 0x0060d040,
CGUnit_C_ClearSpellEffect = 0x00614150,
CGUnit_C_GetEquippedItemAtSlot = 0x005f0d60,
CGBag_C_GetItemAtSlot = 0x006228a0,
GetContainerGuid = 0x004f93e0,
CanInspectUnit = 0x004944a0,
CGUnit_C_OnAuraRemoved = 0x00612320,
CGUnit_C_OnAuraAdded = 0x006123f0,
CGUnit_C_OnAuraAddedStack = 0x0062b800,
+3 -50
View File
@@ -11,22 +11,6 @@
#include <cstring>
namespace Nampower {
auto const lua_error = reinterpret_cast<lua_errorT>(Offsets::lua_error);
auto const lua_gettop = reinterpret_cast<lua_gettopT>(Offsets::lua_gettop);
auto const lua_isstring = reinterpret_cast<lua_isstringT>(Offsets::lua_isstring);
auto const lua_isnumber = reinterpret_cast<lua_isnumberT>(Offsets::lua_isnumber);
auto const lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
auto const lua_tonumber = reinterpret_cast<lua_tonumberT>(Offsets::lua_tonumber);
// Export Lua functions for dbc_fields.hpp templates
lua_pushnumberT lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
lua_pushstringT lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
lua_pushnilT lua_pushnil = reinterpret_cast<lua_pushnilT>(Offsets::lua_pushnil);
lua_newtableT lua_newtable = reinterpret_cast<lua_newtableT>(Offsets::lua_newtable);
lua_settableT lua_settable = reinterpret_cast<lua_settableT>(Offsets::lua_settable);
bool gScriptQueued;
int gScriptPriority = 1;
char *queuedScript;
@@ -112,14 +96,7 @@ namespace Nampower {
target = defaultTarget;
}
uint64_t targetGUID;
if (strncmp(target, "0x", 2) == 0 || strncmp(target, "0X", 2) == 0) {
// already a guid
targetGUID = std::stoull(target, nullptr, 16);
} else {
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
targetGUID = getGUIDFromName(target);
}
uint64_t targetGUID = GetUnitGuidFromString(target);
auto playerUnit = game::GetObjectPtr(game::ClntObjMgrGetActivePlayerGuid());
@@ -462,9 +439,6 @@ namespace Nampower {
uint32_t itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
// Load item if not cached
LoadItem(itemId);
// Get from cache
game::ItemStats_C *item = GetItemStats(itemId);
if (!item) {
@@ -550,9 +524,6 @@ namespace Nampower {
uint32_t itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
const char *fieldName = lua_tostring(luaState, 2);
// Load item if not cached
LoadItem(itemId);
// Get from cache
game::ItemStats_C *item = GetItemStats(itemId);
if (!item) {
@@ -753,16 +724,7 @@ namespace Nampower {
}
const char *unitToken = lua_tostring(luaState, 1);
uint64_t guid;
// Check if it's a GUID string (starts with "0x" or "0X")
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
guid = std::stoull(unitToken, nullptr, 16);
} else {
// Get GUID from unit token
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
guid = getGUIDFromName(unitToken);
}
uint64_t guid = GetUnitGuidFromString(unitToken);
if (guid == 0) {
lua_pushnil(luaState);
@@ -808,16 +770,7 @@ namespace Nampower {
const char *unitToken = lua_tostring(luaState, 1);
const char *fieldName = lua_tostring(luaState, 2);
uint64_t guid;
// Check if it's a GUID string (starts with "0x" or "0X")
if (strncmp(unitToken, "0x", 2) == 0 || strncmp(unitToken, "0X", 2) == 0) {
guid = std::stoull(unitToken, nullptr, 16);
} else {
// Get GUID from unit token
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
guid = getGUIDFromName(unitToken);
}
uint64_t guid = GetUnitGuidFromString(unitToken);
if (guid == 0) {
lua_pushnil(luaState);
+1 -1
View File
@@ -412,7 +412,7 @@ namespace Nampower {
if (gUserSettings.quickcastOnDoubleCast) {
auto currentTime = GetTime();
if (IsTargetingTerrainSpell() && spellId > 0) {
if (IsTargetingTerrainSpell()) {
if (gCastData.channeling || EffectiveCastEndMs() >= currentTime) {
// if we are already casting block action as that will interrupt
return;
+1 -2
View File
@@ -25,8 +25,7 @@ namespace Nampower {
auto const lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
auto const unitName = lua_tostring(luaState, 1);
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
auto const guid = getGUIDFromName(unitName);
auto const guid = GetUnitGuidFromString(unitName);
if (guid) {
DEBUG_LOG("Spell target unit " << unitName << " guid " << guid);
// update all cast params so we don't have to figure out which one to use