mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-12 04:47:37 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a51225cb84 | ||
|
|
6969fa77d1 | ||
|
|
74b3765683 | ||
|
|
10df3f11c2 | ||
|
|
e2d7b0f555 | ||
|
|
209a57a8d6 | ||
|
|
acb0456610 | ||
|
|
e47f3aacb2 | ||
|
|
d718530a7a | ||
|
|
55d4e1856f | ||
|
|
68771506a8 | ||
|
|
d0dc8c1965 |
@@ -45,3 +45,4 @@ jobs:
|
||||
ClassicAPI-Bundle-${{ github.ref_name }}.zip
|
||||
ClassicAPI-AddOnOnly-${{ github.ref_name }}.zip
|
||||
ClassicAPI-${{ github.ref_name }}/ClassicAPI.dll
|
||||
generate_release_notes: true
|
||||
|
||||
@@ -35,14 +35,15 @@ Full per-function reference: **[docs/API.md](docs/API.md)**.
|
||||
| [Hooks](docs/API.md#hooks) | `hooksecurefunc` |
|
||||
| [Input](docs/API.md#input) | `IsLeftAltKeyDown`, `IsLeftControlKeyDown`, `IsLeftShiftKeyDown`, `IsModifierKeyDown`, `IsRightAltKeyDown`, `IsRightControlKeyDown`, `IsRightShiftKeyDown` |
|
||||
| [Instance](docs/API.md#instance) | `GetInstanceInfo` |
|
||||
| [Item](docs/API.md#item) | `C_Item.DoesItemExist`, `C_Item.DoesItemExistByID`, `C_Item.EquipItemByName`, `C_Item.GetCurrentItemLevel`, `C_Item.GetDetailedItemLevelInfo`, `C_Item.GetItemCount`, `C_Item.GetItemFamily`, `C_Item.GetItemGUID`, `C_Item.GetItemIcon`, `C_Item.GetItemIconByID`, `C_Item.GetItemID`, `C_Item.GetItemInfoInstant`, `C_Item.GetItemInventoryType`, `C_Item.GetItemInventoryTypeByID`, `C_Item.GetItemLink`, `C_Item.GetItemLocation`, `C_Item.GetItemMaxStackSize`, `C_Item.GetItemMaxStackSizeByID`, `C_Item.GetItemName`, `C_Item.GetItemNameByID`, `C_Item.GetItemQuality`, `C_Item.GetItemQualityByID`, `C_Item.GetItemSellPrice`, `C_Item.GetItemSellPriceByID`, `C_Item.GetItemSetID`, `C_Item.GetItemSetIDByID`, `C_Item.GetItemSetInfo`, `C_Item.GetItemSpell`, `C_Item.GetWeaponEnchantInfo`, `C_Item.IsBound`, `C_Item.IsEquippableItem`, `C_Item.IsEquippedItem`, `C_Item.IsItemDataCached`, `C_Item.IsItemDataCachedByID`, `C_Item.IsLocked`, `C_Item.LockItem`, `C_Item.LockItemByGUID`, `C_Item.UnlockAllItems`, `C_Item.UnlockItem`, `C_Item.RequestLoadItemData`, `C_Item.RequestLoadItemDataByID`, `C_Item.UseItemByName`, `GetAuctionItemID`, `GetAuctionSellItemID`, `GetAverageItemLevel`, `GetCraftReagentItemID`, `GetInboxItemID`, `GetInventoryItemDurability`, `GetInventoryItemID`, `GetInventoryItemsForSlot`, `GetInventoryItemRepairCost`, `GetItemIcon`, `GetLootRollItemID`, `GetLootSlotItemID`, `GetMerchantItemID`, `GetQuestItemID`, `GetQuestLogItemID`, `GetTradePlayerItemID`, `GetTradeSkillItemID`, `GetTradeSkillReagentItemID`, `GetTradeTargetItemID`, `OffhandHasWeapon` |
|
||||
| [Item](docs/API.md#item) | `C_Item.DoesItemExist`, `C_Item.DoesItemExistByID`, `C_Item.EquipItemByName`, `C_Item.GetCurrentItemLevel`, `C_Item.GetDetailedItemLevelInfo`, `C_Item.GetItemCount`, `C_Item.GetItemFamily`, `C_Item.GetItemGUID`, `C_Item.GetItemIcon`, `C_Item.GetItemIconByID`, `C_Item.GetItemID`, `C_Item.GetItemInfoInstant`, `C_Item.GetItemInventoryType`, `C_Item.GetItemInventoryTypeByID`, `C_Item.GetItemLink`, `C_Item.GetItemLocation`, `C_Item.GetItemMaxStackSize`, `C_Item.GetItemMaxStackSizeByID`, `C_Item.GetItemName`, `C_Item.GetItemNameByID`, `C_Item.GetItemQuality`, `C_Item.GetItemQualityByID`, `C_Item.GetItemSellPrice`, `C_Item.GetItemSellPriceByID`, `C_Item.GetItemSetID`, `C_Item.GetItemSetIDByID`, `C_Item.GetItemSetInfo`, `C_Item.GetItemSpell`, `C_Item.GetItemUniqueness`, `C_Item.GetItemUniquenessByID`, `C_Item.GetStackCount`, `C_Item.GetWeaponEnchantInfo`, `C_Item.IsBound`, `C_Item.IsEquippableItem`, `C_Item.IsEquippedItem`, `C_Item.IsItemDataCached`, `C_Item.IsItemDataCachedByID`, `C_Item.IsLocked`, `C_Item.LockItem`, `C_Item.LockItemByGUID`, `C_Item.UnlockAllItems`, `C_Item.UnlockItem`, `C_Item.RequestLoadItemData`, `C_Item.RequestLoadItemDataByID`, `C_Item.UseItemByName`, `GetAuctionItemID`, `GetAuctionSellItemID`, `GetAverageItemLevel`, `GetCraftReagentItemID`, `GetInboxItemID`, `GetInventoryItemDurability`, `GetInventoryItemID`, `GetInventoryItemsForSlot`, `GetInventoryItemRepairCost`, `GetItemIcon`, `GetLootRollItemID`, `GetLootSlotItemID`, `GetMerchantItemID`, `GetQuestItemID`, `GetQuestLogItemID`, `GetTradePlayerItemID`, `GetTradeSkillItemID`, `GetTradeSkillReagentItemID`, `GetTradeTargetItemID`, `OffhandHasWeapon` |
|
||||
| [Macros](docs/API.md#macros) | `GetMacroSpell` |
|
||||
| [Map](docs/API.md#map) | `C_Map.GetBestMapForUnit` |
|
||||
| [MerchantFrame](docs/API.md#merchantframe) | `C_MerchantFrame.GetBuybackItemID`, `C_MerchantFrame.GetItemInfo`, `C_MerchantFrame.GetNumJunkItems`, `C_MerchantFrame.IsMerchantItemRefundable`, `C_MerchantFrame.IsSellAllJunkEnabled`, `C_MerchantFrame.SellAllJunkItems` |
|
||||
| [NameCache](docs/API.md#namecache) | `C_CreatureInfo.GetCreatureID`, `C_PlayerCache.GetPlayerInfoByName`, `C_PlayerCache.IsEnabled`, `C_PlayerCache.IsScanEnabled`, `C_PlayerCache.RememberPlayer`, `C_PlayerCache.SetEnabled`, `C_PlayerCache.SetScanEnabled`, `C_PlayerInfo.GUIDIsCreature`, `C_PlayerInfo.GUIDIsGameObject`, `C_PlayerInfo.GUIDIsPet`, `C_PlayerInfo.GUIDIsPlayer`, `GetPlayerInfoByGUID` |
|
||||
| [Quest](docs/API.md#quest) | `C_QuestLog.GetQuestIDForLogIndex`, `C_QuestLog.GetTitleForQuestID`, `C_QuestLog.IsOnQuest`, `C_QuestLog.IsQuestDataCachedByID`, `C_QuestLog.IsUnitOnQuest`, `C_QuestLog.RequestLoadQuestByID`, `GetQuestLogLeaderBoardID` |
|
||||
| [Spell](docs/API.md#spell) | `C_Spell.CancelSpellByID`, `C_Spell.GetSpellCooldown`, `C_Spell.GetSpellDescription`, `C_Spell.GetSpellInfo`, `C_Spell.GetSpellLink`, `C_Spell.GetSpellName`, `C_Spell.GetSpellReagents`, `C_Spell.GetSpellTexture`, `C_Spell.IsCurrentSpell`, `C_Spell.IsSpellHarmful`, `C_Spell.IsSpellHelpful`, `C_Spell.IsSpellPassive`, `C_Spell.IsSpellUsable`, `C_SpellBook.GetCurrentLevelSpells`, `C_SpellBook.GetSpellLevelLearned`, `C_SpellBook.GetSpellSkillLine`, `CancelSpellByName`, `CastSpellNoToggle`, `FindSpellBookSlotByID`, `GetCraftSpellID`, `GetSpellInfo`, `GetSpellLink`, `GetSpellSchool`, `IsHarmfulSpell`, `IsHelpfulSpell`, `IsPassiveSpell`, `IsPlayerSpell`, `IsSpellKnown`, `IsUsableSpell` |
|
||||
| [State](docs/API.md#state) | `CancelShapeshiftForm`, `Dismount`, `GetMirrorTimerInfo`, `GetMirrorTimerProgress`, `GetShapeshiftFormID`, `IsAssistingRitual`, `IsFalling`, `IsMounted`, `IsStealthed`, `IsSwimming` |
|
||||
| [Spell](docs/API.md#spell) | `C_Spell.CancelSpellByID`, `C_Spell.DoesSpellExist`, `C_Spell.GetSchoolString`, `C_Spell.GetSpellCooldown`, `C_Spell.GetSpellDescription`, `C_Spell.GetSpellInfo`, `C_Spell.GetSpellLink`, `C_Spell.GetSpellName`, `C_Spell.GetSpellReagents`, `C_Spell.GetSpellSubtext`, `C_Spell.GetSpellTexture`, `C_Spell.IsAutoAttackSpell`, `C_Spell.IsCurrentSpell`, `C_Spell.IsRangedAutoAttackSpell`, `C_Spell.IsSelfBuff`, `C_Spell.IsSpellHarmful`, `C_Spell.IsSpellHelpful`, `C_Spell.IsSpellPassive`, `C_Spell.IsSpellUsable`, `C_Spell.SpellHasRange`, `CancelSpellByName`, `CastSpellNoToggle`, `GetCraftSpellID`, `GetSpellInfo`, `GetSpellLink`, `GetSpellSchool`, `IsHarmfulSpell`, `IsHelpfulSpell`, `IsPassiveSpell`, `IsPlayerSpell`, `IsSpellKnown`, `IsUsableSpell`, `SpellHasRange` |
|
||||
| [SpellBook](docs/API.md#spellbook) | `C_SpellBook.GetCurrentLevelSpells`, `C_SpellBook.GetSpellLevelLearned`, `C_SpellBook.GetSpellSkillLine`, `C_SpellBook.IsAutoAttackSpellBookItem`, `C_SpellBook.IsRangedAutoAttackSpellBookItem`, `FindSpellBookSlotByID` |
|
||||
| [State](docs/API.md#state) | `CancelShapeshiftForm`, `Dismount`, `GetMirrorTimerInfo`, `GetMirrorTimerProgress`, `GetShapeshiftFormID`, `IsAssistingRitual`, `IsFalling`, `IsInGroup`, `IsInRaid`, `IsMounted`, `IsStealthed`, `IsSwimming` |
|
||||
| [Table](docs/API.md#table) | `table.wipe` |
|
||||
| [Talent](docs/API.md#talent) | `GetTalentIDByIndex`, `GetTalentSpellID` |
|
||||
| [Time](docs/API.md#time) | `C_DateAndTime.AdjustTimeByDays`, `C_DateAndTime.AdjustTimeByMinutes`, `C_DateAndTime.CompareCalendarTime`, `C_DateAndTime.GetCalendarTimeFromEpoch`, `C_DateAndTime.GetCurrentCalendarTime`, `C_DateAndTime.GetSecondsUntilDailyReset`, `C_DateAndTime.GetServerTimeLocal`, `C_Timer.After`, `C_Timer.NewTicker`, `C_Timer.NewTimer`, `GetServerTime` |
|
||||
|
||||
+372
-123
@@ -166,6 +166,8 @@ build instructions.
|
||||
- [`C_Item.GetCurrentItemLevel(itemLocation)` / `C_Item.GetDetailedItemLevelInfo(item)`](#c_itemgetcurrentitemlevelitemlocation--c_itemgetdetaileditemlevelinfoitem)
|
||||
- [`GetAverageItemLevel()`](#getaverageitemlevel)
|
||||
- [`C_Item.GetItemMaxStackSize(itemLocation)` / `C_Item.GetItemMaxStackSizeByID(item)`](#c_itemgetitemmaxstacksizeitemlocation--c_itemgetitemmaxstacksizebyiditem)
|
||||
- [`C_Item.GetStackCount(itemLocation)`](#c_itemgetstackcountitemlocation)
|
||||
- [`C_Item.GetItemUniqueness(itemLocation)` / `C_Item.GetItemUniquenessByID(item)`](#c_itemgetitemuniquenessitemlocation--c_itemgetitemuniquenessbyiditem)
|
||||
- [`C_Item.GetItemLink(itemLocation)`](#c_itemgetitemlinkitemlocation)
|
||||
- [`C_Item.GetItemInventoryType(itemLocation)` / `C_Item.GetItemInventoryTypeByID(item)`](#c_itemgetiteminventorytypeitemlocation--c_itemgetiteminventorytypebyiditem)
|
||||
- [`C_Item.IsLocked(itemLocation)`](#c_itemislockeditemlocation)
|
||||
@@ -214,15 +216,17 @@ build instructions.
|
||||
- [`GetQuestLogLeaderBoardID(objectiveIndex [, questIndex])`](#getquestlogleaderboardidobjectiveindex--questindex)
|
||||
|
||||
- [Spell](#spell)
|
||||
- [`C_Spell.DoesSpellExist(spellID)`](#c_spelldoesspellexistspellid)
|
||||
- [`C_Spell.GetSchoolString(schoolMask)`](#c_spellgetschoolstringschoolmask)
|
||||
- [`GetSpellInfo(spellID)` / `GetSpellInfo(slot, bookType)`](#getspellinfospellid--getspellinfoslot-booktype)
|
||||
- [`C_Spell.GetSpellInfo(spellID)`](#c_spellgetspellinfospellid)
|
||||
- [`C_Spell.GetSpellName(spellID)`](#c_spellgetspellnamespellid)
|
||||
- [`C_Spell.GetSpellTexture(spellID)`](#c_spellgetspelltexturespellid)
|
||||
- [`FindSpellBookSlotByID(spellID)`](#findspellbookslotbyidspellid)
|
||||
- [`GetSpellLink(spellID)` / `GetSpellLink(slot, bookType)`](#getspelllinkspellid--getspelllinkslot-booktype)
|
||||
- [`C_Spell.GetSpellLink(spellID)`](#c_spellgetspelllinkspellid)
|
||||
- [`C_Spell.GetSpellDescription(spellID)`](#c_spellgetspelldescriptionspellid)
|
||||
- [`C_Spell.GetSpellReagents(spellID)`](#c_spellgetspellreagentsspellid)
|
||||
- [`C_Spell.GetSpellSubtext(spellIdentifier)`](#c_spellgetspellsubtextspellidentifier)
|
||||
- [`IsPassiveSpell(spellID)` / `IsPassiveSpell(slot, bookType)`](#ispassivespellspellid--ispassivespellslot-booktype)
|
||||
- [`C_Spell.IsSpellPassive(spellID)`](#c_spellisspellpassivespellid)
|
||||
- [`IsPlayerSpell(spellID)`](#isplayerspellspellid)
|
||||
@@ -231,15 +235,24 @@ build instructions.
|
||||
- [`C_Spell.IsSpellUsable(spellID)`](#c_spellisspellusablespellid)
|
||||
- [`C_Spell.GetSpellCooldown(spellIdentifier)`](#c_spellgetspellcooldownspellidentifier)
|
||||
- [`C_Spell.IsCurrentSpell(spellIdentifier)`](#c_spelliscurrentspellspellidentifier)
|
||||
- [`C_Spell.IsSelfBuff(spellID)`](#c_spellisselfbuffspellid)
|
||||
- [`C_Spell.SpellHasRange(spellIdentifier)` / `SpellHasRange(slot, bookType)`](#c_spellspellhasrangespellidentifier--spellhasrangeslot-booktype)
|
||||
- [`C_Spell.IsAutoAttackSpell(spellID)`](#c_spellisautoattackspellspellid)
|
||||
- [`C_Spell.IsRangedAutoAttackSpell(spellID)`](#c_spellisrangedautoattackspellspellid)
|
||||
- [`IsHarmfulSpell(spell)` / `IsHelpfulSpell(spell)`](#isharmfulspellspell--ishelpfulspellspell)
|
||||
- [`C_Spell.IsSpellHarmful(spellID)` / `C_Spell.IsSpellHelpful(spellID)`](#c_spellisspellharmfulspellid--c_spellisspellhelpfulspellid)
|
||||
- [`C_SpellBook.GetSpellLevelLearned(spellID)`](#c_spellbookgetspelllevellearnedspellid)
|
||||
- [`C_SpellBook.GetCurrentLevelSpells([level])`](#c_spellbookgetcurrentlevelspellslevel)
|
||||
- [`C_SpellBook.GetSpellSkillLine(spellID)`](#c_spellbookgetspellskilllinespellid)
|
||||
- [`GetSpellSchool(spellID)`](#getspellschoolspellid)
|
||||
- [`CastSpellNoToggle(name | spellID)`](#castspellnotogglename--spellid)
|
||||
- [`C_Spell.CancelSpellByID(spellID)` / `CancelSpellByName(name)`](#c_spellcancelspellbyidspellid--cancelspellbynamename)
|
||||
|
||||
- [SpellBook](#spellbook)
|
||||
- [`FindSpellBookSlotByID(spellID)`](#findspellbookslotbyidspellid)
|
||||
- [`C_SpellBook.GetSpellLevelLearned(spellID)`](#c_spellbookgetspelllevellearnedspellid)
|
||||
- [`C_SpellBook.GetCurrentLevelSpells([level])`](#c_spellbookgetcurrentlevelspellslevel)
|
||||
- [`C_SpellBook.GetSpellSkillLine(spellID)`](#c_spellbookgetspellskilllinespellid)
|
||||
- [`C_SpellBook.IsAutoAttackSpellBookItem(slot, bookType)`](#c_spellbookisautoattackspellbookitemslot-booktype)
|
||||
- [`C_SpellBook.IsRangedAutoAttackSpellBookItem(slot, bookType)`](#c_spellbookisrangedautoattackspellbookitemslot-booktype)
|
||||
|
||||
- [State](#state)
|
||||
- [`IsMounted()`](#ismounted)
|
||||
- [`Dismount()`](#dismount)
|
||||
@@ -247,6 +260,8 @@ build instructions.
|
||||
- [`IsFalling()`](#isfalling)
|
||||
- [`IsSwimming()`](#isswimming)
|
||||
- [`IsAssistingRitual()`](#isassistingritual)
|
||||
- [`IsInGroup()`](#isingroup)
|
||||
- [`IsInRaid()`](#isinraid)
|
||||
- [`GetMirrorTimerInfo(index)` / `GetMirrorTimerProgress(label)`](#getmirrortimerinfoindex--getmirrortimerprogresslabel)
|
||||
- [`GetShapeshiftFormID()`](#getshapeshiftformid)
|
||||
- [`CancelShapeshiftForm()`](#cancelshapeshiftform)
|
||||
@@ -3698,6 +3713,50 @@ Single `uint32` read at cache record `+0x60` (`m_stackable`). By-ID
|
||||
form fires a background cache fill on miss and returns nil; re-call
|
||||
after `GET_ITEM_INFO_RECEIVED`.
|
||||
|
||||
### `C_Item.GetStackCount(itemLocation)`
|
||||
|
||||
Returns the **current** stack count in a specific slot — distinct
|
||||
from `C_Item.GetItemCount(item)`, which sums every stack of that
|
||||
itemID across the player's inventory. Useful when an addon needs to
|
||||
know "this specific stack has 13/20", not "I have 47 total".
|
||||
|
||||
```lua
|
||||
local n = C_Item.GetStackCount({bagID = 0, slotIndex = 1})
|
||||
-- 13 (the bag's first slot has 13 of whatever item)
|
||||
```
|
||||
|
||||
Reads `ITEM_FIELD_STACK_COUNT` directly off the item's
|
||||
`m_objectFields` — same field `GetContainerItemInfo` returns as
|
||||
`itemCount`. Returns `0` for empty / unresolvable locations.
|
||||
|
||||
### `C_Item.GetItemUniqueness(itemLocation)` / `C_Item.GetItemUniquenessByID(item)`
|
||||
|
||||
Returns `(maxAllowed, category, equippedCount)` — the item's
|
||||
uniqueness cap, category name, and current equipped-of-category
|
||||
count.
|
||||
|
||||
| Field | Vanilla source |
|
||||
|-------|----------------|
|
||||
| `maxAllowed` | `ItemStats_C.m_maxCount` — `0` = unlimited, `1` = unique ("Unique" tag in tooltip), higher = inventory cap |
|
||||
| `category` | Always `nil` — vanilla's client has no unique-equipped category system (added in TBC). |
|
||||
| `equippedCount` | Always `0` — without categories there's nothing meaningful to count against. |
|
||||
|
||||
```lua
|
||||
local max = C_Item.GetItemUniqueness({equipmentSlotIndex = 13})
|
||||
-- max = 1 for typical trinkets (Unique)
|
||||
local max = C_Item.GetItemUniquenessByID(2943)
|
||||
-- max = 1 (Eye of Paleth — "Unique")
|
||||
```
|
||||
|
||||
The by-ID form skips item-location resolution and reads `ItemStats_C`
|
||||
directly. Fires a background cache fill on miss and returns nil;
|
||||
re-call after `GET_ITEM_INFO_RECEIVED`.
|
||||
|
||||
Modern WoW returns the same 3-tuple but with the category half
|
||||
populated for unique-equipped categories (e.g. "Brewfest Mug",
|
||||
"Heart of Azeroth"). On this backport, only `maxAllowed` carries
|
||||
information.
|
||||
|
||||
### `C_Item.GetItemLink(itemLocation)`
|
||||
|
||||
Returns the fully-decorated per-instance hyperlink for the item at
|
||||
@@ -4856,6 +4915,50 @@ A separate function keeps the existing call wire-compatible.
|
||||
> `(slot, bookType)` overload — only the `C_Spell.*` namespace is
|
||||
> broadened.
|
||||
|
||||
### `C_Spell.DoesSpellExist(spellID)`
|
||||
|
||||
Returns `true` if `spellID` resolves to a populated `Spell.dbc`
|
||||
record, `false` otherwise. Cheap pre-flight check for any of the
|
||||
spellID-keyed `C_Spell.*` calls — if this returns `false`, the rest
|
||||
of the family will return nil/empty.
|
||||
|
||||
```lua
|
||||
if C_Spell.DoesSpellExist(133) then
|
||||
local info = C_Spell.GetSpellInfo(133) -- safe
|
||||
end
|
||||
```
|
||||
|
||||
### `C_Spell.GetSchoolString(schoolMask)`
|
||||
|
||||
Returns the localized name for a damage-school bitmask. Resolves
|
||||
single-bit masks via the engine's `SPELL_SCHOOL<n>_CAP` global
|
||||
strings (`SPELL_SCHOOL0_CAP` = "Physical", `SPELL_SCHOOL2_CAP` =
|
||||
"Fire", etc.).
|
||||
|
||||
```lua
|
||||
C_Spell.GetSchoolString(4) -- "Fire"
|
||||
C_Spell.GetSchoolString(32) -- "Shadow"
|
||||
```
|
||||
|
||||
Vanilla spells are single-school — the multi-school combos
|
||||
("Frostfire", "Shadowflame", "Spellstorm", etc.) were TBC-and-later
|
||||
additions. Any multi-bit mask returns `"Unknown"`, matching the
|
||||
modern API's documented fallback for unnamed combinations.
|
||||
|
||||
> Lua 5.0 doesn't support hex literals (`0x04` is a syntax error);
|
||||
> pass mask values as decimals, or via `tonumber("0x04", 16)` if you
|
||||
> want to keep hex notation in source.
|
||||
|
||||
| Decimal | Hex | School |
|
||||
|--------:|-----|--------|
|
||||
| `1` | `0x01` | Physical |
|
||||
| `2` | `0x02` | Holy |
|
||||
| `4` | `0x04` | Fire |
|
||||
| `8` | `0x08` | Nature |
|
||||
| `16` | `0x10` | Frost |
|
||||
| `32` | `0x20` | Shadow |
|
||||
| `64` | `0x40` | Arcane |
|
||||
|
||||
### `GetSpellInfo(spellID)` / `GetSpellInfo(slot, bookType)`
|
||||
|
||||
Returns the same nine values as 3.3.5's `GetSpellInfo`, **plus a 10th
|
||||
@@ -4966,36 +5069,6 @@ local path = C_Spell.GetSpellTexture(133)
|
||||
-- path = "Interface\\Icons\\Spell_Fire_FlameBolt"
|
||||
```
|
||||
|
||||
### `FindSpellBookSlotByID(spellID)`
|
||||
|
||||
Inverse of 1.12's `GetSpellName(slot, bookType)`. Given a spellID,
|
||||
returns the 1-based slot it occupies in the player or pet spellbook,
|
||||
along with the matching bookType so the result feeds directly into
|
||||
slot-and-bookType APIs (`GetSpellName`, `GetSpellTexture`,
|
||||
`GameTooltip:SetSpell`, etc.).
|
||||
|
||||
```
|
||||
slot, bookType = FindSpellBookSlotByID(spellID)
|
||||
```
|
||||
|
||||
- Returns `(slot, "spell")` if the spell is in the player spellbook.
|
||||
- Returns `(slot, "pet")` if it's only in the pet spellbook.
|
||||
- Returns `nil` if the spellID isn't currently in either book.
|
||||
|
||||
Player book is searched first, so if a spell somehow appeared in both
|
||||
(unusual but possible for special pet-shared abilities), the player
|
||||
slot wins.
|
||||
|
||||
```lua
|
||||
local slot, book = FindSpellBookSlotByID(133)
|
||||
if slot then
|
||||
GameTooltip:SetSpell(slot, book) -- shows full caster-scaled tooltip
|
||||
end
|
||||
```
|
||||
|
||||
Equivalent to the legacy function of the same name introduced in 3.0
|
||||
(later renamed to `FindSpellBookSlotBySpellID` in 5.x).
|
||||
|
||||
### `GetSpellLink(spellID)` / `GetSpellLink(slot, bookType)`
|
||||
|
||||
Returns the chat-style spell hyperlink and the spellID:
|
||||
@@ -5096,6 +5169,23 @@ documented in some emulator sources places these at `+0x110` /
|
||||
`+0x130`, which is wrong for 1.12.1). Iteration stops at the first
|
||||
empty slot, matching how the engine walks its own reagent loop.
|
||||
|
||||
### `C_Spell.GetSpellSubtext(spellIdentifier)`
|
||||
|
||||
Returns the localized "Rank N" / "Passive" / "Racial Passive" /
|
||||
similar string that appears below the spell's name in the
|
||||
spellbook. Read directly from `Spell.dbc.Rank[9]` at record `+0x204`
|
||||
(same locale array shape as `Name[9]` two fields prior).
|
||||
|
||||
Accepts the modern `SpellIdentifier` shape — `spellID`, name, name
|
||||
with rank (`"Fireball(Rank 3)"`), or `|Hspell:N|h` hyperlink. Returns
|
||||
`nil` for unrecognized input or spells with no rank text.
|
||||
|
||||
```lua
|
||||
C_Spell.GetSpellSubtext(133) -- "Rank 1" (lowest-rank Fireball)
|
||||
C_Spell.GetSpellSubtext(25306) -- "Rank 12" (max-rank Fireball)
|
||||
C_Spell.GetSpellSubtext("Frost Armor") -- "Rank 1"
|
||||
```
|
||||
|
||||
### `IsPassiveSpell(spellID)` / `IsPassiveSpell(slot, bookType)`
|
||||
|
||||
Returns `true` if the spell is passive (no cast bar — applies its effect
|
||||
@@ -5307,6 +5397,78 @@ on this. Reads three engine slots and returns true on any match:
|
||||
covers channeled abilities (Fishing, Drain Soul, Ritual of
|
||||
Summoning, etc.).
|
||||
|
||||
### `C_Spell.IsSelfBuff(spellID)`
|
||||
|
||||
Returns `true` if every active effect on the spell targets only the
|
||||
caster — every effect's implicit target (A and B) must be either
|
||||
`TARGET_NONE` (0) or `TARGET_SELF` (1).
|
||||
|
||||
```lua
|
||||
C_Spell.IsSelfBuff(1006) -- Inner Fire → true
|
||||
C_Spell.IsSelfBuff(7302) -- Frost Armor → true
|
||||
C_Spell.IsSelfBuff(1459) -- Arcane Intellect → false (targets friendly)
|
||||
C_Spell.IsSelfBuff(133) -- Fireball → false (targets enemy)
|
||||
```
|
||||
|
||||
Walks `Spell.dbc.EffectImplicitTargetA[3]` and
|
||||
`EffectImplicitTargetB[3]`. Spells with no active effects (a
|
||||
degenerate case for normal content) return `false`.
|
||||
|
||||
### `C_Spell.SpellHasRange(spellIdentifier)` / `SpellHasRange(slot, bookType)`
|
||||
|
||||
Returns `true` if the spell has a non-zero min or max range — i.e.
|
||||
it's a targeted ability you cast at a unit / location rather than a
|
||||
self-cast (which has range `0`).
|
||||
|
||||
```lua
|
||||
C_Spell.SpellHasRange(133) -- Fireball → true (40yd)
|
||||
C_Spell.SpellHasRange(1006) -- Inner Fire → false (self buff)
|
||||
SpellHasRange(10, "spell") -- 10th spellbook slot in player book
|
||||
```
|
||||
|
||||
Looks up `Spell.dbc.RangeIndex` (`+0x90`) → `SpellRange.dbc` row,
|
||||
then tests `minRange > 0 or maxRange > 0`. Vanilla 1.12 doesn't ship
|
||||
this function at all; 3.3.5+ added it as the `(spellIdentifier)`
|
||||
form only. We expose both the modern namespaced form (any
|
||||
`SpellIdentifier`) and a positional `SpellHasRange(slot, bookType)`
|
||||
matching the dual-signature shape used elsewhere in this backport
|
||||
(`GetSpellInfo`, `GetSpellLink`, etc.). The `bookType` argument
|
||||
follows the same convention as `GetSpellName(slot, bookType)`:
|
||||
`"spell"` (or any non-pet value) → player book, `"pet"` → pet book.
|
||||
|
||||
### `C_Spell.IsAutoAttackSpell(spellID)`
|
||||
|
||||
Returns `true` if the spell is the melee auto-attack — spell ID
|
||||
`6603` ("Auto Attack"), used by every class.
|
||||
|
||||
```lua
|
||||
C_Spell.IsAutoAttackSpell(6603) -- true
|
||||
C_Spell.IsAutoAttackSpell(133) -- false
|
||||
```
|
||||
|
||||
See [`C_SpellBook.IsAutoAttackSpellBookItem`](#c_spellbookisautoattackspellbookitemslot-booktype)
|
||||
for the spellbook-slot variant.
|
||||
|
||||
### `C_Spell.IsRangedAutoAttackSpell(spellID)`
|
||||
|
||||
Returns `true` if the spell is one of the two ranged auto-attacks —
|
||||
spell `75` (Hunter "Auto Shot") or spell `5019` ("Shoot" wand).
|
||||
|
||||
```lua
|
||||
C_Spell.IsRangedAutoAttackSpell(75) -- true (Auto Shot)
|
||||
C_Spell.IsRangedAutoAttackSpell(5019) -- true (Shoot wand)
|
||||
C_Spell.IsRangedAutoAttackSpell(6603) -- false (melee)
|
||||
```
|
||||
|
||||
Tests `Spell.dbc.Attributes & 0x02` (the `SPELL_ATTR_AUTO_REPEAT`
|
||||
flag) — verified empirically against in-game spell data. Auto Shot
|
||||
(`0x00050012`) and Shoot wand (`0x00000012`) share bit 1; Heroic
|
||||
Strike (`0x00050014`) and other ranged abilities don't. Naturally
|
||||
covers any future auto-repeating spell a private server might add.
|
||||
|
||||
See [`C_SpellBook.IsRangedAutoAttackSpellBookItem`](#c_spellbookisrangedautoattackspellbookitemslot-booktype)
|
||||
for the spellbook-slot variant.
|
||||
|
||||
### `C_Item.GetWeaponEnchantInfo()`
|
||||
|
||||
Returns the modern 12-tuple matching WotLK+'s extended
|
||||
@@ -5395,95 +5557,6 @@ C_Spell.IsSpellHelpful(2061) -- true (Flash Heal)
|
||||
Equivalent to `C_Spell.IsSpellHarmful` / `C_Spell.IsSpellHelpful`
|
||||
introduced in 11.x.
|
||||
|
||||
### `C_SpellBook.GetSpellLevelLearned(spellID)`
|
||||
|
||||
Returns the level at which a spell becomes available — the
|
||||
`BaseLevel` field in `Spell.dbc` (record `+0x70`). Direct read off
|
||||
the cached record; no class/race filtering.
|
||||
|
||||
```lua
|
||||
C_SpellBook.GetSpellLevelLearned(133) -- Fireball rank 1 → 4
|
||||
C_SpellBook.GetSpellLevelLearned(25306) -- Fireball rank 12 → 60
|
||||
C_SpellBook.GetSpellLevelLearned(2061) -- Flash Heal rank 1 → 20
|
||||
```
|
||||
|
||||
Returns `0` for invalid spellIDs, spells with no level requirement
|
||||
(most non-class utility spells), or records the engine hasn't
|
||||
cached. Matches modern semantics — unknown / utility spells
|
||||
return 0 rather than nil.
|
||||
|
||||
### `C_SpellBook.GetCurrentLevelSpells([level])`
|
||||
|
||||
Returns a 1-based table of spellIDs the player's class/race can
|
||||
learn at the given level. Without arguments, defaults to the
|
||||
player's current level.
|
||||
|
||||
```lua
|
||||
C_SpellBook.GetCurrentLevelSpells() -- spells trainable at your level
|
||||
C_SpellBook.GetCurrentLevelSpells(20) -- preview what's available at 20
|
||||
C_SpellBook.GetCurrentLevelSpells(60) -- preview at 60
|
||||
```
|
||||
|
||||
Walks `SkillLineAbility.dbc` filtering by the player's class bit
|
||||
(`1 << (classID - 1)`) and race bit, respecting both the include
|
||||
masks (entries with mask = 0 are "all classes"/"all races", which
|
||||
also pass) and the exclude masks. For each surviving entry, looks
|
||||
up the spell's `BaseLevel` and includes it if it matches the
|
||||
queried level.
|
||||
|
||||
> **Vanilla is trainer-driven.** Modern `GetCurrentLevelSpells`
|
||||
> (added in 5.x when trainers were removed) returns *auto-learned*
|
||||
> spells. Vanilla 1.12 requires visiting a trainer to actually
|
||||
> learn most class spells. We return the closest available analog:
|
||||
> **what's *trainable* at this level**. Useful for "what's new
|
||||
> this level" UI panels and level-preview tooling ported from MoP+.
|
||||
|
||||
Class/race come from the local player — there's no
|
||||
`(class, race, level)` form because vanilla doesn't expose a
|
||||
clean class-string→classID lookup. Returns an empty table at
|
||||
character select / pre-login (no CGPlayer yet) and for levels
|
||||
where no class/race spells match.
|
||||
|
||||
### `C_SpellBook.GetSpellSkillLine(spellID)`
|
||||
|
||||
Returns `(name, skillID)` — the SkillLine.dbc row that the given spell
|
||||
belongs to. The name is the user-facing skill/tab string in the engine's
|
||||
current locale: spellbook tab names for class spells (`"Protection"`,
|
||||
`"Fire"`, `"Holy"`), profession headers (`"Tailoring"`, `"Engineering"`),
|
||||
weapon-skill rows (`"Swords"`, `"Bows"`), etc.
|
||||
|
||||
```lua
|
||||
C_SpellBook.GetSpellSkillLine(1671) -- "Protection", 26 (Shield Bash)
|
||||
C_SpellBook.GetSpellSkillLine(133) -- "Fire", 8 (Fireball)
|
||||
C_SpellBook.GetSpellSkillLine(2050) -- "Holy", 56 (Lesser Heal)
|
||||
C_SpellBook.GetSpellSkillLine(2480) -- "Bows", 45 (Shoot Bow)
|
||||
C_SpellBook.GetSpellSkillLine(3273) -- "First Aid", 129 (Anesthetic)
|
||||
```
|
||||
|
||||
Walks `SkillLineAbility.dbc` for any row whose `spellID` matches, then
|
||||
reads the localized `Name[locale]` field at offset `+0x0C` of the
|
||||
referenced `SkillLine.dbc` record. For spells with multiple SLA rows
|
||||
(race-locked variants, Turtle WoW's faction-specific entries) the
|
||||
lookup prefers a row whose class/race masks match the local player —
|
||||
so the result reflects "what tab is this spell in *for me*" when one
|
||||
exists. Falls back to the first matching row otherwise, so the
|
||||
function still answers for spells the local player can't actually use
|
||||
(inspecting other classes, parsing combat-log entries from unfamiliar
|
||||
specs).
|
||||
|
||||
Returns `(nil, nil)` for:
|
||||
- non-numeric or non-positive input
|
||||
- spellIDs with no `SkillLineAbility.dbc` row (most temporary auras,
|
||||
proc-only spells, item-on-use effects, GM-debug spells)
|
||||
- rows whose `skillID` doesn't resolve in `SkillLine.dbc`
|
||||
|
||||
Vanilla's `GetSpellTabInfo(tabIndex)` enumerates spellbook tabs by
|
||||
1-based index — it can't answer "what tab is *this spellID* in".
|
||||
Addons that need a spellID→tab mapping have historically had to walk
|
||||
every tab's slot range and string-match against `GetSpellName`. This
|
||||
function reads it directly off the DBC for any spellID, including
|
||||
spells the player hasn't learned.
|
||||
|
||||
### `GetSpellSchool(spellID)`
|
||||
|
||||
Returns `(schoolID, schoolName)` for any spell — known to the player
|
||||
@@ -5622,6 +5695,155 @@ pass the plain name. Multi-rank buffs match on first-found; if you
|
||||
have multiple ranks of the same buff active (unusual but possible with
|
||||
e.g. paladin blessings before talent merge), the first slot wins.
|
||||
|
||||
## SpellBook
|
||||
|
||||
Functions keyed on the player or pet **spellbook** (slot indices,
|
||||
learn requirements, skill-tab membership). The underlying `Spell.dbc`
|
||||
data is exposed through the [Spell](#spell) section; what lives here
|
||||
is the spellbook-as-a-collection layer.
|
||||
|
||||
### `FindSpellBookSlotByID(spellID)`
|
||||
|
||||
Inverse of 1.12's `GetSpellName(slot, bookType)`. Given a spellID,
|
||||
returns the 1-based slot it occupies in the player or pet spellbook,
|
||||
along with the matching bookType so the result feeds directly into
|
||||
slot-and-bookType APIs (`GetSpellName`, `GetSpellTexture`,
|
||||
`GameTooltip:SetSpell`, etc.).
|
||||
|
||||
```
|
||||
slot, bookType = FindSpellBookSlotByID(spellID)
|
||||
```
|
||||
|
||||
- Returns `(slot, "spell")` if the spell is in the player spellbook.
|
||||
- Returns `(slot, "pet")` if it's only in the pet spellbook.
|
||||
- Returns `nil` if the spellID isn't currently in either book.
|
||||
|
||||
Player book is searched first, so if a spell somehow appeared in both
|
||||
(unusual but possible for special pet-shared abilities), the player
|
||||
slot wins.
|
||||
|
||||
```lua
|
||||
local slot, book = FindSpellBookSlotByID(133)
|
||||
if slot then
|
||||
GameTooltip:SetSpell(slot, book) -- shows full caster-scaled tooltip
|
||||
end
|
||||
```
|
||||
|
||||
Equivalent to the legacy function of the same name introduced in 3.0
|
||||
(later renamed to `FindSpellBookSlotBySpellID` in 5.x).
|
||||
|
||||
### `C_SpellBook.GetSpellLevelLearned(spellID)`
|
||||
|
||||
Returns the level at which a spell becomes available — the
|
||||
`BaseLevel` field in `Spell.dbc` (record `+0x70`). Direct read off
|
||||
the cached record; no class/race filtering.
|
||||
|
||||
```lua
|
||||
C_SpellBook.GetSpellLevelLearned(133) -- Fireball rank 1 → 4
|
||||
C_SpellBook.GetSpellLevelLearned(25306) -- Fireball rank 12 → 60
|
||||
C_SpellBook.GetSpellLevelLearned(2061) -- Flash Heal rank 1 → 20
|
||||
```
|
||||
|
||||
Returns `0` for invalid spellIDs, spells with no level requirement
|
||||
(most non-class utility spells), or records the engine hasn't
|
||||
cached. Matches modern semantics — unknown / utility spells
|
||||
return 0 rather than nil.
|
||||
|
||||
### `C_SpellBook.GetCurrentLevelSpells([level])`
|
||||
|
||||
Returns a 1-based table of spellIDs the player's class/race can
|
||||
learn at the given level. Without arguments, defaults to the
|
||||
player's current level.
|
||||
|
||||
```lua
|
||||
C_SpellBook.GetCurrentLevelSpells() -- spells trainable at your level
|
||||
C_SpellBook.GetCurrentLevelSpells(20) -- preview what's available at 20
|
||||
C_SpellBook.GetCurrentLevelSpells(60) -- preview at 60
|
||||
```
|
||||
|
||||
Walks `SkillLineAbility.dbc` filtering by the player's class bit
|
||||
(`1 << (classID - 1)`) and race bit, respecting both the include
|
||||
masks (entries with mask = 0 are "all classes"/"all races", which
|
||||
also pass) and the exclude masks. For each surviving entry, looks
|
||||
up the spell's `BaseLevel` and includes it if it matches the
|
||||
queried level.
|
||||
|
||||
> **Vanilla is trainer-driven.** Modern `GetCurrentLevelSpells`
|
||||
> (added in 5.x when trainers were removed) returns *auto-learned*
|
||||
> spells. Vanilla 1.12 requires visiting a trainer to actually
|
||||
> learn most class spells. We return the closest available analog:
|
||||
> **what's *trainable* at this level**. Useful for "what's new
|
||||
> this level" UI panels and level-preview tooling ported from MoP+.
|
||||
|
||||
Class/race come from the local player — there's no
|
||||
`(class, race, level)` form because vanilla doesn't expose a
|
||||
clean class-string→classID lookup. Returns an empty table at
|
||||
character select / pre-login (no CGPlayer yet) and for levels
|
||||
where no class/race spells match.
|
||||
|
||||
### `C_SpellBook.GetSpellSkillLine(spellID)`
|
||||
|
||||
Returns `(name, skillID)` — the SkillLine.dbc row that the given spell
|
||||
belongs to. The name is the user-facing skill/tab string in the engine's
|
||||
current locale: spellbook tab names for class spells (`"Protection"`,
|
||||
`"Fire"`, `"Holy"`), profession headers (`"Tailoring"`, `"Engineering"`),
|
||||
weapon-skill rows (`"Swords"`, `"Bows"`), etc.
|
||||
|
||||
```lua
|
||||
C_SpellBook.GetSpellSkillLine(1671) -- "Protection", 26 (Shield Bash)
|
||||
C_SpellBook.GetSpellSkillLine(133) -- "Fire", 8 (Fireball)
|
||||
C_SpellBook.GetSpellSkillLine(2050) -- "Holy", 56 (Lesser Heal)
|
||||
C_SpellBook.GetSpellSkillLine(2480) -- "Bows", 45 (Shoot Bow)
|
||||
C_SpellBook.GetSpellSkillLine(3273) -- "First Aid", 129 (Anesthetic)
|
||||
```
|
||||
|
||||
Walks `SkillLineAbility.dbc` for any row whose `spellID` matches, then
|
||||
reads the localized `Name[locale]` field at offset `+0x0C` of the
|
||||
referenced `SkillLine.dbc` record. For spells with multiple SLA rows
|
||||
(race-locked variants, Turtle WoW's faction-specific entries) the
|
||||
lookup prefers a row whose class/race masks match the local player —
|
||||
so the result reflects "what tab is this spell in *for me*" when one
|
||||
exists. Falls back to the first matching row otherwise, so the
|
||||
function still answers for spells the local player can't actually use
|
||||
(inspecting other classes, parsing combat-log entries from unfamiliar
|
||||
specs).
|
||||
|
||||
Returns `(nil, nil)` for:
|
||||
- non-numeric or non-positive input
|
||||
- spellIDs with no `SkillLineAbility.dbc` row (most temporary auras,
|
||||
proc-only spells, item-on-use effects, GM-debug spells)
|
||||
- rows whose `skillID` doesn't resolve in `SkillLine.dbc`
|
||||
|
||||
Vanilla's `GetSpellTabInfo(tabIndex)` enumerates spellbook tabs by
|
||||
1-based index — it can't answer "what tab is *this spellID* in".
|
||||
Addons that need a spellID→tab mapping have historically had to walk
|
||||
every tab's slot range and string-match against `GetSpellName`. This
|
||||
function reads it directly off the DBC for any spellID, including
|
||||
spells the player hasn't learned.
|
||||
|
||||
### `C_SpellBook.IsAutoAttackSpellBookItem(slot, bookType)`
|
||||
|
||||
Spellbook-slot variant of
|
||||
[`C_Spell.IsAutoAttackSpell`](#c_spellisautoattackspellspellid). Resolves
|
||||
the spellbook slot to its spellID first, then tests `== 6603` (melee
|
||||
auto-attack).
|
||||
|
||||
```lua
|
||||
C_SpellBook.IsAutoAttackSpellBookItem(1, "spell")
|
||||
```
|
||||
|
||||
### `C_SpellBook.IsRangedAutoAttackSpellBookItem(slot, bookType)`
|
||||
|
||||
Spellbook-slot variant of
|
||||
[`C_Spell.IsRangedAutoAttackSpell`](#c_spellisrangedautoattackspellspellid).
|
||||
Resolves the slot to a spellID, returns `true` if it's `75` (Auto
|
||||
Shot) or `5019` (Shoot wand).
|
||||
|
||||
```lua
|
||||
C_SpellBook.IsRangedAutoAttackSpellBookItem(10, "spell")
|
||||
-- true if slot 10 is your wand Shoot
|
||||
```
|
||||
|
||||
## State
|
||||
|
||||
Player movement / visibility state queries that modern WoW exposes
|
||||
@@ -5760,6 +5982,33 @@ object, not in the broadcast descriptor, so the function can't
|
||||
report state for `target` / `party*` / etc. Returns `false` when
|
||||
called before the player object is initialized (login screen).
|
||||
|
||||
### `IsInGroup()`
|
||||
|
||||
Returns `true` if the player is in a party or a raid, `false`
|
||||
otherwise. Modern shortcut over stock 1.12's
|
||||
`GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0`.
|
||||
|
||||
Accepts an optional `groupType` argument (modern
|
||||
`Enum.PartyCategory.Home` / `Instance`). Vanilla has no LFG / LFD
|
||||
instance-group concept, so the argument is accepted and ignored.
|
||||
|
||||
```lua
|
||||
if IsInGroup() then
|
||||
SendChatMessage("ready check", "PARTY")
|
||||
end
|
||||
```
|
||||
|
||||
### `IsInRaid()`
|
||||
|
||||
Returns `true` if the player is in a raid (specifically — parties
|
||||
return `false`), `false` otherwise. Same `groupType` argument story
|
||||
as `IsInGroup`.
|
||||
|
||||
```lua
|
||||
local channel = IsInRaid() and "RAID" or "PARTY"
|
||||
SendChatMessage("ready check", channel)
|
||||
```
|
||||
|
||||
### `GetMirrorTimerInfo(index)` / `GetMirrorTimerProgress(label)`
|
||||
|
||||
Modern (3.0+) readers for the BREATH (drowning) / EXHAUSTION
|
||||
|
||||
@@ -1082,6 +1082,14 @@ enum Offsets {
|
||||
ITEMSTATS_SPELL_SLOT_COUNT = 5,
|
||||
ITEM_SPELLTRIGGER_ON_USE = 0,
|
||||
|
||||
// `ItemStats_C.m_maxCount` — max number of this item the player can
|
||||
// hold in inventory at once. `0` = unlimited, `1` = unique, higher
|
||||
// values are caps like "5x bandage type". Vanilla 1.12 doesn't have
|
||||
// modern "unique-equipped category" semantics; this is the only
|
||||
// uniqueness signal the client has. Derived by summing struct
|
||||
// members in VanillaHelpers's `ItemStats_C` definition.
|
||||
OFF_ITEMSTATS_MAX_COUNT = 0x5C,
|
||||
|
||||
// `Spell.dbc` `Name[9]` field at record +0x1E0 (9-locale string
|
||||
// array, indexed by `[VAR_LOCALE_INDEX]`). Used by `GetItemSpell`
|
||||
// to push the spell name half of its return.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Group::Info {
|
||||
|
||||
namespace {
|
||||
|
||||
bool InRaid() {
|
||||
return *reinterpret_cast<const int *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_RAID_MEMBER_COUNT)) > 0;
|
||||
}
|
||||
|
||||
bool InParty() {
|
||||
auto *guids = reinterpret_cast<const uint64_t *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_PARTY_GUIDS));
|
||||
for (int i = 0; i < Offsets::PARTY_MAX_SLOTS; ++i) {
|
||||
if (guids[i] != 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// `IsInRaid()` — true iff the local player is in a raid group.
|
||||
// Modern signature takes an optional `groupType` (Home / Instance);
|
||||
// vanilla has no LFG/LFD instance-group concept, so we accept and
|
||||
// ignore the argument.
|
||||
static int __fastcall Script_IsInRaid(void *L) {
|
||||
Game::Lua::PushBool(L, InRaid());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// `IsInGroup()` — true iff the local player is in a party OR a raid.
|
||||
// Same `groupType` argument story as `IsInRaid`.
|
||||
static int __fastcall Script_IsInGroup(void *L) {
|
||||
Game::Lua::PushBool(L, InRaid() || InParty());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterGlobalFunction("IsInRaid", &Script_IsInRaid);
|
||||
Game::Lua::RegisterGlobalFunction("IsInGroup", &Script_IsInGroup);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Group::Info
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Item.GetStackCount(itemLocation)` — current stack count for the
|
||||
// specific item slot. Reads `ITEM_FIELD_STACK_COUNT` directly off the
|
||||
// CGItem's m_objectFields descriptor — same field
|
||||
// `Script_GetContainerItemInfo` returns as `itemCount`. Distinct from
|
||||
// `C_Item.GetItemCount(itemID)`, which sums every stack of `itemID`
|
||||
// across the player's inventory.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "item/Location.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Item::StackCount {
|
||||
|
||||
static int __fastcall Script_GetStackCount(void *L) {
|
||||
if (!Item::Location::IsLocationArg(L, 1)) {
|
||||
Game::Lua::PushNumber(L, 0.0);
|
||||
return 1;
|
||||
}
|
||||
auto *item = Item::Location::Resolve(L, 1);
|
||||
if (item == nullptr) {
|
||||
Game::Lua::PushNumber(L, 0.0);
|
||||
return 1;
|
||||
}
|
||||
auto *descriptor = *reinterpret_cast<const uint8_t *const *>(
|
||||
item + Offsets::OFF_ITEM_DESCRIPTOR);
|
||||
if (descriptor == nullptr) {
|
||||
Game::Lua::PushNumber(L, 0.0);
|
||||
return 1;
|
||||
}
|
||||
const int count = *reinterpret_cast<const int *>(
|
||||
descriptor + Offsets::OFF_DESCRIPTOR_STACK_COUNT);
|
||||
Game::Lua::PushNumber(L, static_cast<double>(count));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetStackCount",
|
||||
&Script_GetStackCount);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Item::StackCount
|
||||
@@ -0,0 +1,112 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Item.GetItemUniqueness(itemLocation)` — uniqueness cap. Reads
|
||||
// `ItemStats_C.m_maxCount` from the item's cached record; that's the
|
||||
// only uniqueness signal vanilla's client carries. `1` = unique
|
||||
// ("Held In Off-hand" trinkets, special quest items, etc.); `0` =
|
||||
// unlimited; higher values cap the inventory count.
|
||||
//
|
||||
// Modern WoW returns a 3-tuple `(maxEquipped, category, equippedCount)`
|
||||
// keyed off the post-TBC category system (e.g. "Unique-Equipped:
|
||||
// Brewfest Mug"). Vanilla has no category system, so we surface
|
||||
// `m_maxCount` as the first return and leave the category nil and
|
||||
// equipped count `0` — matching modern's "field present but inapplicable"
|
||||
// convention.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "item/Data.h"
|
||||
#include "item/ID.h"
|
||||
#include "item/Location.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Item::Uniqueness {
|
||||
|
||||
namespace {
|
||||
|
||||
using GetItemRecord_t = const uint8_t *(__thiscall *)(void *cache, uint32_t itemID,
|
||||
const uint64_t *guid,
|
||||
void *callback, void *userData,
|
||||
int unused);
|
||||
|
||||
const uint8_t *PeekItemRecord(uint32_t itemID) {
|
||||
auto fn = reinterpret_cast<GetItemRecord_t>(
|
||||
static_cast<uintptr_t>(Offsets::FUN_DBCACHE_ITEMSTATS_GET_RECORD));
|
||||
auto *cache = reinterpret_cast<void *>(
|
||||
static_cast<uintptr_t>(Offsets::VAR_ITEMDB_CACHE));
|
||||
const uint64_t zeroGuid = 0;
|
||||
return fn(cache, itemID, &zeroGuid, nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Pushes the 3-tuple `(maxAllowed, nil, 0)` derived from the
|
||||
// ItemStats_C record, or nil + early-return on cache miss.
|
||||
int PushFromItemID(void *L, uint32_t itemID) {
|
||||
const uint8_t *record = PeekItemRecord(itemID);
|
||||
if (record == nullptr) {
|
||||
Item::Data::WarmCache(itemID);
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
const int maxCount = *reinterpret_cast<const int *>(
|
||||
record + Offsets::OFF_ITEMSTATS_MAX_COUNT);
|
||||
Game::Lua::PushNumber(L, static_cast<double>(maxCount));
|
||||
Game::Lua::PushNil(L); // category (vanilla has none)
|
||||
Game::Lua::PushNumber(L, 0.0); // equippedCount (not tracked)
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int __fastcall Script_GetItemUniqueness(void *L) {
|
||||
if (!Item::Location::IsLocationArg(L, 1)) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
auto *item = Item::Location::Resolve(L, 1);
|
||||
if (item == nullptr) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
const int itemID = Item::ID::FromCGItem(item);
|
||||
if (itemID <= 0) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
return PushFromItemID(L, static_cast<uint32_t>(itemID));
|
||||
}
|
||||
|
||||
static int __fastcall Script_GetItemUniquenessByID(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
const int itemID = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
if (itemID <= 0) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
return PushFromItemID(L, static_cast<uint32_t>(itemID));
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemUniqueness",
|
||||
&Script_GetItemUniqueness);
|
||||
Game::Lua::RegisterTableFunction("C_Item", "GetItemUniquenessByID",
|
||||
&Script_GetItemUniquenessByID);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Item::Uniqueness
|
||||
@@ -0,0 +1,121 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Spell.IsAutoAttackSpell` / `C_Spell.IsRangedAutoAttackSpell` and
|
||||
// the spellbook-slot variants.
|
||||
//
|
||||
// Melee — there's only one spell that triggers melee swings (`6603` —
|
||||
// "Auto Attack"), and no `Spell.dbc` attribute uniquely identifies it
|
||||
// vs. on-next-swing abilities like Heroic Strike or Maul. We test
|
||||
// against the engine constant directly.
|
||||
//
|
||||
// Ranged — both vanilla ranged auto-attacks (`75` Auto Shot, `5019`
|
||||
// Shoot wand) set bit 1 of `Spell.dbc.Attributes` (the
|
||||
// `SPELL_ATTR_AUTO_REPEAT` flag at value `0x02`). Verified empirically
|
||||
// against the in-game cache: Attr `0x00050012` (Auto Shot) and
|
||||
// `0x00000012` (Shoot) share that bit; on-next-swing Heroic Strike
|
||||
// (`0x00050014`) and other ranged spells like Aimed Shot / Fireball do
|
||||
// NOT. The attribute check naturally extends to any future
|
||||
// auto-repeating spell a private server might add.
|
||||
|
||||
#include "Lookup.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "dbc/Lookup.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace Spell::AutoAttack {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int SPELL_AUTO_ATTACK = 6603;
|
||||
constexpr int OFF_SPELL_ATTRIBUTES = 0x18;
|
||||
constexpr uint32_t SPELL_ATTR_AUTO_REPEAT = 0x00000002;
|
||||
|
||||
bool IsMelee(int spellID) {
|
||||
return spellID == SPELL_AUTO_ATTACK;
|
||||
}
|
||||
|
||||
bool IsRanged(int spellID) {
|
||||
if (spellID <= 0)
|
||||
return false;
|
||||
const uint8_t *rec = DBC::Record(Offsets::VAR_SPELL_RECORDS,
|
||||
Offsets::VAR_SPELL_RECORD_COUNT,
|
||||
static_cast<uint32_t>(spellID));
|
||||
if (rec == nullptr)
|
||||
return false;
|
||||
const uint32_t attr = *reinterpret_cast<const uint32_t *>(
|
||||
rec + OFF_SPELL_ATTRIBUTES);
|
||||
return (attr & SPELL_ATTR_AUTO_REPEAT) != 0;
|
||||
}
|
||||
|
||||
int ReadSpellID(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1))
|
||||
return 0;
|
||||
return static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
}
|
||||
|
||||
int ReadSpellBookSlotID(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1))
|
||||
return 0;
|
||||
const int slot = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
int bookType = 0;
|
||||
if (Game::Lua::IsString(L, 2)) {
|
||||
const char *s = Game::Lua::ToString(L, 2);
|
||||
if (s != nullptr && _stricmp(s, "pet") == 0)
|
||||
bookType = 1;
|
||||
} else if (Game::Lua::IsNumber(L, 2)) {
|
||||
bookType = static_cast<int>(Game::Lua::ToNumber(L, 2));
|
||||
}
|
||||
return Spell::Lookup::SpellbookSlotToID(slot, bookType);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static int __fastcall Script_IsAutoAttackSpell(void *L) {
|
||||
Game::Lua::PushBool(L, IsMelee(ReadSpellID(L)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __fastcall Script_IsRangedAutoAttackSpell(void *L) {
|
||||
Game::Lua::PushBool(L, IsRanged(ReadSpellID(L)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __fastcall Script_IsAutoAttackSpellBookItem(void *L) {
|
||||
Game::Lua::PushBool(L, IsMelee(ReadSpellBookSlotID(L)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __fastcall Script_IsRangedAutoAttackSpellBookItem(void *L) {
|
||||
Game::Lua::PushBool(L, IsRanged(ReadSpellBookSlotID(L)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "IsAutoAttackSpell",
|
||||
&Script_IsAutoAttackSpell);
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "IsRangedAutoAttackSpell",
|
||||
&Script_IsRangedAutoAttackSpell);
|
||||
Game::Lua::RegisterTableFunction("C_SpellBook", "IsAutoAttackSpellBookItem",
|
||||
&Script_IsAutoAttackSpellBookItem);
|
||||
Game::Lua::RegisterTableFunction("C_SpellBook", "IsRangedAutoAttackSpellBookItem",
|
||||
&Script_IsRangedAutoAttackSpellBookItem);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Spell::AutoAttack
|
||||
@@ -0,0 +1,51 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Spell.DoesSpellExist(spellID)` — true iff the spell ID resolves
|
||||
// to a populated `Spell.dbc` record. Cheap existence probe useful
|
||||
// before calling other `C_Spell.*` functions that might OOB-crash or
|
||||
// return nil/empty for missing spells.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "dbc/Lookup.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Spell::Exists {
|
||||
|
||||
static int __fastcall Script_DoesSpellExist(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
const int spellID = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
if (spellID <= 0) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
const uint8_t *rec = DBC::Record(Offsets::VAR_SPELL_RECORDS,
|
||||
Offsets::VAR_SPELL_RECORD_COUNT,
|
||||
static_cast<uint32_t>(spellID));
|
||||
Game::Lua::PushBool(L, rec != nullptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "DoesSpellExist",
|
||||
&Script_DoesSpellExist);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Spell::Exists
|
||||
@@ -0,0 +1,110 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Spell.SpellHasRange(spellIdentifier)` and the parallel
|
||||
// `SpellHasRange(slot, bookType)` — true iff the spell's
|
||||
// `SpellRange.dbc` row has a non-zero min or max range.
|
||||
//
|
||||
// Vanilla 1.12 doesn't ship `SpellHasRange` as a global at all; 3.3.5+
|
||||
// added it but with the spellID-only signature. We provide the modern
|
||||
// namespaced shape *and* a `(slot, bookType)` positional form mirroring
|
||||
// the other dual-signature globals (`GetSpellInfo`, `GetSpellLink`,
|
||||
// etc.) so callers can use whichever they already have.
|
||||
|
||||
#include "Arg.h"
|
||||
#include "Lookup.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "dbc/Lookup.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace Spell::HasRange {
|
||||
|
||||
namespace {
|
||||
|
||||
// Spell.dbc RangeIndex field — mirrors `Script_GetSpellInfo`'s
|
||||
// local constant. Future refactor: promote to `Offsets.h`.
|
||||
constexpr int OFF_SPELL_RANGE_INDEX = 0x90;
|
||||
|
||||
// SpellRange.dbc record layout (verified, documented in CLAUDE.md
|
||||
// under "Sub-DBC record layouts"):
|
||||
// +0x00 id, +0x04 minRange (float), +0x08 maxRange (float), ...
|
||||
constexpr int OFF_SPELLRANGE_MIN = 0x04;
|
||||
constexpr int OFF_SPELLRANGE_MAX = 0x08;
|
||||
|
||||
bool SpellIDHasRange(int spellID) {
|
||||
if (spellID <= 0)
|
||||
return false;
|
||||
const uint8_t *spellRec = DBC::Record(Offsets::VAR_SPELL_RECORDS,
|
||||
Offsets::VAR_SPELL_RECORD_COUNT,
|
||||
static_cast<uint32_t>(spellID));
|
||||
if (spellRec == nullptr)
|
||||
return false;
|
||||
const uint32_t rangeIdx = *reinterpret_cast<const uint32_t *>(
|
||||
spellRec + OFF_SPELL_RANGE_INDEX);
|
||||
if (rangeIdx == 0)
|
||||
return false;
|
||||
const uint8_t *rangeRec = DBC::Record(Offsets::VAR_SPELL_RANGE_RECORDS,
|
||||
Offsets::VAR_SPELL_RANGE_COUNT,
|
||||
rangeIdx);
|
||||
if (rangeRec == nullptr)
|
||||
return false;
|
||||
const float minRange = *reinterpret_cast<const float *>(
|
||||
rangeRec + OFF_SPELLRANGE_MIN);
|
||||
const float maxRange = *reinterpret_cast<const float *>(
|
||||
rangeRec + OFF_SPELLRANGE_MAX);
|
||||
return minRange > 0.0f || maxRange > 0.0f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static int __fastcall Script_C_Spell_SpellHasRange(void *L) {
|
||||
const int spellID = Spell::Arg::ResolveSpellID(L, 1);
|
||||
Game::Lua::PushBool(L, SpellIDHasRange(spellID));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// `SpellHasRange(slot, bookType)` — vanilla-style positional shape.
|
||||
// `bookType` is the same string convention used by `GetSpellName`:
|
||||
// `"spell"` (or omitted / anything-not-pet) → player book,
|
||||
// `"pet"` → pet book.
|
||||
static int __fastcall Script_SpellHasRange(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
const int slot = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
int bookType = 0;
|
||||
if (Game::Lua::IsString(L, 2)) {
|
||||
const char *s = Game::Lua::ToString(L, 2);
|
||||
if (s != nullptr && _stricmp(s, "pet") == 0)
|
||||
bookType = 1;
|
||||
}
|
||||
const int spellID = Spell::Lookup::SpellbookSlotToID(slot, bookType);
|
||||
Game::Lua::PushBool(L, SpellIDHasRange(spellID));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "SpellHasRange",
|
||||
&Script_C_Spell_SpellHasRange);
|
||||
Game::Lua::RegisterGlobalFunction("SpellHasRange",
|
||||
&Script_SpellHasRange);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Spell::HasRange
|
||||
@@ -0,0 +1,94 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Spell.IsSelfBuff(spellID)` — true iff every active effect on the
|
||||
// spell targets the caster and only the caster. Read directly from
|
||||
// `Spell.dbc.EffectImplicitTargetA[3]` and `EffectImplicitTargetB[3]`
|
||||
// — an effect is "self-only" when both targets are either
|
||||
// `TARGET_NONE` (0) or `TARGET_SELF` (1). Effects with a zero
|
||||
// `Effect` code are skipped (unused effect slots).
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "dbc/Lookup.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Spell::IsSelfBuff {
|
||||
|
||||
namespace {
|
||||
|
||||
// Spell.dbc per-effect arrays. Each is an int32[3] in the record.
|
||||
// Field-index derivation against the known `EffectApplyAuraName` at
|
||||
// field 91 / +0x16C — the implicit-target pair sits at field 82/85.
|
||||
constexpr int OFF_SPELL_EFFECT = 0xF4; // SPELL_EFFECT_* code
|
||||
constexpr int OFF_SPELL_EFFECT_TARGET_A = 0x148;
|
||||
constexpr int OFF_SPELL_EFFECT_TARGET_B = 0x154;
|
||||
constexpr int EFFECT_COUNT = 3;
|
||||
|
||||
// Vanilla 1.12 implicit-target codes (CMaNGOS `Targets` enum).
|
||||
constexpr int TARGET_NONE = 0;
|
||||
constexpr int TARGET_SELF = 1;
|
||||
|
||||
bool IsSelfOnlyTarget(int target) {
|
||||
return target == TARGET_NONE || target == TARGET_SELF;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static int __fastcall Script_IsSelfBuff(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
const int spellID = static_cast<int>(Game::Lua::ToNumber(L, 1));
|
||||
if (spellID <= 0) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
const uint8_t *rec = DBC::Record(Offsets::VAR_SPELL_RECORDS,
|
||||
Offsets::VAR_SPELL_RECORD_COUNT,
|
||||
static_cast<uint32_t>(spellID));
|
||||
if (rec == nullptr) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool sawEffect = false;
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||
const int effect = *reinterpret_cast<const int *>(
|
||||
rec + OFF_SPELL_EFFECT + i * 4);
|
||||
if (effect == 0)
|
||||
continue;
|
||||
sawEffect = true;
|
||||
const int targetA = *reinterpret_cast<const int *>(
|
||||
rec + OFF_SPELL_EFFECT_TARGET_A + i * 4);
|
||||
const int targetB = *reinterpret_cast<const int *>(
|
||||
rec + OFF_SPELL_EFFECT_TARGET_B + i * 4);
|
||||
if (!IsSelfOnlyTarget(targetA) || !IsSelfOnlyTarget(targetB)) {
|
||||
Game::Lua::PushBool(L, false);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Game::Lua::PushBool(L, sawEffect);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "IsSelfBuff",
|
||||
&Script_IsSelfBuff);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Spell::IsSelfBuff
|
||||
@@ -0,0 +1,70 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Spell.GetSchoolString(schoolMask)` — localized name for a damage
|
||||
// school bitmask. Vanilla spells are single-school (the multi-school
|
||||
// combos like "Frostfire" / "Shadowflame" / "Spellstorm" were
|
||||
// TBC-and-later additions), so we resolve any single-bit mask to its
|
||||
// `SPELL_SCHOOL<n>_CAP` global string and return "Unknown" for any
|
||||
// combination — matching the modern API's documented fallback.
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
namespace Spell::SchoolString {
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns the 0-based school index for a single-bit mask, or -1 if
|
||||
// the mask has zero or multiple bits set (or is out of the 7-school
|
||||
// range vanilla supports).
|
||||
int SingleBitSchoolIndex(uint32_t mask) {
|
||||
if (mask == 0 || (mask & (mask - 1)) != 0)
|
||||
return -1;
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
if (mask == (1u << i))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static int __fastcall Script_GetSchoolString(void *L) {
|
||||
if (!Game::Lua::IsNumber(L, 1)) {
|
||||
Game::Lua::PushString(L, "Unknown");
|
||||
return 1;
|
||||
}
|
||||
const auto mask = static_cast<uint32_t>(Game::Lua::ToNumber(L, 1));
|
||||
const int idx = SingleBitSchoolIndex(mask);
|
||||
if (idx < 0) {
|
||||
Game::Lua::PushString(L, "Unknown");
|
||||
return 1;
|
||||
}
|
||||
char globalName[24];
|
||||
std::snprintf(globalName, sizeof globalName, "SPELL_SCHOOL%d_CAP", idx);
|
||||
Game::Lua::PushLocalizedString(L, globalName, "Unknown");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "GetSchoolString",
|
||||
&Script_GetSchoolString);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Spell::SchoolString
|
||||
@@ -0,0 +1,65 @@
|
||||
// This file is part of ClassicAPI.
|
||||
//
|
||||
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
|
||||
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License along with
|
||||
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// `C_Spell.GetSpellSubtext(spellIdentifier)` — the localized "Rank N"
|
||||
// / "Passive" / "Racial Passive" string from `Spell.dbc.Rank[9]`.
|
||||
// String-keyed input resolves by name through the spellbook (modern
|
||||
// `SpellIdentifier` semantics); numeric input is a direct spellID
|
||||
// lookup.
|
||||
|
||||
#include "Arg.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Offsets.h"
|
||||
#include "dbc/Lookup.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Spell::Subtext {
|
||||
|
||||
namespace {
|
||||
|
||||
// `Spell.dbc.Rank[9]` — same 9-slot locale-string array shape as the
|
||||
// adjacent `Name[9]` field. Defined locally; sibling modules
|
||||
// (`Info.cpp`, `Tooltip.cpp`, `macro/Spell.cpp`) carry the same
|
||||
// constant — promoting to `Offsets.h` is a future refactor.
|
||||
constexpr int OFF_SPELL_RANK = 0x204;
|
||||
|
||||
} // namespace
|
||||
|
||||
static int __fastcall Script_GetSpellSubtext(void *L) {
|
||||
const int spellID = Spell::Arg::ResolveSpellID(L, 1);
|
||||
if (spellID <= 0) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
const char *subtext = DBC::LocalizedField(Offsets::VAR_SPELL_RECORDS,
|
||||
Offsets::VAR_SPELL_RECORD_COUNT,
|
||||
static_cast<uint32_t>(spellID),
|
||||
OFF_SPELL_RANK);
|
||||
if (subtext == nullptr) {
|
||||
Game::Lua::PushNil(L);
|
||||
return 1;
|
||||
}
|
||||
Game::Lua::PushString(L, subtext);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RegisterLuaFunctions() {
|
||||
Game::Lua::RegisterTableFunction("C_Spell", "GetSpellSubtext",
|
||||
&Script_GetSpellSubtext);
|
||||
}
|
||||
|
||||
static const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
|
||||
|
||||
} // namespace Spell::Subtext
|
||||
Reference in New Issue
Block a user