Files
SuperCleveRoidMacros/NampowerAPI.lua
T

1066 lines
33 KiB
Lua

--[[
NampowerAPI.lua - Nampower API Integration Layer
Provides wrapper functions for Nampower's extended Lua API with fallbacks
for older versions or when functions are unavailable.
New Nampower Functions Wrapped:
- GetSpellRec / GetSpellRecField - Spell record data from client DB
- GetItemStats / GetItemStatsField - Item stats from client DB
- GetUnitData / GetUnitField - Low-level unit field access
- GetSpellModifiers - Spell modifier calculations (talents, buffs, etc.)
Enhanced Functions (now accept spell name or "spellId:number"):
- GetSpellTexture, GetSpellName, GetSpellCooldown, GetSpellAutocast
- ToggleSpellAutocast, PickupSpell, CastSpell, IsCurrentCast, IsSpellPassive
Settings Integration:
- Reads from NampowerSettings addon when available
- Falls back to CVars when addon not present
]]
local _G = _G or getfenv(0)
local CleveRoids = _G.CleveRoids
-- Nampower API namespace
CleveRoids.NampowerAPI = CleveRoids.NampowerAPI or {}
local API = CleveRoids.NampowerAPI
--------------------------------------------------------------------------------
-- VERSION DETECTION
--------------------------------------------------------------------------------
-- Get Nampower version as major, minor, patch
-- Returns 0, 0, 0 if Nampower not installed
function API.GetVersion()
if GetNampowerVersion then
local major, minor, patch = GetNampowerVersion()
return major or 0, minor or 0, patch or 0
end
return 0, 0, 0
end
-- Check if installed version meets minimum requirement
function API.HasMinimumVersion(reqMajor, reqMinor, reqPatch)
local major, minor, patch = API.GetVersion()
if major > reqMajor then
return true
elseif major == reqMajor then
if minor > reqMinor then
return true
elseif minor == reqMinor and patch >= reqPatch then
return true
end
end
return false
end
-- Feature flags based on version
API.features = {
-- Base Nampower features (v2.0+)
hasNampower = (QueueSpellByName ~= nil),
hasSpellQueue = (QueueSpellByName ~= nil),
hasIsSpellInRange = (IsSpellInRange ~= nil),
hasGetCurrentCastingInfo = (GetCurrentCastingInfo ~= nil),
hasGetSpellIdForName = (GetSpellIdForName ~= nil),
-- Extended API (v2.8+)
hasGetSpellRec = (GetSpellRec ~= nil),
hasGetSpellRecField = (GetSpellRecField ~= nil),
hasGetItemStats = (GetItemStats ~= nil),
hasGetItemStatsField = (GetItemStatsField ~= nil),
hasGetUnitData = (GetUnitData ~= nil),
hasGetUnitField = (GetUnitField ~= nil),
hasGetSpellModifiers = (GetSpellModifiers ~= nil),
-- Enhanced spell functions (accept name/"spellId:number")
hasEnhancedSpellFunctions = false, -- Detected at runtime
}
-- Detect if enhanced spell functions are available (accept name/spellId:number)
local function DetectEnhancedSpellFunctions()
if not GetSpellTexture then return false end
-- Try calling GetSpellTexture with a spell name
-- If it doesn't error and returns something, enhanced functions are available
local success, result = pcall(function()
-- Use "Attack" which should exist for all characters
return GetSpellTexture("Attack")
end)
API.features.hasEnhancedSpellFunctions = success and (result ~= nil)
return API.features.hasEnhancedSpellFunctions
end
--------------------------------------------------------------------------------
-- NAMPOWER SETTINGS ACCESS
--------------------------------------------------------------------------------
-- Cache for settings to avoid repeated lookups
API.settingsCache = {}
API.settingsCacheTime = 0
API.SETTINGS_CACHE_DURATION = 5 -- Refresh every 5 seconds
-- All known Nampower CVars with their defaults
API.defaultSettings = {
NP_QueueCastTimeSpells = "1",
NP_QueueInstantSpells = "1",
NP_QueueChannelingSpells = "1",
NP_QueueTargetingSpells = "1",
NP_QueueOnSwingSpells = "0",
NP_QueueSpellsOnCooldown = "1",
NP_InterruptChannelsOutsideQueueWindow = "0",
NP_SpellQueueWindowMs = "500",
NP_OnSwingBufferCooldownMs = "500",
NP_ChannelQueueWindowMs = "1500",
NP_TargetingQueueWindowMs = "500",
NP_CooldownQueueWindowMs = "250",
NP_MinBufferTimeMs = "55",
NP_NonGcdBufferTimeMs = "100",
NP_MaxBufferIncreaseMs = "30",
NP_RetryServerRejectedSpells = "1",
NP_QuickcastTargetingSpells = "0",
NP_ReplaceMatchingNonGcdCategory = "0",
NP_OptimizeBufferUsingPacketTimings = "0",
NP_PreventRightClickTargetChange = "0",
NP_PreventRightClickPvPAttack = "1",
NP_DoubleCastToEndChannelEarly = "0",
NP_SpamProtectionEnabled = "1",
NP_ChannelLatencyReductionPercentage = "75",
NP_NameplateDistance = "41",
}
-- Get a Nampower setting value
-- Priority: NampowerSettings addon (per-character) > CVar > default
function API.GetSetting(settingName)
-- Check if NampowerSettings addon is loaded and has per-character settings enabled
if Nampower and Nampower.db and Nampower.db.profile then
if Nampower.db.profile.per_character_settings and Nampower.db.profile[settingName] ~= nil then
return Nampower.db.profile[settingName]
end
end
-- Fall back to CVar
local cvarValue = GetCVar(settingName)
if cvarValue then
return cvarValue
end
-- Return default if known
return API.defaultSettings[settingName]
end
-- Get a Nampower setting as a boolean
function API.GetSettingBool(settingName)
local value = API.GetSetting(settingName)
if type(value) == "boolean" then
return value
end
return value == "1" or value == true
end
-- Get a Nampower setting as a number
function API.GetSettingNumber(settingName)
local value = API.GetSetting(settingName)
return tonumber(value) or 0
end
-- Check if spell queuing is enabled for a given spell type
function API.IsQueueingEnabled(spellType)
if not API.features.hasNampower then
return false
end
if spellType == "cast" or spellType == "casttime" then
return API.GetSettingBool("NP_QueueCastTimeSpells")
elseif spellType == "instant" or spellType == "gcd" then
return API.GetSettingBool("NP_QueueInstantSpells")
elseif spellType == "channel" or spellType == "channeling" then
return API.GetSettingBool("NP_QueueChannelingSpells")
elseif spellType == "targeting" or spellType == "aoe" then
return API.GetSettingBool("NP_QueueTargetingSpells")
elseif spellType == "onswing" or spellType == "swing" then
return API.GetSettingBool("NP_QueueOnSwingSpells")
elseif spellType == "cooldown" then
return API.GetSettingBool("NP_QueueSpellsOnCooldown")
end
-- Default: all queuing enabled
return API.GetSettingBool("NP_QueueCastTimeSpells")
end
-- Get the queue window for a given spell type (in seconds)
function API.GetQueueWindow(spellType)
local ms = 500 -- default
if spellType == "channel" or spellType == "channeling" then
ms = API.GetSettingNumber("NP_ChannelQueueWindowMs")
elseif spellType == "targeting" or spellType == "aoe" then
ms = API.GetSettingNumber("NP_TargetingQueueWindowMs")
elseif spellType == "cooldown" then
ms = API.GetSettingNumber("NP_CooldownQueueWindowMs")
elseif spellType == "onswing" or spellType == "swing" then
ms = API.GetSettingNumber("NP_OnSwingBufferCooldownMs")
else
ms = API.GetSettingNumber("NP_SpellQueueWindowMs")
end
return ms / 1000 -- Convert to seconds
end
--------------------------------------------------------------------------------
-- SPELL RECORD API (GetSpellRec / GetSpellRecField)
--------------------------------------------------------------------------------
-- Cache for spell records
API.spellRecCache = {}
-- Get full spell record data
-- Returns nil if spell not found or API unavailable
function API.GetSpellRecord(spellId)
if not spellId or spellId == 0 then return nil end
-- Check cache first
if API.spellRecCache[spellId] then
return API.spellRecCache[spellId]
end
-- Use native function if available
if GetSpellRec then
local rec = GetSpellRec(spellId)
if rec then
API.spellRecCache[spellId] = rec
return rec
end
end
return nil
end
-- Get a specific field from spell record
-- Returns nil if not found, raises error if field name invalid (native behavior)
function API.GetSpellField(spellId, fieldName)
if not spellId or spellId == 0 then return nil end
-- Use native function if available (more efficient for single field)
if GetSpellRecField then
local success, result = pcall(GetSpellRecField, spellId, fieldName)
if success then
return result
end
-- If it errors on invalid field name, let it propagate
-- But if spell just not found, return nil
return nil
end
-- Fallback to full record lookup
local rec = API.GetSpellRecord(spellId)
if rec then
return rec[fieldName]
end
return nil
end
-- Get spell name by ID (enhanced, uses new API when available)
function API.GetSpellNameById(spellId)
if not spellId or spellId == 0 then return nil end
-- Try GetSpellNameAndRankForId first (Nampower function)
if GetSpellNameAndRankForId then
local name, rank = GetSpellNameAndRankForId(spellId)
if name then return name, rank end
end
-- Fall back to SpellInfo (SuperWoW)
if SpellInfo then
local name = SpellInfo(spellId)
if name then return name end
end
-- Fall back to GetSpellRecField
if GetSpellRecField then
local name = GetSpellRecField(spellId, "name")
local rank = GetSpellRecField(spellId, "rank")
if name then return name, rank end
end
return nil
end
-- Get spell cast time in seconds
function API.GetSpellCastTime(spellId)
local castTime = API.GetSpellField(spellId, "castTime")
if castTime then
return castTime / 1000 -- Convert from ms to seconds
end
return nil
end
-- Get spell school (0=Physical, 1=Holy, 2=Fire, 3=Nature, 4=Frost, 5=Shadow, 6=Arcane)
function API.GetSpellSchool(spellId)
return API.GetSpellField(spellId, "school")
end
-- Get spell mana cost
function API.GetSpellManaCost(spellId)
return API.GetSpellField(spellId, "manaCost")
end
-- Get spell range (max range in yards)
function API.GetSpellRange(spellId)
local rangeMax = API.GetSpellField(spellId, "rangeMax")
if rangeMax then
return rangeMax / 10 -- Convert from game units to yards
end
return nil
end
-- Get spell cooldown from record (base cooldown, not current)
function API.GetSpellBaseCooldown(spellId)
local recoveryTime = API.GetSpellField(spellId, "recoveryTime")
if recoveryTime then
return recoveryTime / 1000 -- Convert from ms to seconds
end
return nil
end
--------------------------------------------------------------------------------
-- ITEM STATS API (GetItemStats / GetItemStatsField)
--------------------------------------------------------------------------------
-- Cache for item stats
API.itemStatsCache = {}
-- Get full item stats data
function API.GetItemRecord(itemId)
if not itemId or itemId == 0 then return nil end
-- Check cache first
if API.itemStatsCache[itemId] then
return API.itemStatsCache[itemId]
end
-- Use native function if available
if GetItemStats then
local stats = GetItemStats(itemId)
if stats then
API.itemStatsCache[itemId] = stats
return stats
end
end
return nil
end
-- Get a specific field from item stats
function API.GetItemField(itemId, fieldName)
if not itemId or itemId == 0 then return nil end
-- Use native function if available (more efficient)
if GetItemStatsField then
local success, result = pcall(GetItemStatsField, itemId, fieldName)
if success then
return result
end
return nil
end
-- Fallback to full record lookup
local stats = API.GetItemRecord(itemId)
if stats then
return stats[fieldName]
end
return nil
end
-- Get item level
function API.GetItemLevel(itemId)
-- Use dedicated function if available
if GetItemLevel then
local success, result = pcall(GetItemLevel, itemId)
if success then return result end
end
return API.GetItemField(itemId, "itemLevel")
end
-- Get item name
function API.GetItemName(itemId)
return API.GetItemField(itemId, "displayName")
end
-- Get item quality (0=Poor, 1=Common, 2=Uncommon, 3=Rare, 4=Epic, 5=Legendary)
function API.GetItemQuality(itemId)
return API.GetItemField(itemId, "quality")
end
-- Get weapon speed in seconds
function API.GetWeaponSpeed(itemId)
local delay = API.GetItemField(itemId, "delay")
if delay then
return delay / 1000
end
return nil
end
--------------------------------------------------------------------------------
-- UNIT DATA API (GetUnitData / GetUnitField)
--------------------------------------------------------------------------------
-- Get full unit data (no caching - unit data changes frequently)
function API.GetUnitRecord(unitToken)
if not unitToken then return nil end
if GetUnitData then
return GetUnitData(unitToken)
end
return nil
end
-- Get a specific unit field
function API.GetUnitFieldValue(unitToken, fieldName)
if not unitToken then return nil end
-- Use native function if available (more efficient)
if GetUnitField then
local success, result = pcall(GetUnitField, unitToken, fieldName)
if success then
return result
end
return nil
end
-- Fallback to full record
local data = API.GetUnitRecord(unitToken)
if data then
return data[fieldName]
end
return nil
end
-- Get unit's current auras as spell IDs
function API.GetUnitAuras(unitToken)
return API.GetUnitFieldValue(unitToken, "aura")
end
-- Get unit resistances table
function API.GetUnitResistances(unitToken)
return API.GetUnitFieldValue(unitToken, "resistances")
end
-- Get specific resistance value
-- school: 1=Armor, 2=Holy, 3=Fire, 4=Nature, 5=Frost, 6=Shadow, 7=Arcane
function API.GetUnitResistance(unitToken, school)
local resistances = API.GetUnitResistances(unitToken)
if resistances and school then
return resistances[school]
end
return nil
end
-- Check if unit has a specific aura by spell ID
function API.UnitHasAura(unitToken, spellId)
local auras = API.GetUnitAuras(unitToken)
if not auras or not spellId then return false end
for _, auraId in ipairs(auras) do
if auraId == spellId then
return true
end
end
return false
end
--------------------------------------------------------------------------------
-- SPELL MODIFIERS API (GetSpellModifiers)
--------------------------------------------------------------------------------
-- Modifier type constants
API.MODIFIER_DAMAGE = 0
API.MODIFIER_DURATION = 1
API.MODIFIER_THREAT = 2
API.MODIFIER_ATTACK_POWER = 3
API.MODIFIER_CHARGES = 4
API.MODIFIER_RANGE = 5
API.MODIFIER_RADIUS = 6
API.MODIFIER_CRITICAL_CHANCE = 7
API.MODIFIER_ALL_EFFECTS = 8
API.MODIFIER_NOT_LOSE_CASTING_TIME = 9
API.MODIFIER_CASTING_TIME = 10
API.MODIFIER_COOLDOWN = 11
API.MODIFIER_SPEED = 12
API.MODIFIER_COST = 14
API.MODIFIER_CRIT_DAMAGE_BONUS = 15
API.MODIFIER_RESIST_MISS_CHANCE = 16
API.MODIFIER_JUMP_TARGETS = 17
API.MODIFIER_CHANCE_OF_SUCCESS = 18
API.MODIFIER_ACTIVATION_TIME = 19
API.MODIFIER_EFFECT_PAST_FIRST = 20
API.MODIFIER_CASTING_TIME_OLD = 21
API.MODIFIER_DOT = 22
API.MODIFIER_HASTE = 23
API.MODIFIER_SPELL_BONUS_DAMAGE = 24
API.MODIFIER_MULTIPLE_VALUE = 27
API.MODIFIER_RESIST_DISPEL_CHANCE = 28
-- Get spell modifiers
-- Returns: flatMod, percentMod, hasModifier
function API.GetModifiers(spellId, modifierType)
if not GetSpellModifiers then
return 0, 0, false
end
if not spellId or spellId == 0 then
return 0, 0, false
end
local flat, percent, ret = GetSpellModifiers(spellId, modifierType)
return flat or 0, percent or 0, ret and ret ~= 0
end
-- Get duration modifier for a spell (useful for debuff tracking)
-- Returns modified duration given base duration
function API.GetModifiedDuration(spellId, baseDuration)
if not baseDuration then return nil end
local flat, percent, hasModifier = API.GetModifiers(spellId, API.MODIFIER_DURATION)
if not hasModifier then
return baseDuration
end
-- Apply flat modifier first, then percentage
local modified = baseDuration + flat
if percent ~= 0 then
modified = modified * (1 + percent / 100)
end
return modified
end
-- Get damage modifier for a spell
function API.GetDamageModifier(spellId)
local flat, percent = API.GetModifiers(spellId, API.MODIFIER_DAMAGE)
return flat, percent
end
-- Get cooldown modifier for a spell
function API.GetCooldownModifier(spellId)
local flat, percent = API.GetModifiers(spellId, API.MODIFIER_COOLDOWN)
return flat, percent
end
-- Get cast time modifier for a spell
function API.GetCastTimeModifier(spellId)
local flat, percent = API.GetModifiers(spellId, API.MODIFIER_CASTING_TIME)
return flat, percent
end
-- Get cost modifier for a spell
function API.GetCostModifier(spellId)
local flat, percent = API.GetModifiers(spellId, API.MODIFIER_COST)
return flat, percent
end
--------------------------------------------------------------------------------
-- ENHANCED SPELL LOOKUP (using new "spellId:number" and name syntax)
--------------------------------------------------------------------------------
-- Convert spell identifier to spellId:number format if needed
-- Input can be: spellSlot (number), "spellId:123", or "Spell Name"
function API.NormalizeSpellIdentifier(identifier, bookType)
if not identifier then return nil end
-- Already in spellId:number format
if type(identifier) == "string" and string.find(identifier, "^spellId:") then
return identifier
end
-- If it's a number and enhanced functions exist, keep as-is (slot) or convert to spellId
if type(identifier) == "number" then
if API.features.hasEnhancedSpellFunctions then
-- Could be a spell slot - check if we have GetSpellSlotTypeIdForName to get the ID
if identifier > 0 and identifier < 1000 then
-- Likely a spell slot, use as-is
return identifier, bookType or BOOKTYPE_SPELL
else
-- Likely a spell ID, convert to spellId:number format
return "spellId:" .. identifier
end
end
return identifier, bookType
end
-- It's a spell name
if type(identifier) == "string" then
-- If enhanced functions available, pass name directly
if API.features.hasEnhancedSpellFunctions then
return identifier
end
-- Fall back to manual lookup
local spell = CleveRoids.GetSpell and CleveRoids.GetSpell(identifier)
if spell then
return spell.spellSlot, spell.bookType
end
end
return nil
end
-- Get spell texture (works with name, spellId:number, or slot)
function API.GetSpellTexture(identifier, bookType)
if API.features.hasEnhancedSpellFunctions then
-- Pass directly to native function
local id = API.NormalizeSpellIdentifier(identifier)
if id then
return GetSpellTexture(id, bookType)
end
else
-- Fall back to slot-based lookup
local slot, book = API.NormalizeSpellIdentifier(identifier, bookType)
if slot then
return GetSpellTexture(slot, book or BOOKTYPE_SPELL)
end
end
return nil
end
-- Get spell name (works with name, spellId:number, or slot)
function API.GetSpellName(identifier, bookType)
if API.features.hasEnhancedSpellFunctions then
local id = API.NormalizeSpellIdentifier(identifier)
if id then
return GetSpellName(id, bookType)
end
else
local slot, book = API.NormalizeSpellIdentifier(identifier, bookType)
if slot then
return GetSpellName(slot, book or BOOKTYPE_SPELL)
end
end
return nil
end
-- Get spell cooldown (works with name, spellId:number, or slot)
-- Returns: start, duration (same as native)
function API.GetSpellCooldown(identifier, bookType)
if API.features.hasEnhancedSpellFunctions then
local id = API.NormalizeSpellIdentifier(identifier)
if id then
return GetSpellCooldown(id, bookType)
end
else
local slot, book = API.NormalizeSpellIdentifier(identifier, bookType)
if slot then
return GetSpellCooldown(slot, book or BOOKTYPE_SPELL)
end
end
return nil
end
-- Check if spell is passive
function API.IsSpellPassive(identifier, bookType)
if API.features.hasEnhancedSpellFunctions then
local id = API.NormalizeSpellIdentifier(identifier)
if id then
return IsSpellPassive(id, bookType)
end
else
local slot, book = API.NormalizeSpellIdentifier(identifier, bookType)
if slot then
return IsSpellPassive(slot, book or BOOKTYPE_SPELL)
end
end
return nil
end
--------------------------------------------------------------------------------
-- SPELL ID LOOKUP UTILITIES
--------------------------------------------------------------------------------
-- Cache for spell ID lookups
API.spellIdCache = {}
-- Get spell ID from name (cached)
function API.GetSpellIdFromName(spellName)
if not spellName then return nil end
-- Check cache
if API.spellIdCache[spellName] then
return API.spellIdCache[spellName]
end
-- Use Nampower's GetSpellIdForName
if GetSpellIdForName then
local spellId = GetSpellIdForName(spellName)
if spellId and spellId > 0 then
API.spellIdCache[spellName] = spellId
return spellId
end
end
return nil
end
-- Get spell slot, book type, and ID from name
function API.GetSpellSlotInfo(spellName)
if not spellName then return nil end
-- Use Nampower's GetSpellSlotTypeIdForName
if GetSpellSlotTypeIdForName then
local slot, bookType, spellId = GetSpellSlotTypeIdForName(spellName)
if slot and slot > 0 then
return slot, bookType, spellId
end
end
-- Fallback to CleveRoids.GetSpell
if CleveRoids.GetSpell then
local spell = CleveRoids.GetSpell(spellName)
if spell then
local spellId = API.GetSpellIdFromName(spellName)
return spell.spellSlot, spell.bookType, spellId
end
end
return nil
end
--------------------------------------------------------------------------------
-- INITIALIZATION
--------------------------------------------------------------------------------
-- Initialize the API module
function API.Initialize()
-- Detect enhanced spell functions
DetectEnhancedSpellFunctions()
-- Log available features
local version = string.format("%d.%d.%d", API.GetVersion())
if API.features.hasNampower then
CleveRoids.Print("NampowerAPI: v" .. version .. " detected")
local features = {}
if API.features.hasGetSpellRec then table.insert(features, "SpellRec") end
if API.features.hasGetItemStats then table.insert(features, "ItemStats") end
if API.features.hasGetUnitData then table.insert(features, "UnitData") end
if API.features.hasGetSpellModifiers then table.insert(features, "SpellModifiers") end
if API.features.hasEnhancedSpellFunctions then table.insert(features, "EnhancedSpellFuncs") end
if table.getn(features) > 0 then
CleveRoids.Print(" Extended API: " .. table.concat(features, ", "))
end
end
end
-- Clear caches (call on respec/spell change)
function API.ClearCaches()
API.spellRecCache = {}
API.itemStatsCache = {}
API.spellIdCache = {}
API.settingsCache = {}
API.spellTypeCache = {}
end
--------------------------------------------------------------------------------
-- CONVENIENCE WRAPPERS FOR EXISTING CODE
--------------------------------------------------------------------------------
-- Check if a spell is in range (wrapper around IsSpellInRange with fallbacks)
function API.IsSpellInRange(spellIdentifier, unit)
unit = unit or "target"
if not IsSpellInRange then
return nil -- Can't determine
end
-- Convert name to ID if needed
local checkValue = spellIdentifier
if type(spellIdentifier) == "string" and not string.find(spellIdentifier, "^spellId:") then
local spellId = API.GetSpellIdFromName(spellIdentifier)
if spellId and spellId > 0 then
checkValue = spellId
end
end
return IsSpellInRange(checkValue, unit)
end
-- Check if a spell is usable (wrapper around IsSpellUsable)
function API.IsSpellUsable(spellIdentifier)
if not _G.IsSpellUsable then
return nil
end
-- If enhanced functions available, pass directly
if API.features.hasEnhancedSpellFunctions then
return _G.IsSpellUsable(spellIdentifier)
end
-- Convert to ID if name
if type(spellIdentifier) == "string" then
local spellId = API.GetSpellIdFromName(spellIdentifier)
if spellId then
return _G.IsSpellUsable(spellId)
end
end
return _G.IsSpellUsable(spellIdentifier)
end
--------------------------------------------------------------------------------
-- SMART CASTING SYSTEM
--------------------------------------------------------------------------------
-- Spell type constants
API.SPELL_TYPE_UNKNOWN = 0
API.SPELL_TYPE_CAST = 1 -- Has cast time
API.SPELL_TYPE_INSTANT = 2 -- Instant, on GCD
API.SPELL_TYPE_CHANNEL = 3 -- Channeled spell
API.SPELL_TYPE_ON_SWING = 4 -- Next-melee (Heroic Strike, etc.)
API.SPELL_TYPE_NON_GCD = 5 -- Instant, not on GCD (trinkets, etc.)
-- Known on-swing spells (by name, localized via CleveRoids.Localized when available)
API.onSwingSpells = {
["Heroic Strike"] = true,
["Cleave"] = true,
["Maul"] = true,
["Slam"] = true,
["Raptor Strike"] = true,
["Mongoose Bite"] = true,
}
-- Known channeled spells (by name)
API.channeledSpells = {
["Arcane Missiles"] = true,
["Blizzard"] = true,
["Drain Life"] = true,
["Drain Mana"] = true,
["Drain Soul"] = true,
["Evocation"] = true,
["Health Funnel"] = true,
["Hellfire"] = true,
["Hurricane"] = true,
["Mind Flay"] = true,
["Rain of Fire"] = true,
["Tranquility"] = true,
["Volley"] = true,
["Mind Soothe"] = true,
["Mind Vision"] = true,
["First Aid"] = true, -- Bandaging
}
-- Cache for spell type lookups
API.spellTypeCache = {}
-- Determine spell type for a given spell
-- Returns: SPELL_TYPE_* constant
function API.GetSpellType(spellName)
if not spellName then return API.SPELL_TYPE_UNKNOWN end
-- Strip rank from name for cache lookup
local baseName = string.gsub(spellName, "%s*%([Rr]ank%s*%d+%)%s*$", "")
-- Check cache first
if API.spellTypeCache[baseName] then
return API.spellTypeCache[baseName]
end
local spellType = API.SPELL_TYPE_UNKNOWN
-- Check known on-swing spells first (highest priority)
if API.onSwingSpells[baseName] then
spellType = API.SPELL_TYPE_ON_SWING
API.spellTypeCache[baseName] = spellType
return spellType
end
-- Check known channeled spells
if API.channeledSpells[baseName] then
spellType = API.SPELL_TYPE_CHANNEL
API.spellTypeCache[baseName] = spellType
return spellType
end
-- Try to use GetSpellRec for accurate detection
local spellId = API.GetSpellIdFromName(spellName)
if spellId and spellId > 0 and API.features.hasGetSpellRec then
local rec = API.GetSpellRecord(spellId)
if rec then
-- Check for channeled via attributes
-- SPELL_ATTR_EX_CHANNELED = 4 (0x00000004) in attributesEx
local attrEx = rec.attributesEx or 0
if bit and bit.band(attrEx, 4) ~= 0 then
spellType = API.SPELL_TYPE_CHANNEL
API.spellTypeCache[baseName] = spellType
return spellType
end
-- Check cast time
local castTime = rec.castTime or 0
if castTime > 0 then
spellType = API.SPELL_TYPE_CAST
else
-- Instant spell - check if it's on GCD
-- startRecoveryTime > 0 means it triggers GCD
local recoveryTime = rec.startRecoveryTime or 0
if recoveryTime > 0 then
spellType = API.SPELL_TYPE_INSTANT
else
spellType = API.SPELL_TYPE_NON_GCD
end
end
API.spellTypeCache[baseName] = spellType
return spellType
end
end
-- Fallback: assume instant if no other info
-- This is safe because Nampower will handle it correctly anyway
spellType = API.SPELL_TYPE_INSTANT
API.spellTypeCache[baseName] = spellType
return spellType
end
-- Get the queue setting name for a spell type
local function GetQueueSettingForType(spellType)
if spellType == API.SPELL_TYPE_CAST then
return "NP_QueueCastTimeSpells"
elseif spellType == API.SPELL_TYPE_INSTANT then
return "NP_QueueInstantSpells"
elseif spellType == API.SPELL_TYPE_CHANNEL then
return "NP_QueueChannelingSpells"
elseif spellType == API.SPELL_TYPE_ON_SWING then
return "NP_QueueOnSwingSpells"
elseif spellType == API.SPELL_TYPE_NON_GCD then
return "NP_QueueInstantSpells" -- Non-GCD uses instant setting
end
return nil
end
-- Check if queuing is enabled for a specific spell
function API.IsSpellQueueingEnabled(spellName)
if not API.features.hasSpellQueue then
return false
end
local spellType = API.GetSpellType(spellName)
local settingName = GetQueueSettingForType(spellType)
if settingName then
return API.GetSettingBool(settingName)
end
-- Default: use cast time spell setting
return API.GetSettingBool("NP_QueueCastTimeSpells")
end
-- Smart cast function that uses QueueSpellByName when appropriate
-- Returns: true if cast was attempted, false otherwise
-- Parameters:
-- spellName: The spell name (with optional rank)
-- target: Optional target unit (for SuperWoW CastSpellByName)
-- forceQueue: If true, always use QueueSpellByName (if available)
-- forceNoQueue: If true, never use QueueSpellByName
function API.SmartCast(spellName, target, forceQueue, forceNoQueue)
if not spellName then return false end
-- Determine whether to use queuing
local useQueue = false
if forceNoQueue then
useQueue = false
elseif forceQueue then
useQueue = API.features.hasSpellQueue
else
-- Check if queuing is enabled for this spell type
useQueue = API.IsSpellQueueingEnabled(spellName)
end
-- Cast the spell
if useQueue and QueueSpellByName then
-- Use Nampower's queue system
QueueSpellByName(spellName)
return true
elseif CastSpellByName then
-- Use standard casting
if target and CleveRoids.hasSuperwow then
-- SuperWoW supports target parameter
CastSpellByName(spellName, target)
else
CastSpellByName(spellName)
end
return true
end
return false
end
-- Cast without queuing (uses CastSpellByNameNoQueue if available)
function API.CastNoQueue(spellName, target)
if not spellName then return false end
-- Use Nampower's no-queue function if available
if CastSpellByNameNoQueue then
CastSpellByNameNoQueue(spellName)
return true
end
-- Fall back to standard cast
if CastSpellByName then
if target and CleveRoids.hasSuperwow then
CastSpellByName(spellName, target)
else
CastSpellByName(spellName)
end
return true
end
return false
end
-- Force queue a spell (uses QueueSpellByName directly)
function API.ForceQueue(spellName)
if not spellName then return false end
if QueueSpellByName then
QueueSpellByName(spellName)
return true
end
-- Fall back to standard cast if queue not available
if CastSpellByName then
CastSpellByName(spellName)
return true
end
return false
end
-- Queue a script to run after current cast (uses QueueScript if available)
function API.QueueScript(script, priority)
if not script then return false end
if QueueScript then
QueueScript(script, priority or 1)
return true
end
return false
end
-- Clear spell type cache (call on spec change)
function API.ClearSpellTypeCache()
API.spellTypeCache = {}
end
-- Expose API globally for other addons
_G.CleveRoidsNampowerAPI = API