mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 15:46:56 +00:00
+724
-102
@@ -1,140 +1,762 @@
|
||||
-- Nampower Integration for pfUI
|
||||
-- https://github.com/pepopo978/nampower
|
||||
-- Nampower integration module
|
||||
-- Provides spell queue indicator and enhanced cast information
|
||||
-- Requires Nampower DLL: https://gitea.com/avitasia/nampower
|
||||
|
||||
pfUI:RegisterModule("nampower", "vanilla", function ()
|
||||
-- Detect Nampower availability
|
||||
pfUI.api.hasNampower = GetNampowerVersion ~= nil
|
||||
-- Only load if Nampower is available
|
||||
if not GetNampowerVersion then return end
|
||||
|
||||
if not pfUI.api.hasNampower then return end
|
||||
-- Safe wrapper for SuperWoW's GetSpellNameAndRankForId (may not be available)
|
||||
local function SafeGetSpellNameAndRank(spellId)
|
||||
if not GetSpellNameAndRankForId then return nil, nil end
|
||||
local success, name, rank = pcall(GetSpellNameAndRankForId, spellId)
|
||||
if success then
|
||||
return name, rank
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Enhanced health tracking using Nampower
|
||||
if pfUI.libhealth and GetUnitField then
|
||||
local originalGetUnitHealth = pfUI.libhealth.GetUnitHealth
|
||||
pfUI.libhealth.GetUnitHealth = function(self, unitstr)
|
||||
-- Try Nampower for direct health values
|
||||
local success, health = pcall(GetUnitField, unitstr, "health")
|
||||
if success and health then
|
||||
local _, max = pcall(GetUnitField, unitstr, "maxHealth")
|
||||
if max and max > 0 then
|
||||
return health, max, true
|
||||
local rawborder, border = GetBorderSize()
|
||||
|
||||
-- Spell Queue Indicator
|
||||
-- Shows the currently queued spell icon near the castbar
|
||||
if C.unitframes.spellqueue == "1" then
|
||||
local size = tonumber(C.unitframes.spellqueuesize) or 32
|
||||
|
||||
pfUI.spellqueue = CreateFrame("Frame", "pfSpellQueue", UIParent)
|
||||
pfUI.spellqueue:SetFrameStrata("HIGH")
|
||||
pfUI.spellqueue:SetWidth(size)
|
||||
pfUI.spellqueue:SetHeight(size)
|
||||
pfUI.spellqueue:Hide()
|
||||
|
||||
-- Position near player castbar if available
|
||||
if pfUI.castbar and pfUI.castbar.player then
|
||||
pfUI.spellqueue:SetPoint("LEFT", pfUI.castbar.player, "RIGHT", border*3, 0)
|
||||
else
|
||||
pfUI.spellqueue:SetPoint("CENTER", UIParent, "CENTER", 100, -100)
|
||||
end
|
||||
|
||||
pfUI.spellqueue.icon = pfUI.spellqueue:CreateTexture("OVERLAY")
|
||||
pfUI.spellqueue.icon:SetAllPoints(pfUI.spellqueue)
|
||||
pfUI.spellqueue.icon:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
UpdateMovable(pfUI.spellqueue)
|
||||
CreateBackdrop(pfUI.spellqueue)
|
||||
CreateBackdropShadow(pfUI.spellqueue)
|
||||
|
||||
-- Event codes from Nampower
|
||||
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 queue = CreateFrame("Frame")
|
||||
queue:RegisterEvent("SPELL_QUEUE_EVENT")
|
||||
queue:SetScript("OnEvent", function()
|
||||
local eventCode = arg1
|
||||
local spellId = arg2
|
||||
|
||||
if eventCode == NORMAL_QUEUED or eventCode == NON_GCD_QUEUED or eventCode == ON_SWING_QUEUED then
|
||||
-- Get spell texture from SpellInfo (SuperWoW) or GetSpellTexture
|
||||
local texture
|
||||
if SpellInfo then
|
||||
local _, _, tex = SpellInfo(spellId)
|
||||
texture = tex
|
||||
end
|
||||
|
||||
if texture then
|
||||
pfUI.spellqueue.icon:SetTexture(texture)
|
||||
pfUI.spellqueue:Show()
|
||||
end
|
||||
elseif eventCode == NORMAL_QUEUE_POPPED or eventCode == NON_GCD_QUEUE_POPPED or eventCode == ON_SWING_QUEUE_POPPED then
|
||||
pfUI.spellqueue:Hide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Enhanced Debuff Tracking using Nampower events
|
||||
-- DEBUFF_ADDED_OTHER/DEBUFF_REMOVED_OTHER provide accurate debuff tracking with spellId
|
||||
if libdebuff then
|
||||
-- Storage for GUID-based debuff tracking
|
||||
pfUI.nampower_debuffs = pfUI.nampower_debuffs or {}
|
||||
local debuffdb = pfUI.nampower_debuffs
|
||||
|
||||
-- Get player GUID for tracking own debuffs
|
||||
local playerGuid
|
||||
|
||||
local debuffTracker = CreateFrame("Frame")
|
||||
debuffTracker:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
debuffTracker:RegisterEvent("DEBUFF_ADDED_OTHER")
|
||||
debuffTracker:RegisterEvent("DEBUFF_REMOVED_OTHER")
|
||||
debuffTracker:RegisterEvent("DEBUFF_ADDED_SELF")
|
||||
debuffTracker:RegisterEvent("DEBUFF_REMOVED_SELF")
|
||||
|
||||
debuffTracker:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
-- Cache player GUID
|
||||
if UnitExists then
|
||||
local _, guid = UnitExists("player")
|
||||
playerGuid = guid
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- DEBUFF events: arg1=guid, arg2=slot, arg3=spellId, arg4=stackCount, arg5=auraLevel
|
||||
local guid = arg1
|
||||
local slot = arg2
|
||||
local spellId = arg3
|
||||
local stackCount = arg4
|
||||
local auraLevel = arg5
|
||||
|
||||
if not guid or not spellId then return end
|
||||
|
||||
if event == "DEBUFF_ADDED_OTHER" or event == "DEBUFF_ADDED_SELF" then
|
||||
-- Initialize storage for this GUID
|
||||
if not debuffdb[guid] then debuffdb[guid] = {} end
|
||||
|
||||
-- Get spell info
|
||||
local spellName, spellRank, texture
|
||||
if SpellInfo then
|
||||
spellName, spellRank, texture = SpellInfo(spellId)
|
||||
end
|
||||
if not spellName then
|
||||
spellName, spellRank = SafeGetSpellNameAndRank(spellId)
|
||||
end
|
||||
|
||||
if spellName then
|
||||
-- Get duration from libdebuff's duration table or GetSpellRec
|
||||
local duration = 0
|
||||
if libdebuff.GetDuration then
|
||||
duration = libdebuff:GetDuration(spellName, spellRank)
|
||||
end
|
||||
|
||||
-- Try GetSpellRec for duration if libdebuff doesn't have it
|
||||
if duration == 0 and GetSpellRec and spellId then
|
||||
local success, spellRec = pcall(GetSpellRec, spellId)
|
||||
if success and spellRec and spellRec.durationIndex and spellRec.durationIndex > 0 then
|
||||
-- Duration index maps to spell duration - common values:
|
||||
-- This is a rough approximation since we don't have the duration table
|
||||
duration = 30 -- Default fallback
|
||||
end
|
||||
end
|
||||
|
||||
-- Store debuff data
|
||||
debuffdb[guid][spellId] = {
|
||||
spellId = spellId,
|
||||
name = spellName,
|
||||
rank = spellRank,
|
||||
texture = texture,
|
||||
stacks = stackCount or 1,
|
||||
start = GetTime(),
|
||||
duration = duration,
|
||||
slot = slot,
|
||||
auraLevel = auraLevel,
|
||||
caster = (event == "DEBUFF_ADDED_SELF" or guid == playerGuid) and "player" or nil
|
||||
}
|
||||
|
||||
-- Also update libdebuff's internal tracking if we have unit info
|
||||
local unitName = UnitName and guid and UnitName(guid)
|
||||
local unitLevel = UnitLevel and guid and UnitLevel(guid)
|
||||
if unitName and duration > 0 then
|
||||
libdebuff:AddEffect(unitName, unitLevel or 0, spellName, duration, "player")
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == "DEBUFF_REMOVED_OTHER" or event == "DEBUFF_REMOVED_SELF" then
|
||||
-- Remove debuff from tracking
|
||||
if debuffdb[guid] and debuffdb[guid][spellId] then
|
||||
debuffdb[guid][spellId] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Enhanced UnitDebuff function that uses Nampower data
|
||||
-- This provides more accurate debuff information when available
|
||||
local originalUnitDebuff = libdebuff.UnitDebuff
|
||||
function libdebuff:UnitDebuffNampower(unit, id)
|
||||
-- First try the original method
|
||||
local effect, rank, texture, stacks, dtype, duration, timeleft, caster = originalUnitDebuff(self, unit, id)
|
||||
|
||||
-- If we have Nampower data for this unit, try to enhance it
|
||||
if not UnitExists then return effect, rank, texture, stacks, dtype, duration, timeleft, caster end
|
||||
|
||||
local exists, guid = UnitExists(unit)
|
||||
if not exists or not guid or not debuffdb[guid] then
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- Find the debuff by slot
|
||||
for spellId, data in pairs(debuffdb[guid]) do
|
||||
if data.slot == id then
|
||||
-- Use Nampower data for more accurate timing
|
||||
if data.duration and data.duration > 0 and data.start then
|
||||
duration = data.duration
|
||||
timeleft = data.duration + data.start - GetTime()
|
||||
if timeleft < 0 then timeleft = 0 end
|
||||
caster = data.caster
|
||||
stacks = data.stacks or stacks
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback to original estimation
|
||||
if originalGetUnitHealth then
|
||||
return originalGetUnitHealth(self, unitstr)
|
||||
end
|
||||
return 0, 0, false
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- Also enhance GetUnitHealthByName
|
||||
local originalGetUnitHealthByName = pfUI.libhealth.GetUnitHealthByName
|
||||
pfUI.libhealth.GetUnitHealthByName = function(self, unit, level, cur, max)
|
||||
-- If we have actual values from Nampower, use them
|
||||
if cur and max and cur > 100 then
|
||||
return cur, max, true
|
||||
end
|
||||
-- Fallback to original
|
||||
if originalGetUnitHealthByName then
|
||||
return originalGetUnitHealthByName(self, unit, level, cur, max)
|
||||
end
|
||||
return cur or 0, max or 0, false
|
||||
end
|
||||
-- Expose enhanced function
|
||||
pfUI.api.libdebuff_nampower = libdebuff.UnitDebuffNampower
|
||||
end
|
||||
|
||||
-- Enhanced spell range checking
|
||||
if IsSpellInRange then
|
||||
pfUI.api.IsSpellInRange = function(spell, unit)
|
||||
local success, result = pcall(IsSpellInRange, spell, unit or "target")
|
||||
if success then
|
||||
if result == 1 then
|
||||
return true
|
||||
elseif result == 0 then
|
||||
return false
|
||||
-- Enhanced buff tracking using BUFF events
|
||||
if C.unitframes.nampower_buffs == "1" then
|
||||
pfUI.nampower_buffs = pfUI.nampower_buffs or {}
|
||||
local buffdb = pfUI.nampower_buffs
|
||||
|
||||
local buffTracker = CreateFrame("Frame")
|
||||
buffTracker:RegisterEvent("BUFF_ADDED_OTHER")
|
||||
buffTracker:RegisterEvent("BUFF_REMOVED_OTHER")
|
||||
buffTracker:RegisterEvent("BUFF_ADDED_SELF")
|
||||
buffTracker:RegisterEvent("BUFF_REMOVED_SELF")
|
||||
|
||||
buffTracker:SetScript("OnEvent", function()
|
||||
local guid = arg1
|
||||
local slot = arg2
|
||||
local spellId = arg3
|
||||
local stackCount = arg4
|
||||
local auraLevel = arg5
|
||||
|
||||
if not guid or not spellId then return end
|
||||
|
||||
if event == "BUFF_ADDED_OTHER" or event == "BUFF_ADDED_SELF" then
|
||||
if not buffdb[guid] then buffdb[guid] = {} end
|
||||
|
||||
local spellName, spellRank, texture
|
||||
if SpellInfo then
|
||||
spellName, spellRank, texture = SpellInfo(spellId)
|
||||
end
|
||||
if not spellName then
|
||||
spellName, spellRank = SafeGetSpellNameAndRank(spellId)
|
||||
end
|
||||
|
||||
if spellName then
|
||||
buffdb[guid][spellId] = {
|
||||
spellId = spellId,
|
||||
name = spellName,
|
||||
rank = spellRank,
|
||||
texture = texture,
|
||||
stacks = stackCount or 1,
|
||||
start = GetTime(),
|
||||
slot = slot,
|
||||
auraLevel = auraLevel
|
||||
}
|
||||
end
|
||||
elseif event == "BUFF_REMOVED_OTHER" or event == "BUFF_REMOVED_SELF" then
|
||||
if buffdb[guid] and buffdb[guid][spellId] then
|
||||
buffdb[guid][spellId] = nil
|
||||
end
|
||||
end
|
||||
return nil -- Not applicable
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Expose cast info function
|
||||
if GetCastInfo then
|
||||
pfUI.api.GetCastInfo = function()
|
||||
local success, result = pcall(GetCastInfo)
|
||||
if success then
|
||||
return result
|
||||
-- Direct Aura Access API using GetUnitField
|
||||
-- Much faster than tooltip scanning - reads aura arrays directly from unit fields
|
||||
if GetUnitField then
|
||||
pfUI.api.GetUnitAuras = function(unit)
|
||||
local auras = GetUnitField(unit, "aura")
|
||||
local auraLevels = GetUnitField(unit, "auraLevels")
|
||||
local auraStacks = GetUnitField(unit, "auraApplications")
|
||||
|
||||
if not auras then return nil end
|
||||
|
||||
local result = {}
|
||||
for i = 1, 48 do
|
||||
local spellId = auras[i]
|
||||
if spellId and spellId > 0 then
|
||||
local name, rank, texture
|
||||
if SpellInfo then
|
||||
name, rank, texture = SpellInfo(spellId)
|
||||
end
|
||||
if not name then
|
||||
name, rank = SafeGetSpellNameAndRank(spellId)
|
||||
end
|
||||
|
||||
result[i] = {
|
||||
spellId = spellId,
|
||||
name = name,
|
||||
rank = rank,
|
||||
texture = texture,
|
||||
level = auraLevels and auraLevels[i] or 0,
|
||||
stacks = auraStacks and auraStacks[i] or 1,
|
||||
isBuff = i <= 32, -- First 32 slots are buffs, rest are debuffs
|
||||
}
|
||||
end
|
||||
end
|
||||
return nil
|
||||
return result
|
||||
end
|
||||
|
||||
-- Quick check if unit has specific aura by spellId
|
||||
pfUI.api.UnitHasAura = function(unit, spellId)
|
||||
local auras = GetUnitField(unit, "aura")
|
||||
if not auras then return false end
|
||||
for i = 1, 48 do
|
||||
if auras[i] == spellId then return true, i end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Get unit resistances directly
|
||||
pfUI.api.GetUnitResistances = function(unit)
|
||||
local res = GetUnitField(unit, "resistances")
|
||||
if not res then return nil end
|
||||
return {
|
||||
armor = res[1] or 0,
|
||||
holy = res[2] or 0,
|
||||
fire = res[3] or 0,
|
||||
nature = res[4] or 0,
|
||||
frost = res[5] or 0,
|
||||
shadow = res[6] or 0,
|
||||
arcane = res[7] or 0
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Expose spell cooldown info
|
||||
-- Reactive Spell Indicator using IsSpellUsable
|
||||
-- Shows when reactive abilities like Overpower, Revenge, Execute are usable
|
||||
if IsSpellUsable and C.unitframes.reactive_indicator == "1" then
|
||||
local size = tonumber(C.unitframes.reactive_size) or 28
|
||||
local _, class = UnitClass("player")
|
||||
|
||||
-- Reactive spells by class
|
||||
local reactiveSpells = {
|
||||
WARRIOR = {
|
||||
{ name = "Overpower", texture = "Interface\\Icons\\Ability_MeleeDamage" },
|
||||
{ name = "Revenge", texture = "Interface\\Icons\\Ability_Warrior_Revenge" },
|
||||
{ name = "Execute", texture = "Interface\\Icons\\INV_Sword_48" },
|
||||
},
|
||||
ROGUE = {
|
||||
{ name = "Riposte", texture = "Interface\\Icons\\Ability_Warrior_Challange" },
|
||||
},
|
||||
HUNTER = {
|
||||
{ name = "Mongoose Bite", texture = "Interface\\Icons\\Ability_Hunter_SwiftStrike" },
|
||||
{ name = "Counterattack", texture = "Interface\\Icons\\Ability_Warrior_Challange" },
|
||||
},
|
||||
}
|
||||
|
||||
local spells = reactiveSpells[class]
|
||||
if spells then
|
||||
pfUI.reactive = CreateFrame("Frame", "pfReactiveIndicator", UIParent)
|
||||
pfUI.reactive:SetFrameStrata("HIGH")
|
||||
local spellCount = table.getn(spells)
|
||||
pfUI.reactive:SetWidth(size * spellCount + 4 * (spellCount - 1))
|
||||
pfUI.reactive:SetHeight(size)
|
||||
pfUI.reactive:SetPoint("CENTER", UIParent, "CENTER", 0, -200)
|
||||
pfUI.reactive:Hide()
|
||||
|
||||
pfUI.reactive.icons = {}
|
||||
for i, spell in ipairs(spells) do
|
||||
local icon = CreateFrame("Frame", nil, pfUI.reactive)
|
||||
icon:SetWidth(size)
|
||||
icon:SetHeight(size)
|
||||
icon:SetPoint("LEFT", pfUI.reactive, "LEFT", (i-1) * (size + 4), 0)
|
||||
|
||||
icon.texture = icon:CreateTexture(nil, "ARTWORK")
|
||||
icon.texture:SetAllPoints(icon)
|
||||
icon.texture:SetTexture(spell.texture)
|
||||
icon.texture:SetTexCoord(.08, .92, .08, .92)
|
||||
|
||||
icon.glow = icon:CreateTexture(nil, "OVERLAY")
|
||||
icon.glow:SetPoint("TOPLEFT", icon, "TOPLEFT", -4, 4)
|
||||
icon.glow:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 4, -4)
|
||||
icon.glow:SetTexture(pfUI.media["img:glow"])
|
||||
icon.glow:SetVertexColor(1, 1, 0, 0.8)
|
||||
|
||||
CreateBackdrop(icon)
|
||||
icon:Hide()
|
||||
icon.spellName = spell.name
|
||||
pfUI.reactive.icons[i] = icon
|
||||
end
|
||||
|
||||
UpdateMovable(pfUI.reactive)
|
||||
|
||||
pfUI.reactive:SetScript("OnUpdate", function()
|
||||
local anyVisible = false
|
||||
for _, icon in ipairs(this.icons) do
|
||||
local usable = IsSpellUsable(icon.spellName)
|
||||
if usable == 1 then
|
||||
icon:Show()
|
||||
anyVisible = true
|
||||
else
|
||||
icon:Hide()
|
||||
end
|
||||
end
|
||||
if anyVisible then
|
||||
this:Show()
|
||||
else
|
||||
this:Hide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Enhanced Cooldown Tracking API using GetSpellIdCooldown
|
||||
if GetSpellIdCooldown then
|
||||
pfUI.api.GetSpellCooldown = function(spellId)
|
||||
local success, result = pcall(GetSpellIdCooldown, spellId)
|
||||
if success then
|
||||
return result
|
||||
pfUI.api.GetPreciseCooldown = function(spellId)
|
||||
local cd = GetSpellIdCooldown(spellId)
|
||||
if not cd then return nil end
|
||||
return {
|
||||
onCooldown = (cd.isOnCooldown or 0) == 1,
|
||||
remaining = (cd.cooldownRemainingMs or 0) / 1000,
|
||||
remainingMs = cd.cooldownRemainingMs or 0,
|
||||
gcdRemaining = (cd.gcdCategoryRemainingMs or 0) / 1000,
|
||||
gcdRemainingMs = cd.gcdCategoryRemainingMs or 0,
|
||||
individualRemaining = (cd.individualRemainingMs or 0) / 1000,
|
||||
categoryRemaining = (cd.categoryRemainingMs or 0) / 1000,
|
||||
}
|
||||
end
|
||||
|
||||
-- Item cooldown helper
|
||||
pfUI.api.GetPreciseItemCooldown = function(itemId)
|
||||
if not GetItemIdCooldown then return nil end
|
||||
local cd = GetItemIdCooldown(itemId)
|
||||
if not cd then return nil end
|
||||
return {
|
||||
onCooldown = (cd.isOnCooldown or 0) == 1,
|
||||
remaining = (cd.cooldownRemainingMs or 0) / 1000,
|
||||
remainingMs = cd.cooldownRemainingMs or 0,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- UNIT_DIED event handling
|
||||
-- Can be used to clear tracking data or trigger effects on unit death
|
||||
local deathTracker = CreateFrame("Frame")
|
||||
deathTracker:RegisterEvent("UNIT_DIED")
|
||||
deathTracker:SetScript("OnEvent", function()
|
||||
local guid = arg1
|
||||
if not guid then return end
|
||||
|
||||
-- Clean up debuff tracking for dead units
|
||||
if pfUI.nampower_debuffs and pfUI.nampower_debuffs[guid] then
|
||||
pfUI.nampower_debuffs[guid] = nil
|
||||
end
|
||||
if pfUI.nampower_buffs and pfUI.nampower_buffs[guid] then
|
||||
pfUI.nampower_buffs[guid] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
-- Trinket Management API
|
||||
if GetTrinkets then
|
||||
pfUI.api.GetEquippedTrinkets = function()
|
||||
local trinkets = GetTrinkets()
|
||||
if not trinkets then return {} end
|
||||
local equipped = {}
|
||||
for _, trinket in pairs(trinkets) do
|
||||
if trinket and trinket.bagIndex == nil then -- nil bagIndex = equipped
|
||||
table.insert(equipped, trinket)
|
||||
end
|
||||
end
|
||||
return equipped
|
||||
end
|
||||
|
||||
pfUI.api.GetTrinketCooldown = function(slot)
|
||||
if not GetTrinketCooldown then return nil end
|
||||
local cd = GetTrinketCooldown(slot)
|
||||
if cd == -1 or not cd then return nil end
|
||||
return {
|
||||
onCooldown = (cd.isOnCooldown or 0) == 1,
|
||||
remaining = (cd.cooldownRemainingMs or 0) / 1000,
|
||||
remainingMs = cd.cooldownRemainingMs or 0,
|
||||
}
|
||||
end
|
||||
|
||||
pfUI.api.UseTrinket = function(slot, target)
|
||||
if not UseTrinket then return false end
|
||||
return UseTrinket(slot, target) == 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Nampower Item Stats API (use distinct name to avoid conflicts)
|
||||
if GetItemStats then
|
||||
pfUI.api.GetNampowerItemStats = function(itemId)
|
||||
local success, stats = pcall(GetItemStats, itemId, true)
|
||||
if not success or not stats then return nil end
|
||||
return stats
|
||||
end
|
||||
|
||||
-- Quick item level lookup
|
||||
pfUI.api.GetNampowerItemLevel = function(itemId)
|
||||
if GetItemLevel then
|
||||
return GetItemLevel(itemId)
|
||||
end
|
||||
local success, stats = pcall(GetItemStats, itemId, true)
|
||||
if success and stats and stats.itemLevel then
|
||||
return stats.itemLevel
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Spell data cache for fast lookups
|
||||
pfUI.api.spellCache = pfUI.api.spellCache or {}
|
||||
|
||||
-- Get spell data from ID using Nampower
|
||||
pfUI.api.GetSpellData = function(spellId)
|
||||
if not spellId then return nil end
|
||||
|
||||
-- Check cache first
|
||||
if pfUI.api.spellCache[spellId] then
|
||||
return pfUI.api.spellCache[spellId]
|
||||
-- Spell Modifiers API for damage/heal predictions
|
||||
if GetSpellModifiers then
|
||||
pfUI.api.GetSpellBonus = function(spellId, modType)
|
||||
-- modType: 0=DAMAGE, 1=DURATION, 6=RADIUS, 7=CRIT, 10=CAST_TIME, 14=COST, etc.
|
||||
local flat, percent, hasmod = GetSpellModifiers(spellId, modType or 0)
|
||||
return {
|
||||
flat = flat or 0,
|
||||
percent = percent or 0,
|
||||
hasModifier = hasmod and hasmod ~= 0,
|
||||
}
|
||||
end
|
||||
|
||||
local data = {}
|
||||
|
||||
-- Try SuperWoW SpellInfo first (if available)
|
||||
if SpellInfo then
|
||||
local success, name, rank, icon, minRange, maxRange = pcall(SpellInfo, spellId)
|
||||
if success and name then
|
||||
data.name = name
|
||||
data.rank = rank
|
||||
data.icon = icon
|
||||
data.minRange = minRange
|
||||
data.maxRange = maxRange
|
||||
end
|
||||
-- Common spell modifier lookups
|
||||
pfUI.api.GetSpellDamageBonus = function(spellId)
|
||||
return pfUI.api.GetSpellBonus(spellId, 0) -- DAMAGE
|
||||
end
|
||||
|
||||
-- Try Nampower GetSpellRec for additional data
|
||||
if GetSpellRec then
|
||||
local success, rec = pcall(GetSpellRec, spellId)
|
||||
if success and rec then
|
||||
data.name = data.name or rec.name
|
||||
data.manaCost = rec.manaCost
|
||||
data.castTime = rec.castingTimeIndex
|
||||
data.spellLevel = rec.spellLevel
|
||||
data.school = rec.school
|
||||
data.powerType = rec.powerType
|
||||
end
|
||||
pfUI.api.GetSpellCritBonus = function(spellId)
|
||||
return pfUI.api.GetSpellBonus(spellId, 7) -- CRITICAL_CHANCE
|
||||
end
|
||||
|
||||
-- Cache if we got data
|
||||
if data.name then
|
||||
pfUI.api.spellCache[spellId] = data
|
||||
return data
|
||||
pfUI.api.GetSpellCostReduction = function(spellId)
|
||||
return pfUI.api.GetSpellBonus(spellId, 14) -- COST
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Get spell ID from name using Nampower
|
||||
-- Inventory/Bag API
|
||||
if GetBagItems then
|
||||
pfUI.api.GetAllBagItems = function()
|
||||
return GetBagItems()
|
||||
end
|
||||
|
||||
pfUI.api.FindItem = function(itemIdOrName)
|
||||
if FindPlayerItemSlot then
|
||||
local bag, slot = FindPlayerItemSlot(itemIdOrName)
|
||||
return bag, slot
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
pfUI.api.UseItem = function(itemIdOrName, target)
|
||||
if UseItemIdOrName then
|
||||
return UseItemIdOrName(itemIdOrName, target) == 1
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Equipment Inspection API
|
||||
if GetEquippedItems then
|
||||
pfUI.api.GetPlayerEquipment = function()
|
||||
return GetEquippedItems("player")
|
||||
end
|
||||
|
||||
pfUI.api.GetTargetEquipment = function()
|
||||
return GetEquippedItems("target")
|
||||
end
|
||||
|
||||
pfUI.api.GetEquippedItemInfo = function(unit, slot)
|
||||
if GetEquippedItem then
|
||||
return GetEquippedItem(unit, slot)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Spell Lookup Helpers
|
||||
if GetSpellIdForName then
|
||||
pfUI.api.GetSpellIdByName = function(spellName)
|
||||
local success, result = pcall(GetSpellIdForName, spellName)
|
||||
if success then
|
||||
return result
|
||||
end
|
||||
return nil
|
||||
pfUI.api.GetMaxRankSpellId = function(spellName)
|
||||
return GetSpellIdForName(spellName)
|
||||
end
|
||||
end
|
||||
|
||||
if GetSpellSlotTypeIdForName then
|
||||
pfUI.api.GetSpellSlotInfo = function(spellName)
|
||||
local slot, bookType, spellId = GetSpellSlotTypeIdForName(spellName)
|
||||
return {
|
||||
slot = slot,
|
||||
bookType = bookType,
|
||||
spellId = spellId,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Queue Script API for advanced macro functionality
|
||||
if QueueScript then
|
||||
pfUI.api.QueueLuaScript = function(script, priority)
|
||||
QueueScript(script, priority or 1)
|
||||
end
|
||||
end
|
||||
|
||||
if QueueSpellByName then
|
||||
pfUI.api.QueueSpell = function(spellName)
|
||||
QueueSpellByName(spellName)
|
||||
end
|
||||
end
|
||||
|
||||
-- Channel optimization
|
||||
if ChannelStopCastingNextTick then
|
||||
pfUI.api.StopChannelNextTick = function()
|
||||
ChannelStopCastingNextTick()
|
||||
end
|
||||
end
|
||||
|
||||
-- Spell Database Access via GetSpellRec
|
||||
if GetSpellRec then
|
||||
pfUI.api.GetSpellRecord = function(spellId)
|
||||
local success, rec = pcall(GetSpellRec, spellId)
|
||||
if not success or not rec then return nil end
|
||||
return {
|
||||
spellId = spellId,
|
||||
name = rec.name or "",
|
||||
rank = rec.rank or "",
|
||||
description = rec.description or "",
|
||||
manaCost = rec.manaCost or 0,
|
||||
baseLevel = rec.baseLevel or 0,
|
||||
spellLevel = rec.spellLevel or 0,
|
||||
maxLevel = rec.maxLevel or 0,
|
||||
maxTargetLevel = rec.maxTargetLevel or 0,
|
||||
maxTargets = rec.maxTargets or 0,
|
||||
durationIndex = rec.durationIndex or 0,
|
||||
powerType = rec.powerType or 0,
|
||||
rangeIndex = rec.rangeIndex or 0,
|
||||
speed = rec.speed or 0,
|
||||
schoolMask = rec.schoolMask or 0,
|
||||
runeCostID = rec.runeCostID or 0,
|
||||
spellMissileID = rec.spellMissileID or 0,
|
||||
iconID = rec.iconID or 0,
|
||||
activeIconID = rec.activeIconID or 0,
|
||||
nameSubtext = rec.nameSubtext or "",
|
||||
castingTimeIndex = rec.castingTimeIndex or 0,
|
||||
categoryRecoveryTime = rec.categoryRecoveryTime or 0,
|
||||
recoveryTime = rec.recoveryTime or 0,
|
||||
startRecoveryCategory = rec.startRecoveryCategory or 0,
|
||||
startRecoveryTime = rec.startRecoveryTime or 0,
|
||||
}
|
||||
end
|
||||
|
||||
-- Get spell school (fire, frost, nature, etc.)
|
||||
pfUI.api.GetSpellSchool = function(spellId)
|
||||
local success, rec = pcall(GetSpellRec, spellId)
|
||||
if not success or not rec or not rec.schoolMask then return nil end
|
||||
local schools = {
|
||||
[1] = "Physical",
|
||||
[2] = "Holy",
|
||||
[4] = "Fire",
|
||||
[8] = "Nature",
|
||||
[16] = "Frost",
|
||||
[32] = "Shadow",
|
||||
[64] = "Arcane",
|
||||
}
|
||||
return schools[rec.schoolMask] or "Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
-- Disenchant All utility
|
||||
if DisenchantAll then
|
||||
pfUI.api.DisenchantAllItems = function()
|
||||
DisenchantAll()
|
||||
end
|
||||
|
||||
SLASH_PFDISENCHANTALL1 = "/disenchantall"
|
||||
SLASH_PFDISENCHANTALL2 = "/dea"
|
||||
SlashCmdList["PFDISENCHANTALL"] = function()
|
||||
DisenchantAll()
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpfUI|r: Disenchanting all eligible items...")
|
||||
end
|
||||
end
|
||||
|
||||
-- Enhanced Heal Prediction with AURA_CAST events
|
||||
-- This helps libpredict detect HoT applications more accurately
|
||||
if libpredict then
|
||||
local auraCastFrame = CreateFrame("Frame")
|
||||
auraCastFrame:RegisterEvent("AURA_CAST_ON_SELF")
|
||||
auraCastFrame:RegisterEvent("AURA_CAST_ON_OTHER")
|
||||
|
||||
auraCastFrame:SetScript("OnEvent", function()
|
||||
local casterGuid = arg1
|
||||
local targetGuid = arg2
|
||||
local spellId = arg3
|
||||
|
||||
if not spellId or not targetGuid then return end
|
||||
|
||||
-- Get spell name
|
||||
local spellName
|
||||
if SpellInfo then
|
||||
spellName = SpellInfo(spellId)
|
||||
end
|
||||
if not spellName then
|
||||
spellName = SafeGetSpellNameAndRank(spellId)
|
||||
end
|
||||
|
||||
if not spellName then return end
|
||||
|
||||
-- Check if this is a HoT spell we care about
|
||||
local hotSpells = {
|
||||
["Rejuvenation"] = true,
|
||||
["Renew"] = true,
|
||||
["Regrowth"] = true,
|
||||
["Verjüngung"] = true, -- German
|
||||
["Erneuerung"] = true,
|
||||
["Nachwachsen"] = true,
|
||||
}
|
||||
|
||||
if hotSpells[spellName] then
|
||||
-- Signal to libpredict that a HoT was applied
|
||||
-- This can be used to update heal predictions
|
||||
if pfUI.api.libpredict and pfUI.api.libpredict.OnHotApplied then
|
||||
pfUI.api.libpredict:OnHotApplied(targetGuid, spellName, spellId)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Swing Timer Integration
|
||||
-- Track auto-attack swing timers for melee classes
|
||||
local swingFrame = CreateFrame("Frame")
|
||||
swingFrame.mainHand = { start = 0, speed = 0 }
|
||||
swingFrame.offHand = { start = 0, speed = 0 }
|
||||
swingFrame.ranged = { start = 0, speed = 0 }
|
||||
|
||||
swingFrame:RegisterEvent("PLAYER_ENTER_COMBAT")
|
||||
swingFrame:RegisterEvent("PLAYER_LEAVE_COMBAT")
|
||||
swingFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_HITS")
|
||||
swingFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES")
|
||||
|
||||
swingFrame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTER_COMBAT" then
|
||||
local mainSpeed, offSpeed = UnitAttackSpeed("player")
|
||||
this.mainHand.speed = mainSpeed or 2
|
||||
this.offHand.speed = offSpeed or 0
|
||||
this.mainHand.start = GetTime()
|
||||
if this.offHand.speed > 0 then
|
||||
this.offHand.start = GetTime()
|
||||
end
|
||||
elseif event == "CHAT_MSG_COMBAT_SELF_HITS" or event == "CHAT_MSG_COMBAT_SELF_MISSES" then
|
||||
-- Reset swing timer on hit/miss
|
||||
local mainSpeed, offSpeed = UnitAttackSpeed("player")
|
||||
this.mainHand.speed = mainSpeed or 2
|
||||
this.mainHand.start = GetTime()
|
||||
end
|
||||
end)
|
||||
|
||||
pfUI.api.GetSwingTimers = function()
|
||||
local now = GetTime()
|
||||
local mainRemaining = swingFrame.mainHand.speed - (now - swingFrame.mainHand.start)
|
||||
local offRemaining = swingFrame.offHand.speed > 0 and (swingFrame.offHand.speed - (now - swingFrame.offHand.start)) or 0
|
||||
|
||||
return {
|
||||
mainHand = {
|
||||
remaining = math.max(0, mainRemaining),
|
||||
speed = swingFrame.mainHand.speed,
|
||||
progress = swingFrame.mainHand.speed > 0 and (1 - math.max(0, mainRemaining) / swingFrame.mainHand.speed) or 0,
|
||||
},
|
||||
offHand = {
|
||||
remaining = math.max(0, offRemaining),
|
||||
speed = swingFrame.offHand.speed,
|
||||
progress = swingFrame.offHand.speed > 0 and (1 - math.max(0, offRemaining) / swingFrame.offHand.speed) or 0,
|
||||
},
|
||||
}
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user