mirror of
https://github.com/brues-code/pfUI.git
synced 2026-10-01 03:19:59 +00:00
update for libdebuff
Read the readme! Version push
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# pfUI - Turtle WoW Edition
|
||||
|
||||
[](https://github.com/me0wg4ming/pfUI)
|
||||
[](https://github.com/me0wg4ming/pfUI)
|
||||
[](https://turtlecraft.gg/)
|
||||
[](https://github.com/balakethelock/SuperWoW)
|
||||
[](https://gitea.com/avitasia/nampower)
|
||||
@@ -14,6 +14,76 @@ This version includes significant performance improvements, DLL-enhanced feature
|
||||
|
||||
---
|
||||
|
||||
## 🎯 What's New in Version 7.6.2 (February 6, 2026)
|
||||
|
||||
### 🚀 Pure GetUnitField Debuff System (libdebuff.lua)
|
||||
|
||||
**Major performance rewrite: UnitDebuff() now runs entirely through Nampower's GetUnitField — zero Blizzard API calls, zero tooltip scans.**
|
||||
|
||||
Previously, every debuff icon required three expensive calls per update:
|
||||
1. `UnitDebuff(unit, slot)` — Blizzard C-side API call
|
||||
2. `scanner:SetUnitDebuff(unit, slot)` — Tooltip object creation + GameTooltip parse
|
||||
3. `scanner:Line(1)` — String extraction from tooltip
|
||||
|
||||
All three are now replaced by pure Lua table lookups into cached GetUnitField data:
|
||||
|
||||
| Data | Old (Blizzard API) | New (GetUnitField) |
|
||||
|------|-------------------|-------------------|
|
||||
| Spell Name | Tooltip scan | `SpellInfo(spellId)` |
|
||||
| Texture | `UnitDebuff()` ret.1 | `GetSpellIconTexture(GetSpellRecField(spellId, "spellIconID"))` |
|
||||
| Stacks | `UnitDebuff()` ret.2 | `GetUnitField(guid, "auraApplications")[slot]` |
|
||||
| DebuffType | `UnitDebuff()` ret.3 | `GetSpellRecField(spellId, "dispel")` → dispelTypeMap |
|
||||
| Duration/Timeleft | ownDebuffs tracking | unchanged |
|
||||
| Caster | slotOwnership tracking | unchanged |
|
||||
|
||||
**Performance impact:** With 5 debuffs on target = 15 expensive calls eliminated per update cycle. With 10 visible nameplates × 3 debuffs = 90 calls eliminated per refresh. Estimated **3-5x faster** per UnitDebuff call.
|
||||
|
||||
**DebuffType now works in Nampower path:** Previously dtype was only available from Blizzard's UnitDebuff(). Now resolved from SpellRec DBC via `GetSpellRecField(spellId, "dispel")`, meaning debuff frame border colors (Magic=blue, Curse=purple, Poison=green, Disease=brown) now work correctly for all units including nameplates.
|
||||
|
||||
### 🎨 Item-Cast Icon & Name Support (libdebuff.lua + castbar.lua)
|
||||
|
||||
**Castbar now shows the correct item icon and item name for item-triggered casts!**
|
||||
|
||||
Previously, using items with cast times (Gnomish Death Ray, Net-o-Matic, Noggenfogger Elixir etc.) showed the generic spell icon and spell name on the castbar. Now:
|
||||
|
||||
- ✅ `SPELL_START_SELF/OTHER` `arg1` (itemId) is now parsed and used
|
||||
- ✅ `SPELL_GO_SELF/OTHER` `arg1` (itemId) is now parsed
|
||||
- ✅ Item icon resolved via `GetItemStatsField(itemId, "displayInfoID")` → `GetItemIconTexture()`
|
||||
- ✅ Item name resolved via `GetItemStatsField(itemId, "displayName")`
|
||||
- ✅ `pfUI.libdebuff_item_icons` — Persistent item icon/name cache that survives SPELL_GO clearing cast data
|
||||
- ✅ `castbar.lua` — Reads item icon + name from persistent cache with fallback to spell data
|
||||
|
||||
**Note:** Item icon/name detection only works for your own casts (WoW 1.12.1 protocol limitation — server sends `itemId=0` to other clients).
|
||||
|
||||
### 🔧 Icon Path Fix (libdebuff.lua)
|
||||
|
||||
**Fixed missing icons from Nampower texture functions.**
|
||||
|
||||
`GetSpellIconTexture()` and `GetItemIconTexture()` return short texture names (e.g. `INV_Gizmo_08`) without the `Interface\Icons\` prefix required by `SetTexture()`. Both `GetSpellIcon()` and the item icon lookup now auto-prefix the full path when needed.
|
||||
|
||||
### ⚡ Memory & GC Optimizations (libdebuff.lua)
|
||||
|
||||
- ✅ **Carnage frame recycling** — Persistent `carnageCheckFrame` reused instead of `CreateFrame()` per Ferocious Bite (eliminates frame leak in combat)
|
||||
- ✅ **Recycled cleanup buffers** — `_cleanupBuf1`/`_cleanupBuf2` reused instead of `table.insert` + new table per `CleanupExpiredTimers` call
|
||||
- ✅ **SelfOverwrite buffer recycling** — Reused buffer instead of new `oldCasters` table per overwrite
|
||||
- ✅ **Pre-defined sort function** — `_ownDebuffSortFunc` defined once instead of anonymous closure per `UnitOwnDebuff` call
|
||||
- ✅ **spellId stored in ownDebuffs** — Enables direct DBC lookups for dtype without slotMap iteration
|
||||
|
||||
### 📊 Code Statistics
|
||||
|
||||
**libdebuff.lua:**
|
||||
- Blizzard API calls in Nampower path: 3 per debuff → 0
|
||||
- New GetUnitField calls: `aura` + `auraApplications` (cached 50ms)
|
||||
- New DBC lookups: `GetSpellRecField(spellId, "dispel")` for dtype
|
||||
- New exports: `pfUI.libdebuff_item_icons`
|
||||
|
||||
**castbar.lua:**
|
||||
- Item icon override via `pfUI.libdebuff_item_icons` (persistent cache)
|
||||
- Item name override via `GetItemStatsField(itemId, "displayName")`
|
||||
- Works for player + target + focus castbars
|
||||
|
||||
---
|
||||
|
||||
## 🎯 What's New in Version 7.6.1 (February 6, 2026)
|
||||
- Added a new menu in /pfui named "Throttling" - Players who were unsatisfied with the throttling update rate can change it now for nameplates, Toolip Cursor and Chat Tab.
|
||||
|
||||
@@ -1423,7 +1493,7 @@ Same as original pfUI - free to use and modify.
|
||||
|
||||
---
|
||||
|
||||
**Version:** 7.6.0
|
||||
**Release Date:** February 3, 2026
|
||||
**Version:** 7.6.2
|
||||
**Release Date:** February 6, 2026
|
||||
**Compatibility:** Turtle WoW 1.18.0
|
||||
**Status:** Stable
|
||||
+204
-106
@@ -173,6 +173,7 @@ local iconCache = pfUI.libdebuff_icon_cache
|
||||
-- Cast Tracking: [casterGuid] = {spellID, spellName, icon, startTime, duration, endTime}
|
||||
-- Shared with nameplates for cast-bar display
|
||||
pfUI.libdebuff_casts = pfUI.libdebuff_casts or {}
|
||||
pfUI.libdebuff_item_icons = pfUI.libdebuff_item_icons or {} -- [casterGuid] = icon (persists across SPELL_GO)
|
||||
|
||||
-- Cleveroids API: [targetGUID][spellID] = {start, duration, caster, stacks}
|
||||
pfUI.libdebuff_objects_guid = pfUI.libdebuff_objects_guid or {}
|
||||
@@ -327,6 +328,10 @@ function libdebuff:GetSpellIcon(spellId)
|
||||
local spellIconId = GetSpellRecField(spellId, "spellIconID")
|
||||
if spellIconId and type(spellIconId) == "number" and spellIconId > 0 then
|
||||
texture = GetSpellIconTexture(spellIconId)
|
||||
-- GetSpellIconTexture may return short name, needs full path for SetTexture
|
||||
if texture and not string.find(texture, "\\") then
|
||||
texture = "Interface\\Icons\\" .. texture
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -365,8 +370,16 @@ end
|
||||
local slotMapCache = {}
|
||||
local SLOT_MAP_CACHE_DURATION = 0.05 -- 50ms cache (1-2 frames)
|
||||
|
||||
-- Dispel type mapping: SpellRec.dispel index -> Blizzard DebuffTypeColor key
|
||||
local dispelTypeMap = {
|
||||
[1] = "Magic",
|
||||
[2] = "Curse",
|
||||
[3] = "Disease",
|
||||
[4] = "Poison",
|
||||
}
|
||||
|
||||
-- Get current debuff state directly from WoW via GetUnitField
|
||||
-- Returns: { [displaySlot] = {auraSlot, spellId, spellName} }
|
||||
-- Returns: { [displaySlot] = {auraSlot, spellId, spellName, stacks, texture, dtype} }
|
||||
local function GetDebuffSlotMap(guid)
|
||||
if not guid or not GetUnitField or not SpellInfo then
|
||||
return nil
|
||||
@@ -382,6 +395,9 @@ local function GetDebuffSlotMap(guid)
|
||||
local auras = GetUnitField(guid, "aura")
|
||||
if not auras then return nil end
|
||||
|
||||
-- Fetch stacks array (reusable reference - extract values immediately)
|
||||
local auraApps = GetUnitField(guid, "auraApplications")
|
||||
|
||||
if debugStats.enabled then
|
||||
debugStats.getunitfield_calls = debugStats.getunitfield_calls + 1
|
||||
end
|
||||
@@ -395,11 +411,28 @@ local function GetDebuffSlotMap(guid)
|
||||
if spellId and spellId > 0 then
|
||||
displaySlot = displaySlot + 1
|
||||
local spellName = SpellInfo(spellId)
|
||||
local texture = libdebuff:GetSpellIcon(spellId)
|
||||
|
||||
-- Get stacks from auraApplications (extract immediately - reusable table)
|
||||
local stacks = auraApps and auraApps[auraSlot] or 0
|
||||
if stacks == 0 then stacks = 1 end -- 0 means 1 stack (no stacking)
|
||||
|
||||
-- Get debuff type from SpellRec DBC
|
||||
local dtype = nil
|
||||
if GetSpellRecField then
|
||||
local dispelId = GetSpellRecField(spellId, "dispel")
|
||||
if dispelId and dispelId > 0 then
|
||||
dtype = dispelTypeMap[dispelId]
|
||||
end
|
||||
end
|
||||
|
||||
map[displaySlot] = {
|
||||
auraSlot = auraSlot,
|
||||
spellId = spellId,
|
||||
spellName = spellName or "Unknown"
|
||||
spellName = spellName or "Unknown",
|
||||
stacks = stacks,
|
||||
texture = texture,
|
||||
dtype = dtype
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -449,6 +482,10 @@ end
|
||||
|
||||
local lastRangeCheck = 0
|
||||
|
||||
-- Recycled buffers for cleanup (avoids table creation per call)
|
||||
local _cleanupBuf1 = {}
|
||||
local _cleanupBuf2 = {}
|
||||
|
||||
local function CleanupUnit(guid)
|
||||
if not guid then return false end
|
||||
|
||||
@@ -491,30 +528,34 @@ local function CleanupExpiredTimers(guid)
|
||||
|
||||
-- Cleanup ownDebuffs
|
||||
if ownDebuffs[guid] then
|
||||
local toDelete = {}
|
||||
local n = 0
|
||||
for spellName, data in pairs(ownDebuffs[guid]) do
|
||||
local timeleft = (data.startTime + data.duration) - now
|
||||
if timeleft < -2 then -- Grace period
|
||||
table.insert(toDelete, spellName)
|
||||
n = n + 1
|
||||
_cleanupBuf1[n] = spellName
|
||||
end
|
||||
end
|
||||
for _, spellName in ipairs(toDelete) do
|
||||
ownDebuffs[guid][spellName] = nil
|
||||
for i = 1, n do
|
||||
ownDebuffs[guid][_cleanupBuf1[i]] = nil
|
||||
_cleanupBuf1[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Cleanup allAuraCasts
|
||||
if allAuraCasts[guid] then
|
||||
for spellName, casterTable in pairs(allAuraCasts[guid]) do
|
||||
local castersToDelete = {}
|
||||
local n2 = 0
|
||||
for casterGuid, data in pairs(casterTable) do
|
||||
local timeleft = (data.startTime + data.duration) - now
|
||||
if timeleft < -2 then
|
||||
table.insert(castersToDelete, casterGuid)
|
||||
n2 = n2 + 1
|
||||
_cleanupBuf2[n2] = casterGuid
|
||||
end
|
||||
end
|
||||
for _, casterGuid in ipairs(castersToDelete) do
|
||||
allAuraCasts[guid][spellName][casterGuid] = nil
|
||||
for i = 1, n2 do
|
||||
allAuraCasts[guid][spellName][_cleanupBuf2[i]] = nil
|
||||
_cleanupBuf2[i] = nil
|
||||
end
|
||||
-- Remove empty spell tables
|
||||
local hasCasters = false
|
||||
@@ -735,46 +776,40 @@ local cache = {}
|
||||
function libdebuff:UnitDebuff(unit, displaySlot)
|
||||
local unitname = UnitName(unit)
|
||||
local unitlevel = UnitLevel(unit)
|
||||
local texture, stacks, dtype = UnitDebuff(unit, displaySlot) -- Blizzard API for texture/stacks
|
||||
local duration, timeleft = nil, -1
|
||||
local rank = nil
|
||||
local caster = nil
|
||||
local effect = nil
|
||||
local texture = nil
|
||||
local stacks = 0
|
||||
local dtype = nil
|
||||
|
||||
-- Get effect name from tooltip
|
||||
if texture then
|
||||
scanner:SetUnitDebuff(unit, displaySlot)
|
||||
effect = scanner:Line(1) or ""
|
||||
end
|
||||
|
||||
-- Nampower: Use GetUnitField for accurate slot mapping
|
||||
if hasNampower and UnitExists and effect then
|
||||
-- Nampower: Use GetUnitField for ALL debuff data (no Blizzard UnitDebuff needed)
|
||||
if hasNampower and UnitExists then
|
||||
local _, guid = UnitExists(unit)
|
||||
if not guid then
|
||||
-- Fallback to legacy
|
||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
||||
-- Safety fallback: no GUID available (should not happen with Nampower)
|
||||
local bTexture, bStacks, bDtype = UnitDebuff(unit, displaySlot)
|
||||
if bTexture then
|
||||
scanner:SetUnitDebuff(unit, displaySlot)
|
||||
effect = scanner:Line(1) or ""
|
||||
end
|
||||
return effect, rank, bTexture, bStacks, bDtype, duration, timeleft, caster
|
||||
end
|
||||
|
||||
-- Get current slot map from GetUnitField
|
||||
-- Get current slot map from GetUnitField (cached 50ms)
|
||||
local slotMap = GetDebuffSlotMap(guid)
|
||||
if not slotMap or not slotMap[displaySlot] then
|
||||
-- Slot doesn't exist in GetUnitField - might be Blizzard displaying stale data
|
||||
return nil
|
||||
end
|
||||
|
||||
local slotData = slotMap[displaySlot]
|
||||
local gufSpellName = slotData.spellName
|
||||
effect = slotData.spellName
|
||||
texture = slotData.texture
|
||||
stacks = slotData.stacks
|
||||
dtype = slotData.dtype
|
||||
local auraSlot = slotData.auraSlot
|
||||
|
||||
-- Verify spell names match (sanity check)
|
||||
if gufSpellName ~= effect then
|
||||
-- Mismatch! GetUnitField and UnitDebuff disagree.
|
||||
-- This can happen during the brief moment when debuffs change.
|
||||
-- Trust GetUnitField (it's more accurate).
|
||||
effect = gufSpellName
|
||||
texture = libdebuff:GetSpellIcon(slotData.spellId)
|
||||
end
|
||||
|
||||
-- Get caster info for this slot
|
||||
local slotCasterGuid, isOurs = GetSlotCaster(guid, auraSlot, effect)
|
||||
|
||||
@@ -833,6 +868,16 @@ function libdebuff:UnitDebuff(unit, displaySlot)
|
||||
-- FALLBACK: Legacy (non-Nampower) system
|
||||
-- ============================================================================
|
||||
|
||||
local bTexture, bStacks, bDtype = UnitDebuff(unit, displaySlot)
|
||||
texture = bTexture
|
||||
stacks = bStacks
|
||||
dtype = bDtype
|
||||
|
||||
if texture then
|
||||
scanner:SetUnitDebuff(unit, displaySlot)
|
||||
effect = scanner:Line(1) or ""
|
||||
end
|
||||
|
||||
if effect and libdebuff.objects[unitname] then
|
||||
for level, effects in pairs(libdebuff.objects[unitname]) do
|
||||
if effects[effect] and effects[effect].duration then
|
||||
@@ -854,6 +899,14 @@ end
|
||||
-- API: UnitOwnDebuff (only OUR debuffs)
|
||||
-- ============================================================================
|
||||
|
||||
-- Pre-defined sort function for UnitOwnDebuff (avoids closure creation per call)
|
||||
local _ownDebuffSortFunc = function(a, b)
|
||||
if a.data.startTime == b.data.startTime then
|
||||
return a.spellName < b.spellName
|
||||
end
|
||||
return a.data.startTime < b.data.startTime
|
||||
end
|
||||
|
||||
function libdebuff:UnitOwnDebuff(unit, id)
|
||||
if hasNampower and UnitExists then
|
||||
local _, guid = UnitExists(unit)
|
||||
@@ -865,29 +918,35 @@ function libdebuff:UnitOwnDebuff(unit, id)
|
||||
for spellName, data in pairs(ownDebuffs[guid]) do
|
||||
local timeleft = (data.startTime + data.duration) - now
|
||||
if timeleft > -1 then -- Grace period
|
||||
table.insert(sortedDebuffs, {
|
||||
local count = table.getn(sortedDebuffs) + 1
|
||||
sortedDebuffs[count] = {
|
||||
spellName = spellName,
|
||||
data = data,
|
||||
timeleft = timeleft
|
||||
})
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort by startTime (oldest first = lowest display slot)
|
||||
-- If startTime is equal (e.g. after Carnage refresh), use spellName for stable sorting
|
||||
table.sort(sortedDebuffs, function(a, b)
|
||||
if a.data.startTime == b.data.startTime then
|
||||
return a.spellName < b.spellName
|
||||
end
|
||||
return a.data.startTime < b.data.startTime
|
||||
end)
|
||||
table.sort(sortedDebuffs, _ownDebuffSortFunc)
|
||||
|
||||
-- Return debuff at position 'id'
|
||||
if sortedDebuffs[id] then
|
||||
local entry = sortedDebuffs[id]
|
||||
local texture = entry.data.texture or "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
local displayTimeleft = entry.timeleft > 0 and entry.timeleft or 0
|
||||
return entry.spellName, entry.data.rank, texture, 1, nil, entry.data.duration, displayTimeleft, "player"
|
||||
|
||||
-- Get dtype from SpellRec DBC via stored spellId
|
||||
local entryDtype = nil
|
||||
if entry.data.spellId and GetSpellRecField then
|
||||
local dispelId = GetSpellRecField(entry.data.spellId, "dispel")
|
||||
if dispelId and dispelId > 0 then
|
||||
entryDtype = dispelTypeMap[dispelId]
|
||||
end
|
||||
end
|
||||
|
||||
return entry.spellName, entry.data.rank, texture, 1, entryDtype, entry.data.duration, displayTimeleft, "player"
|
||||
end
|
||||
end
|
||||
return nil
|
||||
@@ -988,6 +1047,69 @@ if hasNampower then
|
||||
carnageRank = rank or 0
|
||||
end
|
||||
|
||||
-- Persistent Carnage check frame (reused instead of CreateFrame per Bite)
|
||||
local carnageState = nil -- {targetGuid, checkTime}
|
||||
local carnageCheckFrame = CreateFrame("Frame")
|
||||
carnageCheckFrame:Hide()
|
||||
carnageCheckFrame:SetScript("OnUpdate", function()
|
||||
if not carnageState then
|
||||
this:Hide()
|
||||
return
|
||||
end
|
||||
if GetTime() < carnageState.checkTime then return end
|
||||
|
||||
-- Check if we gained a combo point (indicates Carnage proc)
|
||||
local cp = GetComboPoints() or 0
|
||||
|
||||
if cp > 0 then
|
||||
-- Carnage triggered! Refresh Rip & Rake
|
||||
local guid = carnageState.targetGuid
|
||||
local refreshTime = GetTime()
|
||||
local myGuid = GetPlayerGUID()
|
||||
|
||||
-- Refresh in ownDebuffs
|
||||
if ownDebuffs[guid] then
|
||||
if ownDebuffs[guid]["Rip"] then
|
||||
ownDebuffs[guid]["Rip"].startTime = refreshTime
|
||||
if debugStats.enabled then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[CARNAGE]|r Rip refreshed (CP detected)")
|
||||
end
|
||||
end
|
||||
if ownDebuffs[guid]["Rake"] then
|
||||
ownDebuffs[guid]["Rake"].startTime = refreshTime
|
||||
if debugStats.enabled then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[CARNAGE]|r Rake refreshed (CP detected)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Refresh in allAuraCasts
|
||||
if allAuraCasts[guid] then
|
||||
if allAuraCasts[guid]["Rip"] and allAuraCasts[guid]["Rip"][myGuid] then
|
||||
allAuraCasts[guid]["Rip"][myGuid].startTime = refreshTime
|
||||
end
|
||||
if allAuraCasts[guid]["Rake"] and allAuraCasts[guid]["Rake"][myGuid] then
|
||||
allAuraCasts[guid]["Rake"][myGuid].startTime = refreshTime
|
||||
end
|
||||
end
|
||||
|
||||
-- Trigger UI updates
|
||||
if pfTarget and UnitExists("target") then
|
||||
local _, currentTargetGuid = UnitExists("target")
|
||||
if currentTargetGuid == guid then
|
||||
pfTarget.update_aura = true
|
||||
end
|
||||
end
|
||||
|
||||
if pfUI.nameplates and pfUI.nameplates.OnAuraUpdate then
|
||||
pfUI.nameplates:OnAuraUpdate(guid)
|
||||
end
|
||||
end
|
||||
|
||||
carnageState = nil
|
||||
this:Hide()
|
||||
end)
|
||||
|
||||
local frame = CreateFrame("Frame")
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame:RegisterEvent("PLAYER_TALENT_UPDATE")
|
||||
@@ -1024,6 +1146,7 @@ if hasNampower then
|
||||
end
|
||||
|
||||
elseif event == "SPELL_START_SELF" or event == "SPELL_START_OTHER" then
|
||||
local itemId = arg1
|
||||
local spellId = arg2
|
||||
local casterGuid = arg3
|
||||
local castTime = arg6
|
||||
@@ -1033,8 +1156,31 @@ if hasNampower then
|
||||
local spellName = SpellInfo and SpellInfo(spellId) or nil
|
||||
local icon = libdebuff:GetSpellIcon(spellId)
|
||||
|
||||
-- Use item icon for item-triggered casts
|
||||
if itemId and itemId > 0 and GetItemStatsField and GetItemIconTexture then
|
||||
local displayInfoId = GetItemStatsField(itemId, "displayInfoID")
|
||||
if displayInfoId then
|
||||
local itemIcon = GetItemIconTexture(displayInfoId)
|
||||
if itemIcon then
|
||||
-- GetItemIconTexture returns short name (e.g. "INV_Gizmo_08"), needs full path
|
||||
if not string.find(itemIcon, "\\") then
|
||||
itemIcon = "Interface\\Icons\\" .. itemIcon
|
||||
end
|
||||
icon = itemIcon
|
||||
end
|
||||
end
|
||||
-- Store in persistent item icon cache (survives SPELL_GO clearing libdebuff_casts)
|
||||
pfUI.libdebuff_item_icons[casterGuid] = {
|
||||
icon = icon,
|
||||
name = GetItemStatsField and GetItemStatsField(itemId, "displayName") or nil
|
||||
}
|
||||
else
|
||||
pfUI.libdebuff_item_icons[casterGuid] = nil
|
||||
end
|
||||
|
||||
pfUI.libdebuff_casts[casterGuid] = {
|
||||
spellID = spellId,
|
||||
itemID = itemId and itemId > 0 and itemId or nil,
|
||||
spellName = spellName,
|
||||
icon = icon,
|
||||
startTime = GetTime(),
|
||||
@@ -1044,6 +1190,7 @@ if hasNampower then
|
||||
}
|
||||
|
||||
elseif event == "SPELL_GO_SELF" or event == "SPELL_GO_OTHER" then
|
||||
local itemId = arg1
|
||||
local spellId = arg2
|
||||
local casterGuid = arg3
|
||||
local targetGuid = arg4
|
||||
@@ -1086,69 +1233,16 @@ if hasNampower then
|
||||
end
|
||||
|
||||
-- CARNAGE TALENT: Ferocious Bite refreshes Rip & Rake
|
||||
-- NEW METHOD: Check for combo point gain after Bite (indicates Carnage proc)
|
||||
-- Check for combo point gain after Bite (indicates Carnage proc)
|
||||
-- Carnage gives +1 CP immediately after Bite if it procs
|
||||
if class == "DRUID" and carnageRank >= 1 and spellName == "Ferocious Bite" and casterGuid == myGuid then
|
||||
if targetGuid and numHit > 0 then
|
||||
-- Schedule delayed check (50ms to allow CP to register)
|
||||
local checkFrame = CreateFrame("Frame")
|
||||
checkFrame.targetGuid = targetGuid
|
||||
checkFrame.startTime = GetTime()
|
||||
|
||||
checkFrame:SetScript("OnUpdate", function()
|
||||
-- Wait 0.05 seconds
|
||||
if GetTime() - this.startTime >= 0.05 then
|
||||
-- Check if we gained a combo point (indicates Carnage proc)
|
||||
local cp = GetComboPoints() or 0
|
||||
|
||||
if cp > 0 then
|
||||
-- Carnage triggered! Refresh Rip & Rake
|
||||
local guid = this.targetGuid
|
||||
local refreshTime = GetTime()
|
||||
|
||||
-- Refresh in ownDebuffs
|
||||
if ownDebuffs[guid] then
|
||||
if ownDebuffs[guid]["Rip"] then
|
||||
ownDebuffs[guid]["Rip"].startTime = refreshTime
|
||||
if debugStats.enabled then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[CARNAGE]|r Rip refreshed (CP detected)")
|
||||
end
|
||||
end
|
||||
if ownDebuffs[guid]["Rake"] then
|
||||
ownDebuffs[guid]["Rake"].startTime = refreshTime
|
||||
if debugStats.enabled then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ffff[CARNAGE]|r Rake refreshed (CP detected)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Refresh in allAuraCasts
|
||||
if allAuraCasts[guid] then
|
||||
if allAuraCasts[guid]["Rip"] and allAuraCasts[guid]["Rip"][myGuid] then
|
||||
allAuraCasts[guid]["Rip"][myGuid].startTime = refreshTime
|
||||
end
|
||||
if allAuraCasts[guid]["Rake"] and allAuraCasts[guid]["Rake"][myGuid] then
|
||||
allAuraCasts[guid]["Rake"][myGuid].startTime = refreshTime
|
||||
end
|
||||
end
|
||||
|
||||
-- Trigger UI updates
|
||||
if pfTarget and UnitExists("target") then
|
||||
local _, currentTargetGuid = UnitExists("target")
|
||||
if currentTargetGuid == guid then
|
||||
pfTarget.update_aura = true
|
||||
end
|
||||
end
|
||||
|
||||
if pfUI.nameplates and pfUI.nameplates.OnAuraUpdate then
|
||||
pfUI.nameplates:OnAuraUpdate(guid)
|
||||
end
|
||||
end
|
||||
|
||||
-- Cleanup: Remove OnUpdate handler
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end)
|
||||
carnageState = {
|
||||
targetGuid = targetGuid,
|
||||
checkTime = GetTime() + 0.05
|
||||
}
|
||||
carnageCheckFrame:Show()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1258,14 +1352,16 @@ if hasNampower then
|
||||
|
||||
-- Handle self-overwrite debuffs (clear other casters)
|
||||
if selfOverwriteDebuffs[spellName] then
|
||||
local oldCasters = {}
|
||||
local n = 0
|
||||
for otherCaster in pairs(allAuraCasts[targetGuid][spellName]) do
|
||||
if otherCaster ~= casterGuid then
|
||||
table.insert(oldCasters, otherCaster)
|
||||
n = n + 1
|
||||
_cleanupBuf1[n] = otherCaster
|
||||
end
|
||||
end
|
||||
for _, otherCaster in ipairs(oldCasters) do
|
||||
allAuraCasts[targetGuid][spellName][otherCaster] = nil
|
||||
for i = 1, n do
|
||||
allAuraCasts[targetGuid][spellName][_cleanupBuf1[i]] = nil
|
||||
_cleanupBuf1[i] = nil
|
||||
end
|
||||
|
||||
-- Clear from ownDebuffs if we're being overwritten
|
||||
@@ -1372,6 +1468,7 @@ if hasNampower then
|
||||
data.duration = duration
|
||||
data.texture = texture
|
||||
data.rank = rankNum
|
||||
data.spellId = spellId
|
||||
|
||||
-- Handle variant pairs for ownDebuffs
|
||||
if debuffOverwritePairs[spellName] then
|
||||
@@ -1497,7 +1594,8 @@ if hasNampower then
|
||||
startTime = auraData.startTime,
|
||||
duration = auraData.duration,
|
||||
texture = texture,
|
||||
rank = auraData.rank or 0
|
||||
rank = auraData.rank or 0,
|
||||
spellId = spellId
|
||||
}
|
||||
|
||||
if debugStats.enabled and IsCurrentTarget(guid) then
|
||||
|
||||
+1
-1
@@ -243,7 +243,7 @@ libpredict:SetScript("OnUpdate", function()
|
||||
-- throttle cleanup - no need to check every frame
|
||||
local now = pfUI.uf.now or GetTime()
|
||||
if (this.tick or 0) > now then return end
|
||||
this.tick = now + 0.1 -- Default: 10 FPS
|
||||
this.tick = now + pfUI.throttle:Get("libpredict") -- Default: Normal (10 FPS)
|
||||
|
||||
-- update on timeout events
|
||||
for timestamp, targets in pairs(events) do
|
||||
|
||||
+32
-2
@@ -149,8 +149,37 @@ pfUI:RegisterModule("castbar", "vanilla", function ()
|
||||
this.icon:Show()
|
||||
this.icon:SetHeight(size)
|
||||
this.icon:SetWidth(size)
|
||||
this.icon.texture:SetTexture(texture)
|
||||
|
||||
-- Override with item icon from libdebuff_casts or persistent item icon cache
|
||||
local useTexture = texture
|
||||
local useItemName = nil
|
||||
if pfUI.libdebuff_casts or pfUI.libdebuff_item_icons then
|
||||
local castGuid = nil
|
||||
if this.unitstr == "player" and GetPlayerGUID then
|
||||
castGuid = GetPlayerGUID()
|
||||
elseif this.unitstr and UnitExists then
|
||||
local _, guid = UnitExists(this.unitstr)
|
||||
castGuid = guid
|
||||
end
|
||||
if castGuid then
|
||||
-- First check active cast data
|
||||
if pfUI.libdebuff_casts and pfUI.libdebuff_casts[castGuid] and pfUI.libdebuff_casts[castGuid].itemID then
|
||||
useTexture = pfUI.libdebuff_casts[castGuid].icon or texture
|
||||
-- Fallback to persistent item icon cache
|
||||
elseif pfUI.libdebuff_item_icons and pfUI.libdebuff_item_icons[castGuid] then
|
||||
useTexture = pfUI.libdebuff_item_icons[castGuid].icon or texture
|
||||
useItemName = pfUI.libdebuff_item_icons[castGuid].name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
this.icon.texture:SetTexture(useTexture)
|
||||
this.bar:SetPoint("TOPLEFT", this.icon, "TOPRIGHT", this.spacing, 0)
|
||||
|
||||
-- Override spell name with item name for item-triggered casts
|
||||
if useItemName and this.showname then
|
||||
this.bar.left:SetText(useItemName .. " " .. rank)
|
||||
end
|
||||
else
|
||||
this.bar:SetPoint("TOPLEFT", this, 0, 0)
|
||||
this.icon:Hide()
|
||||
@@ -189,6 +218,7 @@ pfUI:RegisterModule("castbar", "vanilla", function ()
|
||||
this.bar:SetValue(100)
|
||||
this.fadeout = 1
|
||||
this.delay = 0
|
||||
this.itemIconApplied = nil
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -337,4 +367,4 @@ pfUI:RegisterModule("castbar", "vanilla", function ()
|
||||
|
||||
UpdateMovable(pfUI.castbar.focus)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
+1
-6
@@ -13,12 +13,7 @@ pfUI:RegisterModule("player", "vanilla:tbc", function ()
|
||||
|
||||
-- Add throttle to player frame OnUpdate
|
||||
if pfUI.uf.player:GetScript("OnUpdate") then
|
||||
local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate")
|
||||
pfUI.uf.player:SetScript("OnUpdate", function()
|
||||
if (this.throttleTick or 0) > GetTime() then return end
|
||||
this.throttleTick = GetTime() + 0.1 -- Default: 10 FPS
|
||||
originalOnUpdate()
|
||||
end)
|
||||
pfUI.uf.player:SetScript("OnUpdate", pfUI.uf.player:GetScript("OnUpdate"))
|
||||
end
|
||||
|
||||
-- Replace default's RESET_INSTANCES button with an always working one
|
||||
|
||||
+1
-1
@@ -79,4 +79,4 @@ pfUI:RegisterModule("skin", "vanilla", function ()
|
||||
else
|
||||
UIErrorsFrame:RegisterEvent("UI_ERROR_MESSAGE")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
## Author: Shagu - modfied by me0wg4ming
|
||||
## Notes: A complete user interface replacement.
|
||||
## Notes-ruRU: Полная замена пользовательского интерфейса.
|
||||
## Version: 7.6.0 (experiment version)
|
||||
## Version: 7.6.2 (experiment version)
|
||||
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
|
||||
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Author: Shagu - modfied by me0wg4ming
|
||||
## Notes: A complete user interface replacement.
|
||||
## Notes-ruRU: Полная замена пользовательского интерфейса.
|
||||
## Version: 7.6.0 (experiment version)
|
||||
## Version: 7.6.2 (experiment version)
|
||||
## SavedVariables: pfUI_profiles, pfUI_addon_profiles, pfUI_cache, pfUI_throttle
|
||||
## SavedVariablesPerCharacter: pfUI_config, pfUI_init, pfUI_playerDB
|
||||
|
||||
|
||||
Reference in New Issue
Block a user