From ef8481f37d29580179385d6265147e4f69d9e9de Mon Sep 17 00:00:00 2001 From: jrc13245 Date: Mon, 19 Jan 2026 12:07:56 -0500 Subject: [PATCH] fix getactioncooldown hook for blizzard ui users --- Core.lua | 3 +- nampower_wiki.txt | 1833 +++++++++++++++++++++++++++++++++++++++++++ superwow_wiki.txt | 70 ++ unitxp_sp3_wiki.txt | 189 +++++ 4 files changed, 2094 insertions(+), 1 deletion(-) create mode 100644 nampower_wiki.txt create mode 100644 superwow_wiki.txt create mode 100644 unitxp_sp3_wiki.txt diff --git a/Core.lua b/Core.lua index 7388b0f..9a6503e 100644 --- a/Core.lua +++ b/Core.lua @@ -4098,7 +4098,8 @@ function GetActionCooldown(slot) return GetInventoryItemCooldown("player", a.item.inventoryID) end end - return 0, 0, 0 + -- Fallback to original function (preserves SuperWoW's #showtooltip handling) + return CleveRoids.Hooks.GetActionCooldown(slot) else return CleveRoids.Hooks.GetActionCooldown(slot) end diff --git a/nampower_wiki.txt b/nampower_wiki.txt new file mode 100644 index 0000000..ac79f0f --- /dev/null +++ b/nampower_wiki.txt @@ -0,0 +1,1833 @@ +DBC Field Names + +Related structs live in nampower/game.hpp (notably SpellRec and ItemStats_C). +ItemStats_C Fields (alphabetical) + + allowableClass + allowableRace + ammoType + area + bagFamily + block + bonding + buyPrice + class + containerSlots + delay + displayInfoID + duration + flags + inventoryType + itemLevel + itemSet + languageID + lockID + map + material + maxCount + maxDurability + pageMaterial + pageText + quality + randomProperty + rangedModRange + requiredCityRank + requiredHonorRank + requiredLevel + requiredRep + requiredRepRank + requiredSkill + requiredSkillRank + requiredSpell + sellPrice + sheatheType + stackable + startQuestID + subclass + +ItemStats_C Array Fields (alphabetical) + + bonusAmount + bonusStat + damageType + maxDamage + minDamage + resistances + spellCategory + spellCategoryCooldown + spellCharges + spellCooldown + spellID + spellTrigger + +SpellRec Fields (alphabetical) + + activeIconID + attributes + attributesEx + attributesEx2 + attributesEx3 + attributesEx4 + auraInterruptFlags + baseLevel + castUI + category + categoryRecoveryTime + casterAuraState + castingTimeIndex + channelInterruptFlags + dispel + dmgClass + durationIndex + equippedItemClass + equippedItemInventoryTypeMask + equippedItemSubClassMask + id + interruptFlags + manaCost + manaCostPercentage + manaCostPerlevel + manaPerSecond + manaPerSecondPerLevel + maxAffectedTargets + maxLevel + maxTargetLevel + mechanic + minFactionId + minReputation + modalNextSpell + powerType + preventionType + procChance + procCharges + procFlags + rangeIndex + recoveryTime + requiredAuraVision + school + spellFamilyFlags + spellFamilyName + spellIconID + spellLevel + spellPriority + spellVisual + spellVisual2 + stackAmount + stances + stancesNot + startRecoveryCategory + startRecoveryTime + targetAuraState + targetCreatureType + targets + +SpellRec Array Fields (alphabetical) + + dmgMultiplier + effect + effectAmplitude + effectApplyAuraName + effectBaseDice + effectBasePoints + effectChainTarget + effectDicePerLevel + effectDieSides + effectImplicitTargetA + effectImplicitTargetB + effectItemType + effectMechanic + effectMiscValue + effectMultipleValue + effectPointsPerComboPoint + effectRadiusIndex + effectRealPointsPerLevel + effectTriggerSpell + reagent + reagentCount + totem + +Nampower Custom Events + +This document describes all custom events added by Nampower that you can register and listen to in your addons. + +For custom Lua functions, see SCRIPTS.md. For general usage information, see README.md. +Table of Contents + + SPELL_QUEUE_EVENT + SPELL_CAST_EVENT + SPELL_START_SELF and SPELL_START_OTHER + SPELL_GO_SELF and SPELL_GO_OTHER + SPELL_FAILED_SELF and SPELL_FAILED_OTHER + SPELL_DELAYED_SELF and SPELL_DELAYED_OTHER + SPELL_CHANNEL_START and SPELL_CHANNEL_UPDATE + SPELL_DAMAGE_EVENT_SELF and SPELL_DAMAGE_EVENT_OTHER + Buff/Debuff Events + AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER + AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER + UNIT_DIED + +Custom Events +SPELL_QUEUE_EVENT + +I've added a new event you can register in game to get updates when spells are added and popped from the queue. + +The event is SPELL_QUEUE_EVENT and has 2 parameters: + + int eventCode - see below + int spellId + +Possible Event codes: + + ON_SWING_QUEUED = 0 + ON_SWING_QUEUE_POPPED = 1 + NORMAL_QUEUED = 2 + NORMAL_QUEUE_POPPED = 3 + NON_GCD_QUEUED = 4 + NON_GCD_QUEUE_POPPED = 5 + +Example from NampowerSettings: + +local ON_SWING_QUEUED = 0 +local ON_SWING_QUEUE_POPPED = 1 +local NORMAL_QUEUED = 2 +local NORMAL_QUEUE_POPPED = 3 +local NON_GCD_QUEUED = 4 +local NON_GCD_QUEUE_POPPED = 5 + +local function spellQueueEvent(eventCode, spellId) + if eventCode == NORMAL_QUEUED or eventCode == NON_GCD_QUEUED then + local _, _, texture = SpellInfo(spellId) -- superwow function + Nampower.queued_spell.texture:SetTexture(texture) + Nampower.queued_spell:Show() + elseif eventCode == NORMAL_QUEUE_POPPED or eventCode == NON_GCD_QUEUE_POPPED then + Nampower.queued_spell:Hide() + end +end + +NampowerSettings:RegisterEvent("SPELL_QUEUE_EVENT", spellQueueEvent) + +SPELL_CAST_EVENT + +Event you can register in game to get updates when you cast spells with some additional information. This will only fire for spells you (and certain pets) initiated. Triggered when you start casting a spell in the client before it is sent to the server. + +The event is SPELL_CAST_EVENT and has 5 parameters: + + int success - 1 if cast succeeded, 0 if failed + int spellId + int castType - see below + string targetGuid - guid string like "0xF5300000000000A5" + int itemId - the id of the item that triggered the spell, 0 if it wasn't triggered by an item + +Possible Cast Types: + +NORMAL=1 +NON_GCD=2 +ON_SWING=3 +CHANNEL=4 +TARGETING=5 (targeting is the term I used for spells with terrain targeting) +TARGETING_NON_GCD=6 + +targetGuid will be "0x000000000" unless an explicit target is specified which currently only happens in 2 circumstances: + + It was specified as the 2nd param of CastSpellByName (added by superwow) + Mouseover casts that use SpellTargetUnit to specify a target + +Example (uses ace RegisterEvent): + +Cursive:RegisterEvent("SPELL_CAST_EVENT", function(success, spellId, castType, targetGuid, itemId) + print(success) + print(spellId) + print(castType) + print(targetGuid) + print(itemId) +end); + +SPELL_START_SELF and SPELL_START_OTHER + +Fire when a spell start packet is received. "Self" fires when the active player is the caster, "Other" fires for any other caster. Triggered by the server to notify that a spell with a cast time has begun. + +These events are gated behind the NP_EnableSpellStartEvents CVar (default 0). Set it to 1 to enable. + +Parameters: + + int itemId - the id of the item that triggered the spell, 0 if it wasn't triggered by an item + int spellId + string casterGuid - caster guid like "0xF5300000000000A5" + string targetGuid - target guid like "0xF5300000000000A5" or "0x0000000000000000" if none + int castFlags + int castTime - cast time in milliseconds + +SPELL_GO_SELF and SPELL_GO_OTHER + +Fire when a spell go packet is received. "Self" fires when the active player is the caster, "Other" fires for any other caster. Triggered by the server to indicate a spell completed casting. + +These events are gated behind the NP_EnableSpellGoEvents CVar (default 0). Set it to 1 to enable. + +Parameters: + + int itemId - the id of the item that triggered the spell, 0 if it wasn't triggered by an item + int spellId + string casterGuid - caster guid like "0xF5300000000000A5" + string targetGuid - target guid like "0xF5300000000000A5" or "0x0000000000000000" if none + int castFlags + int numTargetsHit + int numTargetsMissed + +CastFlags (bitmask) + +CAST_FLAG_NONE = 0 (0x00000000) +CAST_FLAG_HIDDEN_COMBATLOG = 1 (0x00000001) +CAST_FLAG_UNKNOWN2 = 2 (0x00000002) +CAST_FLAG_UNKNOWN3 = 4 (0x00000004) +CAST_FLAG_UNKNOWN4 = 8 (0x00000008) +CAST_FLAG_UNKNOWN5 = 16 (0x00000010) +CAST_FLAG_AMMO = 32 (0x00000020) +CAST_FLAG_UNKNOWN7 = 64 (0x00000040) +CAST_FLAG_UNKNOWN8 = 128 (0x00000080) +CAST_FLAG_UNKNOWN9 = 256 (0x00000100) + +SPELL_FAILED_SELF and SPELL_FAILED_OTHER + +Fire when a spell failure is reported. "Self" is fired from the client spell failure hook, "Other" is fired from the server handler (only includes caster guid and spell id). + +Parameters for SPELL_FAILED_SELF: + + int spellId + int spellResult - SpellCastResult enum value + int failedByServer - 1 if failed by server, 0 otherwise + +Parameters for SPELL_FAILED_OTHER: + + string casterGuid - caster guid like "0xF5300000000000A5" + int spellId + +SPELL_DELAYED_SELF and SPELL_DELAYED_OTHER + +Fire when a spell is delayed, generally due to taking damage. "Self" fires when the active player is affected, "Other" for other players. + +Parameters: + + string casterGuid - caster guid like "0xF5300000000000A5" + int delayMs - delay in milliseconds + +SPELL_CHANNEL_START and SPELL_CHANNEL_UPDATE + +Fire when the active player starts or updates a channeled spell. These are self-only events. + +Channel target guid is read from ChannelTargetGuid = 0xC4D980 and included in these events. + +Parameters for SPELL_CHANNEL_START: + + int spellId + string targetGuid - target guid like "0xF5300000000000A5" or "0x0000000000000000" if none + int durationMs - channel duration in milliseconds + +Parameters for SPELL_CHANNEL_UPDATE: + + int spellId + string targetGuid - target guid like "0xF5300000000000A5" or "0x0000000000000000" if none + int remainingMs - remaining channel time in milliseconds + +SPELL_DAMAGE_EVENT_SELF and SPELL_DAMAGE_EVENT_OTHER + +New events you can register in game to get updates whenever spell damage occurs. SPELL_DAMAGE_EVENT_SELF will only trigger for damage you deal, while SPELL_DAMAGE_EVENT_OTHER will only trigger for damage dealt by others. + +Both of these events have the following parameters: + + string targetGuid - guid string like "0xF5300000000000A5" + string casterGuid - guid string like "0xF5300000000000A5" + int spellId + int amount - the amount of damage dealt. If the 4th value in effectAuraStr is 89 (SPELL_AURA_PERIODIC_DAMAGE_PERCENT) I believe this is the percentage of health lost. + string mitigationStr - comma separated string containing "aborb,block,resist" amounts + int hitInfo - see below but generally 0 unless the spell was a crit in which case it will be 2 + int spellSchool - the damage school of the spell, see below + string effectAuraStr - comma separated string containing the three spell effect numbers and the aura type (usually means a Dot but not all Dots will have an aura type) if applicable. So "effect1,effect2,effect3,auraType" + +Spell hit info enum: 94f05231d4/src/game/Spells/SpellDefines.h (L109) + +Spell school enum: 94f05231d4/src/game/Spells/SpellDefines.h (L641) + +Spell effect enum: 94f05231d4/src/game/Spells/SpellDefines.h (L142) + +Aura type enum: 94f05231d4/src/game/Spells/SpellAuraDefines.h (L43) + +Example (uses ace RegisterEvent): + +Cursive:RegisterEvent("SPELL_DAMAGE_EVENT_SELF", + function(targetGuidStr, + casterGuidStr, + spellId, + amount, + mitigationStr, + hitInfo, + spellSchool, + effectAuraStr) + print(targetGuidStr .. " " .. casterGuidStr .. " " .. tostring(spellId) .. " " .. tostring(amount) .. " " .. tostring(spellSchool) .. " " .. mitigationStr .. " " .. hitInfo .. " " .. effectAuraStr) + end); + +Buff/Debuff Events + +New events fire whenever a buff or debuff is added or removed on you or any other unit that the client tracks. + +Events: + +BUFF_ADDED_SELF +BUFF_REMOVED_SELF +BUFF_ADDED_OTHER +BUFF_REMOVED_OTHER +DEBUFF_ADDED_SELF +DEBUFF_REMOVED_SELF +DEBUFF_ADDED_OTHER +DEBUFF_REMOVED_OTHER + +All eight events pass the same parameters: + + string guid - unit guid like "0xF5300000000000A5" + int slot - 1-based Lua slot index for the buff/debuff (skips empty slots to match UnitBuff/UnitDebuff ordering) + int spellId + int stackCount - current stack count for the aura (1 for a new aura; 0 when fully removed) + int auraLevel - caster level for the aura from UnitFields.auraLevels (uint8 per slot, 48 entries) + +Buff stack gains also fire the appropriate ADDED events. + +Example: + +local function onAuraEvent(eventName, guid, slot, spellId, stacks, auraLevel) + DEFAULT_CHAT_FRAME:AddMessage(string.format("[%s] %s slot=%d spell=%d stacks=%d level=%d", eventName, guid, slot, spellId, stacks, auraLevel)) +end + +for _, eventName in ipairs({"BUFF_ADDED_SELF", "BUFF_REMOVED_SELF", "DEBUFF_ADDED_OTHER", "DEBUFF_REMOVED_OTHER"}) do + frame:RegisterEvent(eventName, function(...) onAuraEvent(eventName, ...) end) +end + +AURA_CAST_ON_SELF and AURA_CAST_ON_OTHER + +Fire when a spell cast applies an aura. "Self" covers casts that land on the active player (including cases where the active player is the caster with no explicit target); "Other" covers all other targets. + +These events are gated behind the NP_EnableAuraCastEvents CVar (default 0). Set it to 1 to enable. Note: some auras do not have spell effects and won't trigger these events; the BUFF/DEBUFF gain events are the only way to track those. + +These events are primarily intended for basic tracking of aura applications when buff/debuff caps prevent normal GAINS events from firing. +Multi-Target Behavior + +These events fire separately for each target affected by the spell: + + Self-targeting spells (e.g., buffs with TARGET_UNIT_CASTER) trigger once on the caster + Single-target spells (e.g., spells that target specific units) trigger once on the primary target + AOE spells (e.g., area buffs/debuffs) trigger once for each affected target based on the spell's implicit targeting rules + +Each event fires once per qualifying spell effect per target. A spell with multiple aura-applying effects will trigger multiple events for the same target. + +Note: There may be edge cases or bugs with certain spells where targeting logic doesn't work as expected. If you encounter issues with specific spells not firing events correctly or firing too many/too few times, please report them in the issues tracker. + +Parameters: + + int spellId + string casterGuid - caster guid like "0xF5300000000000A5" + string targetGuid - target guid like "0xF5300000000000A5" + int effect - aura-applying effect id (event fires once for each qualifying effect in the spell) + int effectAuraName - corresponding entry from EffectApplyAuraName + int effectAmplitude - EffectAmplitude entry for the selected aura effect + int effectMiscValue - EffectMiscValue entry for the selected aura effect + int durationMs - spell duration in milliseconds (includes client modifiers if you are the caster) + int auraCapStatus - bitfield: 1 = buff bar full, 2 = debuff bar full (3 means both) + +AUTO_ATTACK_SELF and AUTO_ATTACK_OTHER + +Fire when auto attack damage is processed. "Self" fires when the active player is the attacker; "Other" fires someone other than the active player is the attacker. + +These events are gated behind the NP_EnableAutoAttackEvents CVar (default 0). Set it to 1 to enable. + +These events provide detailed information about auto attack rounds including damage, hit type (critical, glancing, crushing), victim state (dodged, parried, blocked), and mitigation amounts. + +Parameters: + + string attackerGuid - attacker guid like "0xF5300000000000A5" + string targetGuid - target guid like "0xF5300000000000A5" + int totalDamage - total damage dealt by the attack + int hitInfo - bitfield containing hit flags (see below) + int victimState - state of the victim after the attack (see below) + int subDamageCount - number of damage components (usually 1, can be up to 3 for weapons with elemental damages and maybe other cases). The sub-damage components are not provided in this event as they didn't seem that useful and limited to 9 parameters on events. + int blockedAmount - amount of damage blocked + int totalAbsorb - total damage absorbed across all sub-damage components + int totalResist - total damage resisted across all sub-damage components + +HitInfo Flags + +Bitfield values that can be combined: + +HITINFO_NORMALSWING = 0 (0x0) +HITINFO_UNK0 = 1 (0x1) +HITINFO_AFFECTS_VICTIM = 2 (0x2) +HITINFO_LEFTSWING = 4 (0x4) -- Off-hand attack +HITINFO_UNK3 = 8 (0x8) +HITINFO_MISS = 16 (0x10) +HITINFO_ABSORB = 32 (0x20) +HITINFO_RESIST = 64 (0x40) +HITINFO_CRITICALHIT = 128 (0x80) +HITINFO_UNK8 = 256 (0x100) +HITINFO_UNK9 = 8192 (0x2000) +HITINFO_GLANCING = 16384 (0x4000) +HITINFO_CRUSHING = 32768 (0x8000) +HITINFO_NOACTION = 65536 (0x10000) +HITINFO_SWINGNOHITSOUND = 524288 (0x80000) + +Victim States + +VICTIMSTATE_UNAFFECTED = 0 -- Seen with HITINFO_MISS +VICTIMSTATE_NORMAL = 1 +VICTIMSTATE_DODGE = 2 +VICTIMSTATE_PARRY = 3 +VICTIMSTATE_INTERRUPT = 4 +VICTIMSTATE_BLOCKS = 5 +VICTIMSTATE_EVADES = 6 +VICTIMSTATE_IS_IMMUNE = 7 +VICTIMSTATE_DEFLECTS = 8 + +Example: + +local HITINFO_CRITICALHIT = 128 -- 0x80 +local HITINFO_GLANCING = 16384 -- 0x4000 +local HITINFO_CRUSHING = 32768 -- 0x8000 + +local function onAutoAttack(attackerGuid, targetGuid, totalDamage, hitInfo, victimState, subDamageCount, blockedAmount, totalAbsorb, totalResist) + local isCrit = bit.band(hitInfo, HITINFO_CRITICALHIT) ~= 0 + local isGlancing = bit.band(hitInfo, HITINFO_GLANCING) ~= 0 + local isCrushing = bit.band(hitInfo, HITINFO_CRUSHING) ~= 0 + + local hitType = "Normal" + if isCrit then hitType = "Critical" + elseif isGlancing then hitType = "Glancing" + elseif isCrushing then hitType = "Crushing" + end + + local victimStateNames = { + [0] = "Unaffected", [1] = "Normal", [2] = "Dodged", [3] = "Parried", + [4] = "Interrupted", [5] = "Blocked", [6] = "Evaded", [7] = "Immune", [8] = "Deflected" + } + + DEFAULT_CHAT_FRAME:AddMessage(string.format( + "%s hit for %d (%s) - State: %s, Absorbed: %d, Resisted: %d, Blocked: %d", + hitType, totalDamage, attackerGuid, victimStateNames[victimState] or "Unknown", + totalAbsorb, totalResist, blockedAmount + )) +end + +frame:RegisterEvent("AUTO_ATTACK_SELF", onAutoAttack) +frame:RegisterEvent("AUTO_ATTACK_OTHER", onAutoAttack) + +UNIT_DIED + +Fires when a unit death is recorded in the combat log. + +Parameters: + + string guid - guid of the unit that died + +Example: + +frame:RegisterEvent("UNIT_DIED", function(guid) + DEFAULT_CHAT_FRAME:AddMessage("Unit died: " .. guid) +end) + + +Nampower Custom Lua Functions + +This document describes all custom Lua functions and events added by Nampower. + +For installation, configuration, and general usage information, see the main README.md. +Table of Contents + + Performance Optimization - Table References + Custom Lua Functions + Spell/Item/Unit Information + GetItemStats + GetItemStatsField + FindPlayerItemSlot + UseItemIdOrName + GetEquippedItems + GetEquippedItem + GetBagItems + GetBagItem + GetSpellRec + GetSpellRecField + GetSpellModifiers + GetUnitData + GetUnitField + GetSpellIdForName + GetSpellNameAndRankForId + GetSpellSlotTypeIdForName + GetNampowerVersion + GetItemLevel + Spell Casting and Queuing + QueueSpellByName + CastSpellByNameNoQueue + QueueScript + IsSpellInRange + IsSpellUsable + ChannelStopCastingNextTick + Cast Information + GetCurrentCastingInfo + GetCastInfo + Cooldown Information + GetSpellIdCooldown + GetItemIdCooldown + GetTrinkets + GetTrinketCooldown + UseTrinket + Utility Functions + DisenchantAll + +Performance Optimization - Table References + +Nampower functions that return tables use reusable table references to reduce memory allocations and improve performance. This means the same table object is reused across multiple function calls, with its contents updated each time. +Functions Using Reusable Table References + +The following functions use reusable table references: + + GetCastInfo() - Returns cast information table + GetEquippedItems([unitToken]) - Returns equipped items table + GetBagItems([bagIndex]) - Returns bag items table + GetBagItem(bagIndex, slot) - Returns item info table + GetEquippedItem(unitToken, slot) - Returns item info table + GetSpellIdCooldown(spellId) - Returns cooldown detail table + GetItemIdCooldown(itemId) - Returns cooldown detail table + GetItemStats(itemId, [copy]) - Returns item stats table + GetUnitData(unitToken, [copy]) - Returns unit data table + GetSpellRec(spellId, [copy]) - Returns spell record table + GetItemStatsField(itemId, fieldName, [copy]) - Returns individual item field value + GetUnitField(unitToken, fieldName, [copy]) - Returns individual unit field value + GetSpellRecField(spellId, fieldName, [copy]) - Returns individual spell field value + GetTrinkets([copy]) - Returns trinket list table + +Important: When using these functions without the copy parameter, immediately copy or extract any values you need to store for later use. Do not store references to the returned tables themselves. Alternatively, pass 1 as the copy parameter to get an independent table that is safe to store. + +Note: Functions like GetItemStats, GetUnitData, and GetSpellRec also use reusable references for their nested array fields (e.g., bonusStat, auras, EffectImplicitTargetA). Each nested array field name has its own dedicated reference that is reused across calls. + +-- ✓ SAFE - Extract values immediately +local castInfo = GetCastInfo() +if castInfo then + local spellId = castInfo.spellId + local castEnd = castInfo.castEndS + -- Use spellId and castEnd later +end + +-- ✓ SAFE - Extract nested array values immediately +local itemStats = GetItemStats(19019) +if itemStats then + local bonusStats = {} + for i = 1, #itemStats.bonusStat do + bonusStats[i] = itemStats.bonusStat[i] + end + -- Now bonusStats is a safe independent copy +end + +-- ✗ UNSAFE - Storing table references from the same function +local cast1 = GetCastInfo() -- Gets table reference +-- ... later ... +local cast2 = GetCastInfo() -- Gets SAME table reference with new data +-- cast1 and cast2 both point to the same table with cast2's data! + +-- ✗ UNSAFE - Storing nested array references +local item1 = GetItemStats(19019) +local item1BonusStats = item1.bonusStat -- Stores reference to nested array +local item2 = GetItemStats(22589) +-- item1BonusStats was overwritten! The "bonusStat" nested array reference is reused + +-- ✓ SAFE - Using copy parameter for nested arrays +local item1 = GetItemStats(19019, 1) -- Pass 1 to get independent copy +local item1BonusStats = item1.bonusStat -- Safe to store, it's an independent copy +local item2 = GetItemStats(22589, 1) -- Another independent copy +-- Both item1BonusStats and item2.bonusStat are independent tables + +Important for array field functions: Each field name gets its own dedicated table reference, but the table is still reused across calls with the same field name. Always extract values immediately - never store the table reference itself. Alternatively, pass 1 as the copy parameter to get an independent table copy: + +-- ✓ SAFE - Extract array values immediately +local bonusStats = {} +local tempTable = GetItemStatsField(itemId, "bonusStat") +for i = 1, #tempTable do + bonusStats[i] = tempTable[i] +end +-- Now bonusStats is a safe independent copy + +-- ✓ EASIER - Use copy parameter to get independent table +local bonusStats = GetItemStatsField(itemId, "bonusStat", 1) +-- Safe to store, no manual copying needed! + +-- ✗ UNSAFE - Storing table references (even with different field names) +local bonusStats = GetItemStatsField(itemId, "bonusStat") +local bonusAmounts = GetItemStatsField(itemId, "bonusAmount") +-- Later... +local newBonusStats = GetItemStatsField(otherItemId, "bonusStat") +-- bonusStats was overwritten! The "bonusStat" reference is reused across calls + +-- ✗ ALSO UNSAFE - Same field name, multiple calls +local item1Stats = GetItemStatsField(19019, "bonusStat") +local item2Stats = GetItemStatsField(22589, "bonusStat") +-- item1Stats was immediately overwritten by the second call! + +Custom Lua Functions +Spell/Item/Unit information +GetItemStats(itemId, [copy]) + +Returns a Lua table reference containing all fields for the item's ItemStats record (including localized displayName and description). Returns nil if the item cannot be found or loaded. + +Optional parameter: Pass 1 for copy to get an independent table copy instead of a reusable reference. + +Full field name lists are in DBC_FIELDS.md. +GetItemStatsField(itemId, fieldName, [copy]) + +Fast lookup for a single field on an item. Returns the requested field value; returns nil if the item is not found; raises a Lua error if the field name is invalid. + +Optional parameter: Pass 1 for copy to get an independent table copy (for array fields only). + +Full field name lists are in DBC_FIELDS.md. + +Examples: + +-- Get item name +local name = GetItemStatsField(19019, "displayName") +print(name) -- "Thunderfury, Blessed Blade of the Windseeker" + +-- Get item level +local ilvl = GetItemStatsField(22589, "itemLevel") +print("Atiesh item level: " .. ilvl) -- 90 + +-- Get item quality (0=Poor, 1=Common, 2=Uncommon, 3=Rare, 4=Epic, 5=Legendary) +local quality = GetItemStatsField(19019, "quality") +print("Quality: " .. quality) -- 5 (Legendary) + +-- Get item delay (weapon speed in milliseconds) +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-10 = 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-10): Returns 1-indexed slot within the bag + Returns nil,nil if the item is not found + +Examples: + +-- 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 + +UseItemIdOrName(itemIdOrName, [target]) + +Uses the first matching item found in the player's inventory (including equipped items) by item ID or name. + +Parameters: + + itemIdOrName (number|string): Item ID or item name (case-insensitive) + target (optional, string|number): Unit token (e.g. "target", "player") or GUID + If omitted, uses LockedTargetGuid if set; otherwise falls back to the active player GUID. + +Returns: + + 1 if the item was found and CGItem_C::Use(...) returned non-zero + 0 if the item was not found or use failed + +Examples: + +-- Use Hearthstone +UseItemIdOrName("Hearthstone") + +-- Use a healing potion on yourself (if the item requires a target) +UseItemIdOrName(13446, "player") + +GetEquippedItems(unitToken) + +Returns a table reference 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 reference 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: + +-- 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 reference 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: + +-- 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([bagIndex]) + +If no bagIndex is specified, the function returns a nested table reference containing all items in all bags (including bank if open). With specified index, it only returns the contents of that bag + +Returns: + + A Lua table reference 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-10 = 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: + +-- 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 + +-- Get all items in backbag +local bagContents = GetBagItems(0) +for slot, itemInfo in pairs(bagContents) do + print(" Slot " .. slot .. ": " .. itemInfo.itemId .. " (x" .. itemInfo.stackCount .. ")") +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-10 = Bank bags (requires bank to be open) + -2 = Keyring + slot (number): 1-indexed slot number within the bag + +Returns: + + A Lua table reference containing the item info (same fields as GetBagItems) + Returns nil if the slot is empty or invalid + +Examples: + +-- 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, [copy]) + +Returns a Lua table reference containing all fields for the spell's SpellRec record (including localized name and rank). Returns nil if the spell cannot be found. + +Optional parameter: Pass 1 for copy to get an independent table copy instead of a reusable reference. + +Full field name lists are in DBC_FIELDS.md. +GetSpellRecField(spellId, fieldName, [copy]) + +Fast lookup for a single field on a spell. Returns the requested field value; returns nil if the spell is not found; raises a Lua error if the field name is invalid. + +Optional parameter: Pass 1 for copy to get an independent table copy (for array fields only). + +Full field name lists are in DBC_FIELDS.md. + +Examples: + +-- Get spell name +local name = GetSpellRecField(116, "name") +print(name) -- "Frostbolt" + +-- Get spell rank +local rank = GetSpellRecField(116, "rank") +print(rank) -- "Rank 1" + +-- Get spell cast time in milliseconds +local castTime = GetSpellRecField(133, "castTime") +print("Fireball cast time: " .. (castTime / 1000) .. " seconds") -- 3.5 seconds + +-- Get spell range (max range in yards * 10, so divide by 10) +local maxRange = GetSpellRecField(116, "rangeMax") +print("Frostbolt max range: " .. (maxRange / 10) .. " yards") -- 30 yards + +-- Get spell mana cost +local manaCost = GetSpellRecField(116, "manaCost") +print("Mana cost: " .. manaCost) + +-- Get spell school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane) +local school = GetSpellRecField(116, "school") +print("School: " .. school) -- 4 (Frost) + +-- Get spell icon ID +local spellIconID = GetSpellRecField(116, "spellIconID") +print("Icon ID: " .. spellIconID) + +GetSpellModifiers(spellId, modifierType) + +Returns the current spell modifiers applied to a spell for the player. This includes buffs, talents, and other effects that modify spell behavior. + +Parameters: + + spellId (number): The spell ID to check + modifierType (number): The type of modifier to check (see list below) + +Returns: + + 1st param (number): Flat modification value (e.g., +50 damage) + 2nd param (number): Percent modification value (e.g., 10 for +10%) + 3rd param (number): Return value from the function (whether there was any percent or flat modifier) + +Modifier Types: + + 0 = DAMAGE + 1 = DURATION + 2 = THREAT + 3 = ATTACK_POWER + 4 = CHARGES + 5 = RANGE + 6 = RADIUS + 7 = CRITICAL_CHANCE + 8 = ALL_EFFECTS + 9 = NOT_LOSE_CASTING_TIME + 10 = CASTING_TIME + 11 = COOLDOWN + 12 = SPEED + 14 = COST + 15 = CRIT_DAMAGE_BONUS + 16 = RESIST_MISS_CHANCE + 17 = JUMP_TARGETS + 18 = CHANCE_OF_SUCCESS + 19 = ACTIVATION_TIME + 20 = EFFECT_PAST_FIRST + 21 = CASTING_TIME_OLD + 22 = DOT + 23 = HASTE + 24 = SPELL_BONUS_DAMAGE + 27 = MULTIPLE_VALUE + 28 = RESIST_DISPEL_CHANCE + +Example: + +-- Check damage modifiers on Frostbolt (spell ID 116) +local flatMod, percentMod, ret = GetSpellModifiers(116, 0) +print("Flat damage bonus: " .. flatMod) +print("Percent damage bonus: " .. percentMod .. "%") + +GetUnitData(unitToken, [copy]) + +Returns a Lua table reference containing all unit fields for the specified unit. This provides access to low-level unit data like health, mana, stats, auras, resistances, and more. + +Parameters: + + unitToken (string): Can be a standard unit token ("player", "target", "pet", "mouseover", etc.) or a GUID string (e.g., "0xF5300000000000A5") + copy (number, optional): Pass 1 to get an independent table copy instead of a reusable reference + +Returns: + + A Lua table reference containing all unit fields, or nil if the unit cannot be found + +Full field name lists are in UNIT_FIELDS.md. + +Example: + +-- Get all unit data for your current target +local data = GetUnitData("target") +if data then + print("Target health: " .. data.health .. "/" .. data.maxHealth) + print("Target level: " .. data.level) + print("Target display ID: " .. data.displayId) +end + +-- Using a GUID +local data = GetUnitData("0xF5300000000000A5") + +GetUnitField(unitToken, fieldName, [copy]) + +Fast lookup for a single field on a unit. More efficient than GetUnitData when you only need one specific field. + +Parameters: + + unitToken (string): Can be a standard unit token ("player", "target", "pet", "mouseover", etc.) or a GUID string + fieldName (string): The name of the field to retrieve + copy (number, optional): Pass 1 to get an independent table copy (for array fields only) + +Returns: + + The requested field value; returns nil if the unit is not found; raises a Lua error if the field name is invalid + For array fields (like "aura", "resistances"), returns a Lua table with numeric indices + +Full field name lists are in UNIT_FIELDS.md. + +Examples: + +-- Get target's current health +local health = GetUnitField("target", "health") +print("Target health: " .. health) + +-- Get player's current mana (power1) +local mana = GetUnitField("player", "power1") +print("Player mana: " .. mana) + +-- Get all auras on target (returns a table) +local auras = GetUnitField("target", "aura") +for i, auraId in ipairs(auras) do + print("Aura " .. i .. ": " .. auraId) +end + +-- Get all resistances (returns a table) +local resistances = GetUnitField("player", "resistances") +-- resistances[1] = armor, [2] = holy, [3] = fire, [4] = nature, [5] = frost, [6] = shadow, [7] = arcane + +QueueSpellByName(spellName) + +Will force queue a spell regardless of the appropriate queue window. If no spell is currently being cast it will be cast immediately. For example can make a macro with + +/run QueueSpellByName("Frostbolt");QueueSpellByName("Frostbolt") + +to cast 2 frostbolts in a row. Currently, can only queue 1 GCD spell at a time and 5 non gcd spells. This means you can't do 3 frostbolts in a row with one macro. +CastSpellByNameNoQueue(spellName) + +Will force a spell cast to never queue even if your settings would normally queue. Can be used to fix addons that don't work with queued spells. +QueueScript(script, [priority]) + +Queues any arbitrary script using the same logic as a regular spell using NP_SpellQueueWindowMs as the window. If no spell is being cast and you are not on the gcd the script will be run immediately. + +Priority is optional and defaults to 1. +Priority 1 means the script will run before any other queued spells. Priority 2 means the script will run after any queued non gcd spells but before any queued normal spells. Priority 3 means the script will run after any type of queued spells. + +Convert slash commands from other addons like /equip to their function form SlashCmdList.EQUIP to use them inside QueueScript. + +For example, you can equip a libram before casting a queued heal using + +/run QueueScript('SlashCmdList.EQUIP("Libram of +heal")') + +IsSpellInRange(spellName, [target]) or IsSpellInRange(spellId, [target]) + +Takes a spell name or spell id and an optional target. Target can the usual UNIT tokens like "player", "target", "mouseover", etc or a unit guid. + +If using spell name it must be a spell you have in your spellbook. If using spell id it can be any spell id. + +Returns 1 if the spell is in range, 0 if not in range, and -1 if the spell is not valid for this check (must be TARGET_UNIT_PET, TARGET_UNIT_TARGET_ENEMY, TARGET_UNIT_TARGET_ALLY, TARGET_UNIT_TARGET_ANY). This is because this uses the same underlying function as IsActionInRange which returns 1 for spells that are not single target which can be misleading. + +Examples: + +/run local result=IsSpellInRange("Frostbolt"); if result == 1 then print("In range") else if result == 0 then print("Out of range") else print("Not single target") end + +IsSpellUsable(spellName) or IsSpellUsable(spellId) + +Takes a spell name or spell id. + +Usable does not equal castable. This is most often used to check if a reactive spell is usable. + +If using spell name it must be a spell you have in your spellbook. If using spell id it can be any spell id. + +Returns: + +1st param: 1 if the spell is usable, 0 if not usable. 2nd param: Always 0 if spell is not usable for a different reason other than mana. 1 if out of mana, 0 if not out of mana. + +Examples: + +/run local result=IsSpellUsable("Frostbolt"); if result == 1 then print("Frostbolt usable") else print("Frostbolt not usable") end + +GetCurrentCastingInfo() + +Returns: + +1st param: Casting spell id or 0 2nd param: Visual spell id or 0. This won't always get cleared after a spell finishes. 3rd param: Auto repeating spell id or 0. 4th param: 1 if casting spell with a cast time, 0 if not. 5th param: 1 if channeling, 0 if not. 6th param: 1 if on swing spell is pending, 0 if not. 7th param: 1 if auto attacking, 0 if not. + +For normal spells these will be the same. For some spells like auto-repeating and channeling spells only the visual spell id will be set. + +Examples: + +/run local castId,visId,autoId,casting,channeling,onswing,autoattack=GetCurrentCastingInfo();print(castId);print(visId);print(autoId);print(casting);print(channeling);print(onswing);print(autoattack); + +GetCastInfo() + +Returns detailed information about the currently active cast or channel. Returns nil if there is no active cast or channel. GetCurrentCastingInfo was made very early on and doesn't provide enough information for many use cases, but still has some uses and is available for backwards compatibility. + +Returns: A Lua table reference with the following fields, or nil if no cast is active: + + castId (number): Unique identifier for this cast + spellId (number): The spell ID being cast + guid (number): Target GUID (0 if no explicit target) + castType (number): Type of cast - 0=NORMAL, 3=CHANNEL, 4=TARGETING + castStartS (number): When the cast started in WoW time (seconds with decimals, e.g., 1234567.890) + castEndS (number): When the cast will end in WoW time (seconds with decimals) + castRemainingMs (number): Milliseconds remaining until cast ends + castDurationMs (number): Total cast duration in milliseconds + gcdEndS (number): When the GCD will end in WoW time (seconds with decimals) + gcdRemainingMs (number): Milliseconds remaining until GCD expires + +Notes: + + Time fields ending in S (castStartS, castEndS, gcdEndS) are absolute timestamps in seconds with decimal precision to match GetTime() in Lua + Duration and remaining fields ending in Ms (castRemainingMs, castDurationMs, gcdRemainingMs) are in milliseconds for precision + Returns nil if there is no active cast (castSpellId is 0) and no active channel (channelSpellId is 0) + +Examples: + +-- Check current cast information +local info = GetCastInfo() +if info then + print("Casting spell: " .. info.spellId) + print("Cast ends at: " .. info.castEndS) + print("Time remaining: " .. info.castRemainingMs .. "ms") + print("GCD ends at: " .. info.gcdEndS) + print("GCD remaining: " .. info.gcdRemainingMs .. "ms") +else + print("No active cast") +end + +-- Check if you can cast another spell (GCD check) +local info = GetCastInfo() +if not info or info.gcdRemainingMs == 0 then + print("Ready to cast!") +else + print("On GCD for " .. info.gcdRemainingMs .. "ms more") +end + +-- Monitor cast progress +local info = GetCastInfo() +if info and info.castDurationMs > 0 then + local progress = ((info.castDurationMs - info.castRemainingMs) / info.castDurationMs) * 100 + print("Cast progress: " .. string.format("%.1f%%", progress)) +end + +GetSpellIdCooldown(spellId) + +Returns detailed cooldown information for a spell from the spell history. This provides precise timing data for individual spell cooldowns, category cooldowns, and GCD. + +Parameters: + + spellId (number): The spell ID to check + +Returns: A Lua table reference with the following fields: + + isOnCooldown (number): 1 if any cooldown is active, 0 otherwise + cooldownRemainingMs (number): Maximum remaining time across all cooldown types in milliseconds + itemId (number): Item ID tied to the cooldown (0 if none) + itemHasActiveSpell (number): 1 if the item has an on-use spell, 0 otherwise + itemActiveSpellId (number): Spell ID of the active item spell (0 if none) + +Individual Spell Cooldown: + + individualStartS (number): When the individual spell cooldown started (seconds, WoW time) + individualDurationMs (number): Total duration of the individual spell cooldown in milliseconds + individualRemainingMs (number): Milliseconds remaining on the individual spell cooldown + isOnIndividualCooldown (number): 1 if the spell-specific cooldown is active, 0 otherwise + +Category Cooldown: + + categoryId (number): The cooldown category ID (0 if no category cooldown) + categoryStartS (number): When the category cooldown started (seconds, WoW time) + categoryDurationMs (number): Total duration of the category cooldown in milliseconds + categoryRemainingMs (number): Milliseconds remaining on the category cooldown + isOnCategoryCooldown (number): 1 if the category cooldown is active, 0 otherwise + +GCD (Global Cooldown): + + gcdCategoryId (number): The GCD category ID (typically 133 for most spells) + gcdCategoryStartS (number): When the GCD started (seconds, WoW time) + gcdCategoryDurationMs (number): Total GCD duration in milliseconds (typically 1500ms) + gcdCategoryRemainingMs (number): Milliseconds remaining on the GCD + isOnGcdCategoryCooldown (number): 1 if the GCD is active, 0 otherwise + +Notes: + + Time fields ending in S are absolute timestamps in seconds to match GetTime() in Lua + Fields ending in Ms are in milliseconds for precision + The spell must have been cast at least once for accurate data to be available + cooldownRemainingMs is the maximum of all three cooldown types + +Example: + +-- Check if Frostbolt is ready to cast +local cd = GetSpellIdCooldown(116) -- Frostbolt +if cd.isOnCooldown == 0 then + print("Frostbolt is ready!") +else + print("Frostbolt on cooldown for " .. cd.cooldownRemainingMs .. "ms") + if cd.isOnGcdCategoryCooldown == 1 then + print(" GCD: " .. cd.gcdCategoryRemainingMs .. "ms remaining") + end + if cd.isOnIndividualCooldown == 1 then + print(" Spell CD: " .. cd.individualRemainingMs .. "ms remaining") + end + if cd.isOnCategoryCooldown == 1 then + print(" Category CD: " .. cd.categoryRemainingMs .. "ms remaining") + end +end + +GetItemIdCooldown(itemId) + +Returns detailed cooldown information for an item from the spell history. Works similarly to GetSpellIdCooldown but for items. + +Parameters: + + itemId (number): The item ID to check + +Returns: A Lua table reference with the same structure as GetSpellIdCooldown (see above). + +Notes: + + Returns the longest cooldown among all spells associated with the item + If the item has multiple on-use effects, returns information for the one with the longest remaining cooldown + Item cooldowns are tracked through their associated spell entries in the spell history + +Example: + +-- Check if a trinket is ready +local cd = GetItemIdCooldown(12345) -- Replace with your trinket ID +if cd.isOnCooldown == 0 then + print("Trinket is ready to use!") +else + print("Trinket on cooldown for " .. cd.cooldownRemainingMs .. "ms") +end + +GetTrinkets([copy]) + +Returns a table of trinkets from equipped trinket slots and carried bags. + +Parameters: + + [copy] (number|boolean, optional): Pass 1 (or any truthy value) to force creation of a fresh Lua table. By default the function reuses an internal table and entry tables for performance. + +Returns: A Lua table where each entry contains: + + itemId (number) + trinketName (string, "Unknown" if no name available) + texture (string): Texture name for the item icon + bagIndex (number|nil): nil when equipped; 0 for backpack; 1-4 for equipped bags + slotIndex (number): Lua 1-based slot within the container (or 1/2 for equipped trinket slots) + +Notes: + + Scans only equipped trinket slots and bags 0-4 (backpack + equipped bags). Does not scan bank or keyring. + Reuses cached Lua tables unless copyTable is truthy; prefer copies if you will mutate the returned tables. + +GetTrinketCooldown(slot|itemIdOrName) + +Returns cooldown information for the equipped trinket(s) in slots 13 or 14. Accepts slot shortcuts or item identifiers. + +Parameters: + + slot|itemIdOrName (number|string): + 1 or 13 => first trinket slot + 2 or 14 => second trinket slot + Any other number => treat as item ID to match against trinket slots + String => item name (case-insensitive) to match against trinket slots + +Returns: + + If no matching trinket is equipped in slots 13/14: returns -1 + Otherwise: a cooldown detail table with the same structure as GetSpellIdCooldown / GetItemIdCooldown + +Example: + +-- Get cooldown for first trinket slot +local cd = GetTrinketCooldown(1) +if cd ~= -1 and cd.isOnCooldown == 0 then + print("Trinket ready") +end + +-- Check by name +local cd = GetTrinketCooldown("Royal Seal of Eldre'Thalas") +if cd ~= -1 then + print("Remaining: " .. cd.cooldownRemainingMs .. "ms") +end + +UseTrinket(slot|itemIdOrName, [target]) + +Uses a trinket from the equipped trinket slots (13 and 14 only). + +Parameters: + + slot|itemIdOrName (number|string): + 1 or 13 => use first trinket slot + 2 or 14 => use second trinket slot + Any other number => treat as item ID to find in trinket slots + String => item name (case-insensitive) to find in trinket slots + target (optional, string|number): Unit token or GUID. If omitted, uses LockedTargetGuid if set; otherwise falls back to active player GUID. + +Returns: + + 1 if the trinket was found and CGItem_C::Use(...) returned non-zero + 0 if the trinket was found but use returned zero + -1 if no matching trinket was found in slots 13/14 + +Examples: + +-- Use first trinket slot +UseTrinket(1) +-- Use second trinket slot on current target +UseTrinket(2, "target") +-- Use by item id if present in either trinket slot +UseTrinket(18406) +-- Use by name +UseTrinket("Royal Seal of Eldre'Thalas") + +GetSpellIdForName(spellName) + +Returns: + +1st param: the max rank spell id for a spell name if it exists in your spellbook. Returns 0 if the spell is not in your spellbook. + +Examples: + +/run local spellId=GetSpellIdForName("Frostbolt");print(spellId) +/run local spellId=GetSpellIdForName("Frostbolt(Rank 1)");print(spellId) + +GetSpellNameAndRankForId(id) + +Returns: + +1st param: the spell name for a spell id 2nd param: the spell rank for a spell id as a string such as "Rank 1" + +Examples: + +/run local spellName,spellRank=GetSpellNameAndRankForId(116);print(spellName);print(spellRank) +prints "Frostbolt" and "Rank 1" + +GetSpellSlotTypeIdForName(spellName) + +Returns: + +1st param: the 1 indexed (lua calls expect this) spell slot number for a spell name if it exists in your spellbook. Returns 0 if the spell is not in your spellbook. 2nd param: the book type of the spell, either "spell", "pet" or "unknown". 3rd param: the spell id of the spell. Returns 0 if the spell is not in your spellbook. + +Examples: + +/run local slot, bookType, spellId=GetSpellSlotTypeIdForName("Frostbolt");print(slot);print(bookType);print(spellId) + +GetNampowerVersion() + +Returns the current version of Nampower split into major, minor and patch numbers. + +So if version was v2.8.6 it would return 2, 8, 6 as integers. + +Examples: + +/run local major, minor, patch=GetNampowerVersion();print(major);print(minor);print(patch) + +The previous version of this GetSpellSlotAndTypeForName was removed as it was returning a 0 indexed slot number which was confusing to use in lua. +GetItemLevel(itemId) + +Returns the item level of an item. Returns an error if the item id is invalid. + +Examples: + +/run local itemLevel=GetItemLevel(22589);print(itemLevel) +should print 90 for atiesh + +ChannelStopCastingNextTick() + +Will stop channeling early on the next tick if you have queue channeling spells enabled and try to cast a spell before the next tick (didn't know how to cancel channels without casting another spell). Uses your ChannelLatencyReductionPercentage to determine when to stop the channel. +Utility Functions +DisenchantAll(itemIdOrName, [includeSoulbound]) or DisenchantAll(quality, [includeSoulbound]) + +Automatically disenchants items in your inventory. Can disenchant a specific item by ID/name, or all weapons and armor of a specified quality. + +⚠️ WARNING ⚠️ THIS FUNCTION WILL AUTOMATICALLY DISENCHANT ITEMS WITHOUT CONFIRMATION! + + Use at your own risk - there is no undo for disenchanting + Only disenchants items from player inventory bags (backpack and bags 1-4) + Equipped items, bank items, and keyring are PROTECTED - will not be touched + Quest items are ALWAYS protected regardless of settings + Soulbound items are protected by default (can be overridden with optional parameter) + Make sure you have the Disenchant spell and the items are disenchantable before using + Always double-check your bags before running this command + +Parameters: + +Mode 1: Disenchant by Item ID or Name + + itemIdOrName (number|string): Item ID (number) or item name (string) + Disenchants all copies of the specified item found in your bags + Works on any disenchantable item type (weapons, armor, etc.) + includeSoulbound (number, optional): Pass any non-zero value (e.g., 1) to include soulbound items (defaults to 0) + +Mode 2: Disenchant by Quality (weapons and armor only) + + quality (string): Can be a single quality or combination (pipe-separated): + "greens" - Disenchants all uncommon (green) quality weapons and armor + "blues" - Disenchants all rare (blue) quality weapons and armor + "purples" - Disenchants all epic (purple) quality weapons and armor + "greens|blues" - Disenchants both greens and blues + "blues|purples" - Disenchants both blues and purples + "greens|blues|purples" - Disenchants greens, blues, and purples + Only affects weapons (class 2) and armor (class 4) + includeSoulbound (number, optional): Pass any non-zero value (e.g., 1) to include soulbound items (defaults to 0) + +Returns: + + 1 if the first disenchant succeeded + 0 if no matching items were found or the disenchant failed + +Behavior: + + Searches player inventory bags (backpack and bags 1-4) only - equipped items, bank, and keyring are protected + Finds the first matching item in your inventory + Displays a chat message showing which item is being disenchanted + Casts Disenchant spell on that item + Automatically continues disenchanting matching items every 5 seconds + Stops when no more matching items are found or an error occurs + Displays completion or error messages in chat + +Examples: + +-- Disenchant all green weapons and armor in your bags (excluding soulbound) +DisenchantAll("greens") + +-- Disenchant all blue weapons and armor including soulbound items +DisenchantAll("blues", 1) + +-- Disenchant all purple (epic) weapons and armor +DisenchantAll("purples") + +-- Disenchant both greens and blues +DisenchantAll("greens|blues") + +-- Disenchant blues and purples including soulbound items +DisenchantAll("blues|purples", 1) + +-- Disenchant all greens, blues, and purples +DisenchantAll("greens|blues|purples") + +-- Disenchant a specific item by ID (excluding soulbound) +DisenchantAll(12345) + +-- Disenchant a specific item by name including soulbound items +DisenchantAll("Glowing Brightwood Staff", 1) + +Important Notes: + + The function runs continuously until all matching items are disenchanted + Only searches inventory bags (backpack and bags 1-4) - equipped items, bank, and keyring are protected + When using quality mode ("greens"/"blues"/"purples" or combinations), only weapons and armor are affected + When using item ID/name mode, any disenchantable item can be targeted + Make sure you have enough bag space for the disenchanting materials + The function will stop if you run out of matching items or if the disenchant spell fails + Displays chat messages: "Disenchanting [Item Link] move during cast to cancel.", "No more items to disenchant.", and "Disenchant interrupted or failed." + REVIEW YOUR BAGS CAREFULLY BEFORE USE - disenchanting cannot be undone! + + + + +Unit Fields Reference + +This document lists all available unit fields that can be accessed via GetUnitData() and GetUnitField() functions. +Overview + +Unit fields provide low-level access to game data for any unit (player, target, pet, NPCs, etc.). These fields are part of the WoW 1.12 client's internal data structures. +Usage + +-- Get all fields at once +local data = GetUnitData("target") +if data then + print("Health: " .. data.health) +end + +-- Get a single field (more efficient) +local health = GetUnitField("target", "health") + +Simple Fields + +These fields return a single value (number). +Object References (UINT64) + +These fields contain GUIDs (globally unique identifiers) for game objects: + + charm - GUID of the unit that has charmed this unit + summon - GUID of the unit's current summon + charmedBy - GUID of the unit that charmed this unit + summonedBy - GUID of the unit that summoned this unit + createdBy - GUID of the unit that created this unit + target - GUID of the unit's current target + persuaded - GUID of the unit being persuaded + channelObject - GUID of the object being channeled (for channeling spells) + +Health and Power (UINT32) + + health - Current health points + power1 - Current mana (or energy/rage depending on class) + power2 - Current rage + power3 - Current focus (hunter pets) + power4 - Current energy (rogues, druids in cat/bear form) + power5 - Current happiness (hunter pets) + maxHealth - Maximum health points + maxPower1 - Maximum mana + maxPower2 - Maximum rage + maxPower3 - Maximum focus + maxPower4 - Maximum energy + maxPower5 - Maximum happiness + +Basic Unit Info (UINT32) + + level - Unit's level + factionTemplate - Unit's faction template ID + flags - Unit flags (combat, dead, mounted, etc.) + dynamicFlags - Dynamic unit flags (lootable, tapped, etc.) + auraState - Aura state flags (reactive abilities like Revenge, Overpower) + npcFlags - NPC flags (vendor, trainer, quest giver, etc.) + npcEmoteState - Current emote state ID + +Display and Appearance (UINT32) + + displayId - Current display model ID + nativeDisplayId - Original/native display model ID + mountDisplayId - Mount display model ID (if mounted) + +Combat Stats (UINT32) + + baseAttackTime - Base main-hand attack time in milliseconds + offhandAttackTime - Off-hand attack time in milliseconds + rangedAttackTime - Ranged attack time in milliseconds + +Combat Stats (FLOAT) + + boundingRadius - Unit's bounding radius for collision + combatReach - Unit's combat reach distance + minDamage - Minimum main-hand damage + maxDamage - Maximum main-hand damage + minOffhandDamage - Minimum off-hand damage + maxOffhandDamage - Maximum off-hand damage + minRangedDamage - Minimum ranged damage + maxRangedDamage - Maximum ranged damage + +Pet Info (UINT32) + + petNumber - Pet's number identifier + petNameTimestamp - Timestamp of pet name + petExperience - Pet's current experience + petNextLevelExp - Experience needed for pet's next level + +Spell/Channel Info (UINT32) + + channelSpell - Spell ID of currently channeling spell + createdBySpell - Spell ID that created this unit (for summons) + +Casting Speed (FLOAT) + + modCastSpeed - Cast speed modifier (1.0 = normal, <1.0 = faster, >1.0 = slower) + +Training (UINT32) + + trainingPoints - Available training points (for pets and talents) + +Base Stats (UINT32) + + stat0 - Strength + stat1 - Agility + stat2 - Stamina + stat3 - Intellect + stat4 - Spirit + baseMana - Base mana before modifiers + baseHealth - Base health before modifiers + +Attack Power (UINT32) + + attackPower - Total melee attack power + attackPowerMods - Attack power modifiers + rangedAttackPower - Total ranged attack power + rangedAttackPowerMods - Ranged attack power modifiers + +Attack Power Multipliers (FLOAT) + + attackPowerMultiplier - Melee attack power multiplier + rangedAttackPowerMultiplier - Ranged attack power multiplier + +Packed Byte Fields (UINT32) + +These are special fields that pack multiple values into a single 32-bit integer: + + bytes0 - Contains race, class, gender, and power type + Byte 0: Race + Byte 1: Class + Byte 2: Gender + Byte 3: Power type + + bytes1 - Contains stand state, pet talent points, vis flags, and anim tier + Byte 0: Stand state + Byte 1: Pet talent points + Byte 2: Vis flags + Byte 3: Anim tier + + bytes2 - Contains sheath state, pvp flags, pet flags, and shape shift form + Byte 0: Sheath state + Byte 1: PvP flags + Byte 2: Pet flags + Byte 3: Shape shift form + +Array Fields + +These fields return a Lua table with numeric indices (1-based). +Visual Items (UINT32 arrays) + + virtualItemDisplay [3] - Display IDs for virtual items (weapon visuals, etc.) + virtualItemInfo [6] - Additional virtual item information + +Auras/Buffs/Debuffs (UINT32 arrays) + + aura [48] - Array of aura (buff/debuff) spell IDs on the unit + Index 1-48 contains spell IDs (0 = no aura in that slot) + + auraFlags [6] - Bit flags for aura properties (positive, negative, cancelable, etc.) + Each UINT32 contains flags for 8 auras (4 bits per aura) + + auraLevels [48] - Aura caster levels (UINT8 per aura slot) + + auraApplications [48] - Aura stack counts (UINT8 per aura slot) + +Resistances (UINT32 arrays) + + resistances [7] - Resistance values for all schools + Index 1: Armor (physical resistance) + Index 2: Holy resistance + Index 3: Fire resistance + Index 4: Nature resistance + Index 5: Frost resistance + Index 6: Shadow resistance + Index 7: Arcane resistance + +Spell Power Modifiers (FLOAT arrays) + + powerCostModifier [7] - Flat mana cost modifications per school + Index 1-7: Physical, Holy, Fire, Nature, Frost, Shadow, Arcane + + powerCostMultiplier [7] - Percent mana cost modifications per school + Index 1-7: Physical, Holy, Fire, Nature, Frost, Shadow, Arcane + +Examples +Health Monitoring + +-- Monitor target health percentage +local health = GetUnitField("target", "health") +local maxHealth = GetUnitField("target", "maxHealth") +local healthPct = (health / maxHealth) * 100 +print("Target health: " .. healthPct .. "%") + +Checking Auras + +-- Check if target has a specific debuff +local auras = GetUnitField("target", "aura") +local CURSE_OF_AGONY = 980 + +for i, spellId in ipairs(auras) do + if spellId == CURSE_OF_AGONY then + print("Target has Curse of Agony!") + break + end +end + +Reading Resistances + +-- Get all player resistances +local resistances = GetUnitField("player", "resistances") +print("Armor: " .. resistances[1]) +print("Holy resistance: " .. resistances[2]) +print("Fire resistance: " .. resistances[3]) +print("Nature resistance: " .. resistances[4]) +print("Frost resistance: " .. resistances[5]) +print("Shadow resistance: " .. resistances[6]) +print("Arcane resistance: " .. resistances[7]) + +Checking Combat State + +-- Check if unit is in combat (flags field bit check) +local flags = GetUnitField("target", "flags") +local UNIT_FLAG_IN_COMBAT = 0x00080000 +local inCombat = bit.band(flags, UNIT_FLAG_IN_COMBAT) ~= 0 +print("In combat: " .. tostring(inCombat)) + +Monitoring Cast Speed + +-- Check your current cast speed modifier +local modCastSpeed = GetUnitField("player", "modCastSpeed") +-- 1.0 = normal, 0.9 = 10% faster, 1.1 = 10% slower +print("Cast speed modifier: " .. modCastSpeed) + +Attack Power Info + +-- Get your attack power +local ap = GetUnitField("player", "attackPower") +local apMods = GetUnitField("player", "attackPowerMods") +local apMult = GetUnitField("player", "attackPowerMultiplier") +print("Base AP: " .. ap) +print("AP Mods: " .. apMods) +print("AP Multiplier: " .. apMult) + +Pet Information + +-- Check pet experience +if UnitExists("pet") then + local petXP = GetUnitField("pet", "petExperience") + local petNextXP = GetUnitField("pet", "petNextLevelExp") + local progress = (petXP / petNextXP) * 100 + print("Pet XP: " .. progress .. "%") +end + +Notes + + GUID fields return numbers but represent 64-bit values. Lua handles these as doubles. + Array indices are 1-based (Lua convention), not 0-based. + Returns nil if the unit doesn't exist or the field cannot be read. + Some fields may require specific game state to be meaningful (e.g., pet fields only work when you have a pet). + Unit flags and other bit fields require bitwise operations to interpret properly. + +Related Functions + + GetUnitData(unitToken) - Get all fields at once + GetUnitField(unitToken, fieldName) - Get a single field efficiently + GetSpellModifiers(spellId, modifierType) - Get spell modifiers + GetItemStats(itemId) - Get item data + GetSpellRec(spellId) - Get spell data + + diff --git a/superwow_wiki.txt b/superwow_wiki.txt new file mode 100644 index 0000000..8042ea6 --- /dev/null +++ b/superwow_wiki.txt @@ -0,0 +1,70 @@ + + +SuperWoW features list as of 11/04/2024: + + Healing appears on floating combat text. + Chatbubbles appear at 60 yard range instead of 20, and they can appear with nameplates activated + Chatbubbles work for raid, bg, whisper chat + Chat links of spells & crafting recipes now supported by client, can be grabbed directly from wowhead. + Fully absorbed damage is now correctly shown in combat log instead of saying "unit absorbs the attack". + Macros character limit increased from 255 to 510. + Your personal buff bar now shows all your buffs including ones that don't normally have an icon. + Added "UNIT_CASTEVENT" event that tracks units cast starts, finishes, interrupts, channels, and swings (differentiates mainhand & offhand) arg1: casterGUID. arg2: targetGUID. arg3: event type ("START", "CAST", "FAIL", "CHANNEL", "MAINHAND", "OFFHAND"). arg4: spell id. arg5: cast duration. + Added "RAW_COMBATLOG" event that signals the RAW version of all combat log events. arg1: original event name. arg2: event text with GUIDs + Raw combat log events are logged into WoWRawCombatLog.txt concurrently with original WoWCombatLog.txt if logging is enabled. + Combat log now appends owner name to combat log messages that have a owned unit such as a pet or totem. + The default event SPELLCAST_START now triggers on ranged weapon abilities like Aimed Shot & Multishot. + Removed range limit on seeing friendly players equipped items. This is to allow addons such as AdvancedVanillaCombatLog to build profiles of raid members without requiring every individual raid member to download a helper addon and communicate the information to each other. + Targeting circle spells such as Blizzard or Flamestrike no longer show the "I can't cast this ability while moving error" to stop from entering their targeting circle mode. + New CVar "BackgroundSound" to enable or disable background sound while tabbed out (default = "0", can be "0" or "1") + New CVar "UncapSounds". set to "1" to remove the hardcoded soundchannels limit. If you want true uncapped sound experience you still have accompany this CVAR with setting "SoundSoftwareChannels" and "SoundMaxHardwareChannels" to a high number (either by lua function, config.wtf edit or using VanillaTweaks) + New CVar "FoV" to set camera field of view (default = "1.57", can be any value from "0.1" to "3.14") + New CVar "SelectionCircleStyle" to set a different appearance for the target circle. + New CVar "LootSparkle" to toggle Sparkling effect on lootable treasure. + +Function changes: + + CastSpellByName function now can take unit as 2nd argument in addition to true/false OnSelf flag + UnitExists now also returns GUID of unit + UnitDebuff and UnitBuff now additionally return the id of the aura + Using UnitMana("player") as a druid now always returns your current form power and caster form mana at the same time. + frame:GetName(1) can now be used on nameplate frames to return the GUID of the attached unit. + SetRaidTarget now accepts 3rd argument "local" flag to assign a mark to your own client. This allows using target markers while solo. + LootSlot(slotid) that was previously used only to confirm "are you sure you want to loot this item" now has the usage format LootSlot(slotid [, forceloot]). LootSlot(slotid, 1) can now be used to actually loot a slot. + GetContainerItemInfo now returns item's charges instead of stacks if the item is not stackable & has charges. Charges are given as a negative number. + GetWeaponEnchantInfo() now can accept a friendly player (ex: party1) as argument. If used in this way, it gives the name of the temporary enchant on that player's mainhand & offhand. Old functionality is preserved for own player's enchant duration & stacks. + Macros can now be treated by the game as an item or a spell action by starting the macro with: "/tooltip spell:spellid" or "/tooltip item:itemid" respectively. + GetActionCount, GetActionCooldown, and ActionIsConsumable now work for macros returning the result of the linked spell or item. For example, you can create a macro that starts with "/tooltip item:18641" and all of these functions will treat it as if it's the item 18641 (dense dynamite), even if the macro will cast a different action on press. + GetActionText(actionButton) now additionally returns action type ("MACRO", "ITEM", "SPELL") and its id, or for macro, its index. a macro's index is the value used by GetMacroInfo(index). This allows you to differentiate between two macros on your actionbar that have the same name, or to find the id of an item or spell that is on your bar. + +New functions: + + GetPlayerBuffID(buffindex) function that returns id of the aura. + + CombatLogAdd("text"[, addToRawLog]) function that prints a message directly to the combatlog file. If flag is set, prints the message to the raw combatlog file instead. + + SpellInfo(spellid) function that returns information about a spell id (name, rank, texture file, minrange, max range to target). + + TrackUnit(unitid) function that adds a friendly unit to the minimap. + + UnitPosition(unitid) function that returns coordinates of a friendly unit. + + SetMouseoverUnit(unitid) function that sets as current hovered unit. Usage for unitframe addon makers: do SetMouseoverUnit(frameUnit) on enter, and SetMouseoverUnit() on leave to clear. This allows "mouseover" of other functions to work on that currently hovered frame's unit. + + Clickthrough(0/1) to turn off/on Clickthrough mode, Clickthrough() to simply return whether it's on. Clickthrough mode allows you to click through creature corpses that have no loot, to loot the creatures that are under them & covered by them. + + SetAutoloot(0/1) to turn off/on autoloot, SetAutoloot() to simply return whether it's on. The hardcoded activation of autoloot by holding shift has been removed. You now turn it on or off through this function). + + ImportFile("filename") reads a txt file in gamedirectory\imports and returns a string of its contents. + + ExportFile("filename", "text") creates a txt file in gamedirectory\imports and writes text in it. + + all functions that accept a unit as argument ("player", "target", "mouseover") now can accept an additional suffix "owner" which returns the owner of the unit (example, if you target a totem and do UnitName("targetowner") you'll get the name of the shaman). + + all functions taht accept a unit as argument ("player, "target", "mouseover") now can accept "mark1" to "mark8" as argument which returns the unit with the corresponding marker index. + + all functions that accept a unit as argument ("player", "target", "mouseover") now can accept the GUID of the unit, which can be obtained from UnitExists or GetName(1) on its nameplate. Suffixes can still be appended at the end of that string. + + Global variables SUPERWOW_STRING and SUPERWOW_VERSION give mod info for addons. + + diff --git a/unitxp_sp3_wiki.txt b/unitxp_sp3_wiki.txt new file mode 100644 index 0000000..326e7f5 --- /dev/null +++ b/unitxp_sp3_wiki.txt @@ -0,0 +1,189 @@ +UnitXP Service Pack 3 + +Making Vanilla 1.12 better: + + Nameplates would consider line of sight. + Better targeting functions for your TAB key. + Being able to adjust camera height/placement. + In-game Frame-per-second limiter. + Flash taskbar icon when receiving whisper. + High quality floating combat texts. + Screenshot generates JPEG instead of TGA. + Step by step debugger for Lua addon. + And more... + +UnitXP Service Pack 3 (abbr. xp3) is a game mod for Vanilla 1.12 which aims to bring the game modern and better experience. +Installation + + Downloads from https://codeberg.org/konaka/UnitXP_SP3/releases + +To setup the DLL file: + + Put all files under the same folder with the game + Edit your loader's list (which might be dlls.txt) to include UnitXP_SP3.dll + Start the game + +To setup the Lua addon: + + Move UnitXP_SP3_Addon folder into Interface/AddOns/ + Then in-game there is a minimap button to open its config interface. + +UnitXP_SP3_Addon + +When both the DLL and Lua addon are installed and loaded, there would be a minimap button to open up config interface. + +Also the command /unitxp would open it either. Most functions could be configured here. + +Targeting functions could be bound to keys via in-game Key Bindings menu. + +Addon preview +Additional Lua functions +Line of sight + +local bool_abc = UnitXP("inSight", UNIT_ID_1, UNIT_ID_2); + +This would return TRUE if UNIT_ID_1 is in sight of UNIT_ID_2. +Distance + +local double_abc = UnitXP("distanceBetween", UNIT_ID_1, UNIT_ID_2); + +This would return the distance between UNIT_ID_1 and UNIT_ID_2. The default meter is accurate for ranged spell like bolts or heals. + +local double_abc = UnitXP("distanceBetween", UNIT_ID_1, UNIT_ID_2, "AoE"); + +The AoE meter is accurate for novas. + +local double_abc = UnitXP("distanceBetween", UNIT_ID_1, UNIT_ID_2, "meleeAutoAttack"); + +The meleeAutoAttack meter is accurate for melee weapon swings. Beware that melee spell cast is not the same as melee weapon swings: Taunt has a different range than auto-attack. +Behind + +local bool_abc = UnitXP("behind", UNIT_ID_1, UNIT_ID_2); + +This would return TRUE if UNIT_ID_1 is behind UNIT_ID_2. + +There are some strange mobs whose back is not the π radian half of its back. You could set the range smaller by local back = UnitXP("behindThreshold", "set", 2); The third parameter by default is π/2 and it ranges from 0 to π the bigger of it, the smaller radian range would be judged as back. +Timer + +local timer_id = UnitXP("timer", "arm", 1000, 2000, callback_function_name); + +Arm a new timer which would trigger after 1000 milliseconds and would repeat every 2000 milliseconds to execute callback_function_name. You could pass 0 to stop repeating. + +The timer ID would be passed into callback function as 1st parameter. + +The xp3 timers run in a different thread from the game so that they would not cost game time to maintain. And they would trigger only when the time comes, rather than every frame. + +UnitXP("timer", "disarm", TIMER_ID); + +As xp3 timers are in a different thread, they would not be stopped when the game reload. Addon author needs to react to PLAYER_LOGOUT event and disarm running timers. + +local total = UnitXP("timer", "size"); + +Return total count of running timers. +OS notifications + +UnitXP("notify", "taskbarIcon"); + +UnitXP("notify", "systemSound"); + +Trigger a taskbar icon flash or a sound alert in operating system. These functions only effective when the game is in background. +Advanced Lua debugger + +The xp3 provides a step by step debugger for in-game Lua. The debugger program is in UnitXP_SP3-debug packages. + +To use the debugger: + + Run Demo Lua Debugger.exe. It is a C# program which would work on Windows (via .NET framework) and Linux (via Mono). + In the Lua code, add a breakpoint as UnitXP("debug", "breakpoint"); + Start the game and run the Lua code. + +The xp3 would connect to debugger via TCP port 2323. Lua source preview requires Demo Lua Debugger.exe being placed in game's folder. +Version and existence + +To tell if xp3 is exist in the game: + +local xp3 = pcall(UnitXP, "nop", "nop"); + +Returns TRUE for existing. + +local xp3exist, xp3buildTime = pcall(UnitXP, "version", "coffTimeDateStamp"); + +xp3buildTime is the time when xp3 is compiled and built. It is a UNIX timestamp so that you could compare as number to tell which one is newer. + +local xp3exist, xp3info = pcall(UnitXP, "version", "additionalInformation"); + +xp3info is a string contains some description about the version. It meant to be used by different xp3 maintainer to distinguish bloodline. +Performance profile + +local performance = UnitXP("performanceProfile", "get"); + +performance would be a string that shows performance factors of xp3. +Targeting + +These functions could be bound to keys via in-game Key Bindings menu. Also they could be called via Lua. + +Most targeting functions follow rules: + + When there is no selected target, select the nearest enemy. + Select enemies in line of sight of player character. + Select enemies in front of camera. It is possbile to narrow the targeting cone to be smaller than camera sight by UnitXP("target", "rangeCone", 2.5); The third parameter ranges from 2.0 to infinate, the bigger of it, the narrower targeting cone. + The fartherest range (farRange) by default is 41, which could be adjusted in range 26 to 60 by UnitXP("target", "farRange", 60);. + When the player is in-combat, only select enemies who is in-combat either. This limitation could be lifted by UnitXP("target", "disableInCombatFilter");. + Totems, Pets and Critters would be ignored. + Targeting functions return TRUE when they found a target, so that you could chain multiple functions. + +local found = UnitXP("target", "nearestEnemy"); + +Target the nearest enemy. It is the only one, no cycling. + +local found = UnitXP("target", "mostHP"); + +Target the enemy with most HP. It is the only one, no cycling. + +local found = UnitXP("target", "worldBoss"); + +Cycling around world bosses. + +local found = UnitXP("target", "nextEnemyInCycle"); + +local found = UnitXP("target", "previousEnemyInCycle"); + +Cycling around enemies. It is gurandteed that when repeatly trigger this function, all enemies in range would be selected for once. + +Ranged class might prefer this function as TAB key. + +local found = UnitXP("target", "nextEnemyConsideringDistance"); + +local found = UnitXP("target", "previousEnemyConsideringDistance"); + +Cycling around enemies. Enemies are classified into 3 range buckets 0 to 8, 8 to 25, 25 to farRange. This function would give priority to enemies in the near bucket, so that when there is a targetable enemy in near bucket, the rest is ignored. + +In 0 to 8 range the function cycling around all in-range enemies. + +In 8 to 25 range the function cycling around 3 nearest in-range enemies. + +In 25 to farRange range the function cycling around 5 nearest in-range enemies. + +local found = UnitXP("target", "nextMarkedEnemyInCycle"); + +local found = UnitXP("target", "previousMarkedEnemyInCycle"); + +Cycling around raid marked enemies. By default the order is: + + White Skull + Red Cross (X) + Blue Square + White Moon + Green Triangle + Purple Diamond + Orange Circle + Yellow Star + +You could supply a third parameter to reorder or limit to specific marks: + +local found = UnitXP("target", "nextMarkedEnemyInCycle", "138"); would cycle in order: + + Yellow Star which is index 1 + Purple Diamond which is index 3 + White Skull which is index 8 +