mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-25 17:16:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1eba4d295 | |||
| cbc531531c | |||
| b57fc52f9a | |||
| 8268af915a | |||
| 1108746949 | |||
| 38b4100353 | |||
| f497c71523 |
+1
-1
@@ -13,7 +13,7 @@ do
|
|||||||
-- on. There pfUI does load, and will throw wherever it reaches for something
|
-- on. There pfUI does load, and will throw wherever it reaches for something
|
||||||
-- the installed DLL doesn't have yet -- the popup names the cause so those
|
-- the installed DLL doesn't have yet -- the popup names the cause so those
|
||||||
-- errors aren't a mystery.
|
-- errors aren't a mystery.
|
||||||
local PFUI_CLASSIC_API_MIN = 11500 -- (X*10000 + Y*100 + Z)
|
local PFUI_CLASSIC_API_MIN = 11510 -- (X*10000 + Y*100 + Z)
|
||||||
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
|
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
|
||||||
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
|
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
|
||||||
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
|
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
# pfUI - ClassicAPI Edition
|
# pfUI - ClassicAPI Edition
|
||||||
|
|
||||||
[](https://octowow.st/)
|
|
||||||
[](https://github.com/brues-code/ClassicAPI)
|
[](https://github.com/brues-code/ClassicAPI)
|
||||||
[](https://github.com/brues-code/nampower)
|
[](https://github.com/brues-code/nampower)
|
||||||
[](https://github.com/balakethelock/SuperWoW)
|
[](https://github.com/balakethelock/SuperWoW)
|
||||||
[](https://github.com/brues-code/UnitXP_SP3)
|
[](https://github.com/brues-code/UnitXP_SP3)
|
||||||
|
|
||||||
**A pfUI fork specifically optimized for ClassicAPI on [Octo WoW](https://octowow.st/)**
|
|
||||||
|
|
||||||
This version includes significant performance improvements and DLL-enhanced features.
|
This version includes significant performance improvements and DLL-enhanced features.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -18,7 +15,7 @@ This version includes significant performance improvements and DLL-enhanced feat
|
|||||||
|
|
||||||
## DLL Enhancements
|
## DLL Enhancements
|
||||||
|
|
||||||
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality. These DLLs are permitted on Octo WoW:
|
Since pfUI 6.0.0 includes integrations with client-side DLLs for enhanced functionality:
|
||||||
|
|
||||||
### [ClassicAPI](https://github.com/brues-code/ClassicAPI)
|
### [ClassicAPI](https://github.com/brues-code/ClassicAPI)
|
||||||
|
|
||||||
|
|||||||
+26
@@ -1333,6 +1333,32 @@ function pfUI.api.CreateBackdropShadow(f)
|
|||||||
f.backdrop_shadow:SetBackdropBorderColor(0, 0, 0, tonumber(pfUI_config.appearance.border.shadow_intensity))
|
f.backdrop_shadow:SetBackdropBorderColor(0, 0, 0, tonumber(pfUI_config.appearance.border.shadow_intensity))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- [ Set Castbar Shield ]
|
||||||
|
-- Colors a castbar for a cast that none of the player's own interrupts or
|
||||||
|
-- silences can stop. The bar and the icon's border both take the shield color,
|
||||||
|
-- so the state still reads on a bar configured without an icon. Not shielded
|
||||||
|
-- restores the normal cast/channel color and the configured border.
|
||||||
|
-- 'bar' [frame] the castbar statusbar
|
||||||
|
-- 'icon' [frame] the icon frame, or nil for a bar without one
|
||||||
|
-- 'shielded' [bool] whether the cast is uninterruptible
|
||||||
|
-- 'isChannel' [bool] selects the unshielded color
|
||||||
|
function pfUI.api.SetCastbarShield(bar, icon, shielded, isChannel)
|
||||||
|
if shielded then
|
||||||
|
bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar.shieldcolor))
|
||||||
|
else
|
||||||
|
bar:SetStatusBarColor(pfUI.api.GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
local iconborder = icon and icon.backdrop
|
||||||
|
if not iconborder then return end
|
||||||
|
|
||||||
|
if shielded then
|
||||||
|
iconborder:SetBackdropBorderColor(pfUI.api.GetStringColor(C.appearance.castbar.shieldcolor))
|
||||||
|
else
|
||||||
|
iconborder:SetBackdropBorderColor(pfUI.api.GetStringColor(C.appearance.border.color))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- [ Bar Layout Options ] --
|
-- [ Bar Layout Options ] --
|
||||||
-- 'barsize' size of bar in number of buttons
|
-- 'barsize' size of bar in number of buttons
|
||||||
-- returns: array of options as strings for pfUI.gui.bar
|
-- returns: array of options as strings for pfUI.gui.bar
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ function pfUI:LoadConfig()
|
|||||||
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
|
pfUI:UpdateConfig("appearance", "castbar", "castbarcolor", ".7,.7,.9,.8")
|
||||||
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
|
pfUI:UpdateConfig("appearance", "castbar", "channelcolor", ".9,.9,.7,.8")
|
||||||
pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8")
|
pfUI:UpdateConfig("appearance", "castbar", "failcolor", "1,.2,.2,.8")
|
||||||
|
pfUI:UpdateConfig("appearance", "castbar", "shieldcolor", ".8,.45,.45,.8")
|
||||||
|
pfUI:UpdateConfig("appearance", "castbar", "fsrcolor", ".9,.6,.2,.8")
|
||||||
pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
|
pfUI:UpdateConfig("appearance", "castbar", "texture", "Interface\\AddOns\\pfUI\\img\\bar")
|
||||||
pfUI:UpdateConfig("appearance", "infight", "screen", "0")
|
pfUI:UpdateConfig("appearance", "infight", "screen", "0")
|
||||||
pfUI:UpdateConfig("appearance", "infight", "aggro", "0")
|
pfUI:UpdateConfig("appearance", "infight", "aggro", "0")
|
||||||
@@ -324,6 +326,9 @@ function pfUI:LoadConfig()
|
|||||||
pfUI:UpdateConfig("unitframes", "player", "showRest", "0")
|
pfUI:UpdateConfig("unitframes", "player", "showRest", "0")
|
||||||
pfUI:UpdateConfig("unitframes", "player", "energy", "1")
|
pfUI:UpdateConfig("unitframes", "player", "energy", "1")
|
||||||
pfUI:UpdateConfig("unitframes", "player", "manatick", "0")
|
pfUI:UpdateConfig("unitframes", "player", "manatick", "0")
|
||||||
|
pfUI:UpdateConfig("unitframes", "player", "showfsr", "1")
|
||||||
|
pfUI:UpdateConfig("unitframes", "player", "fsrheight", "4")
|
||||||
|
pfUI:UpdateConfig("unitframes", "player", "fsrstyle", "0")
|
||||||
pfUI:UpdateConfig("unitframes", "player", "display_haste", "0")
|
pfUI:UpdateConfig("unitframes", "player", "display_haste", "0")
|
||||||
pfUI:UpdateConfig("unitframes", "player", "display_haste_color", "1,1,1,1")
|
pfUI:UpdateConfig("unitframes", "player", "display_haste_color", "1,1,1,1")
|
||||||
pfUI:UpdateConfig("unitframes", "player", "display_spellpower", "1")
|
pfUI:UpdateConfig("unitframes", "player", "display_spellpower", "1")
|
||||||
|
|||||||
+37
-14
@@ -18,12 +18,7 @@ local ItemQuality = Enum.ItemQuality
|
|||||||
local function SortCategoryPrefix(itemId, classID, quality)
|
local function SortCategoryPrefix(itemId, classID, quality)
|
||||||
if itemId == HEARTHSTONE_ITEM_ID then return "00" end
|
if itemId == HEARTHSTONE_ITEM_ID then return "00" end
|
||||||
if quality == ItemQuality.Poor then return "13" end -- gray always last
|
if quality == ItemQuality.Poor then return "13" end -- gray always last
|
||||||
if classID == ItemClass.Weapon or classID == ItemClass.Armor then
|
if classID == ItemClass.Weapon or classID == ItemClass.Armor then return "01" end
|
||||||
if quality and quality >= ItemQuality.Epic then return "01" end -- Epic+ gear
|
|
||||||
if quality == ItemQuality.Rare then return "02" end -- Rare gear
|
|
||||||
if quality == ItemQuality.Uncommon then return "03" end -- Uncommon gear
|
|
||||||
return "04" -- Common/poor gear
|
|
||||||
end
|
|
||||||
if classID == ItemClass.Consumable then return "05" end
|
if classID == ItemClass.Consumable then return "05" end
|
||||||
if classID == ItemClass.Reagent then return "06" end
|
if classID == ItemClass.Reagent then return "06" end
|
||||||
if classID == ItemClass.Tradegoods then return "07" end
|
if classID == ItemClass.Tradegoods then return "07" end
|
||||||
@@ -42,12 +37,40 @@ local function SortCountSuffix(count)
|
|||||||
return string.sub(s, -6)
|
return string.sub(s, -6)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function SortKey(itemId, name, classID, subClassID, quality, count)
|
-- Gear's slot order, keyed by the equipLoc token (INVTYPE_*). Not the
|
||||||
-- Zero-pad the class/subclass so the secondary grouping sorts numerically
|
-- token's alphabetical order, which puts chest before head, and not its
|
||||||
-- (as a string, "10" would otherwise precede "2").
|
-- INVTYPE number, which parts robe (20) from chest (5). Same order as
|
||||||
return SortCategoryPrefix(itemId, classID, quality)
|
-- ClassicAPI's C_Container.SortBags, taken from Baganator: weapons by hand,
|
||||||
.. string.format("%02d|%02d|", classID or 99, subClassID or 99)
|
-- ranged and ammo, then armor with shields and off-hands first, head to
|
||||||
.. (name or "zzz") .. "|" .. SortCountSuffix(count)
|
-- feet, then jewelry, shirt and tabard.
|
||||||
|
local SLOT_ORDER = {
|
||||||
|
"INVTYPE_2HWEAPON", "INVTYPE_WEAPON", "INVTYPE_WEAPONMAINHAND",
|
||||||
|
"INVTYPE_SHIELD", "INVTYPE_HOLDABLE", "INVTYPE_RANGEDRIGHT",
|
||||||
|
"INVTYPE_WEAPONOFFHAND", "INVTYPE_RANGED", "INVTYPE_THROWN",
|
||||||
|
"INVTYPE_AMMO", "INVTYPE_QUIVER", "INVTYPE_RELIC",
|
||||||
|
"INVTYPE_HEAD", "INVTYPE_SHOULDER", "INVTYPE_CLOAK", "INVTYPE_CHEST",
|
||||||
|
"INVTYPE_ROBE", "INVTYPE_WRIST", "INVTYPE_HAND", "INVTYPE_WAIST",
|
||||||
|
"INVTYPE_LEGS", "INVTYPE_FEET",
|
||||||
|
"INVTYPE_NECK", "INVTYPE_FINGER", "INVTYPE_TRINKET", "INVTYPE_BODY",
|
||||||
|
"INVTYPE_TABARD", "INVTYPE_BAG",
|
||||||
|
}
|
||||||
|
local SLOT_RANK = {}
|
||||||
|
for i, token in ipairs(SLOT_ORDER) do SLOT_RANK[token] = i end
|
||||||
|
|
||||||
|
local function SortKey(itemId, name, classID, subClassID, quality, count, equipLoc)
|
||||||
|
-- Zero-pad the numeric fields so they sort numerically as strings ("10"
|
||||||
|
-- would otherwise precede "2").
|
||||||
|
local key = SortCategoryPrefix(itemId, classID, quality)
|
||||||
|
.. string.format("%02d|", classID or 99)
|
||||||
|
if classID == ItemClass.Weapon or classID == ItemClass.Armor then
|
||||||
|
-- Gear: slot, then subtype, then best quality first, so all helms sit
|
||||||
|
-- together, then all shoulders.
|
||||||
|
key = key .. string.format("%02d|%02d|%d|",
|
||||||
|
SLOT_RANK[equipLoc] or 99, subClassID or 99, 9 - (quality or 0))
|
||||||
|
else
|
||||||
|
key = key .. string.format("%02d|", subClassID or 99)
|
||||||
|
end
|
||||||
|
return key .. (name or "zzz") .. "|" .. SortCountSuffix(count)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ClearSortData()
|
local function ClearSortData()
|
||||||
@@ -168,10 +191,10 @@ local function BuildSortGrid()
|
|||||||
-- sit at positions 12/13. We categorize on those numeric class IDs
|
-- sit at positions 12/13. We categorize on those numeric class IDs
|
||||||
-- rather than the localized itemType/itemSubType strings. (pfUI's
|
-- rather than the localized itemType/itemSubType strings. (pfUI's
|
||||||
-- shimmed global GetItemInfo is only 10 fields and lacks them.)
|
-- shimmed global GetItemInfo is only 10 fields and lacks them.)
|
||||||
local name, _, quality, _, _, _, _, _, _, _, _, classID, subClassID = C_Item.GetItemInfo(itemId)
|
local name, _, quality, _, _, _, _, _, equipLoc, _, _, classID, subClassID = C_Item.GetItemInfo(itemId)
|
||||||
local _, count = GetContainerItemInfo(bag, slot)
|
local _, count = GetContainerItemInfo(bag, slot)
|
||||||
local item = {
|
local item = {
|
||||||
key = SortKey(itemId, name, classID, subClassID, quality, count),
|
key = SortKey(itemId, name, classID, subClassID, quality, count, equipLoc),
|
||||||
-- vanilla items carry at most one family bit, so equality
|
-- vanilla items carry at most one family bit, so equality
|
||||||
-- against a bag family suffices (no bit.band needed).
|
-- against a bag family suffices (no bit.band needed).
|
||||||
family = C_Item.GetItemFamily(itemId) or 0,
|
family = C_Item.GetItemFamily(itemId) or 0,
|
||||||
|
|||||||
+1
-1
@@ -937,7 +937,7 @@ if hasNampower then
|
|||||||
local numMissed = arg7 or 0
|
local numMissed = arg7 or 0
|
||||||
|
|
||||||
-- Fire registered SPELL_GO_SELF hooks BEFORE miss guard
|
-- Fire registered SPELL_GO_SELF hooks BEFORE miss guard
|
||||||
-- (Swingtimer needs to see ALL casts, even misses, for swing reset)
|
-- (Swingtimer needs to see ALL casts, even misses, to clear its queue color)
|
||||||
if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then
|
if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then
|
||||||
for _, fn in pairs(pfUI.libdebuff_spell_go_hooks) do
|
for _, fn in pairs(pfUI.libdebuff_spell_go_hooks) do
|
||||||
fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||||
|
|||||||
+8
-5
@@ -86,7 +86,7 @@ pfUI:RegisterModule("castbar", function ()
|
|||||||
|
|
||||||
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
|
-- Stamp the bar with cast data and render text/icon/lag once. OnUpdate
|
||||||
-- then animates the fill from this state without touching C_Spell.
|
-- then animates the fill from this state without touching C_Spell.
|
||||||
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank)
|
local function StampBar(cb, name, tex, startMs, endMs, spellID, isChannel, delayMs, isTradeskill, rank, noInterrupt)
|
||||||
cb.startTime = startMs
|
cb.startTime = startMs
|
||||||
cb.endTime = endMs
|
cb.endTime = endMs
|
||||||
cb.isChannel = isChannel
|
cb.isChannel = isChannel
|
||||||
@@ -97,7 +97,10 @@ pfUI:RegisterModule("castbar", function ()
|
|||||||
cb:SetAlpha(1)
|
cb:SetAlpha(1)
|
||||||
cb.fadeout = nil
|
cb.fadeout = nil
|
||||||
|
|
||||||
cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"]))
|
-- Shield an uninterruptible cast. Not on the player's own bar: the flag is
|
||||||
|
-- measured against the reader's own interrupts and silences, so on your own
|
||||||
|
-- cast it answers a question nobody asked.
|
||||||
|
SetCastbarShield(cb.bar, cb.icon, noInterrupt and cb.unitstr ~= "player", isChannel)
|
||||||
|
|
||||||
-- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed
|
-- Rank: prefer the value the UNIT_SPELLCAST_* event delivered (arg5, passed
|
||||||
-- through by RefreshBar). Only the retarget re-poll has no event in hand, so
|
-- through by RefreshBar). Only the retarget re-poll has no event in hand, so
|
||||||
@@ -152,10 +155,10 @@ pfUI:RegisterModule("castbar", function ()
|
|||||||
ClearBar(cb)
|
ClearBar(cb)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local name, _, tex, startMs, endMs, isTradeskill, _, _, spellID, _, delayMs = C_Spell.UnitCastingInfo(query)
|
local name, _, tex, startMs, endMs, isTradeskill, _, noInterrupt, spellID, _, delayMs = C_Spell.UnitCastingInfo(query)
|
||||||
local isChan
|
local isChan
|
||||||
if not name then
|
if not name then
|
||||||
name, _, tex, startMs, endMs, _, _, spellID = C_Spell.UnitChannelInfo(query)
|
name, _, tex, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(query)
|
||||||
isChan = true
|
isChan = true
|
||||||
end
|
end
|
||||||
-- Synthetic fallback for abilities the engine treats as instant-cast but
|
-- Synthetic fallback for abilities the engine treats as instant-cast but
|
||||||
@@ -169,7 +172,7 @@ pfUI:RegisterModule("castbar", function ()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if name and startMs and endMs then
|
if name and startMs and endMs then
|
||||||
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank)
|
StampBar(cb, name, tex, startMs, endMs, spellID, isChan, delayMs, isTradeskill, rank, noInterrupt)
|
||||||
else
|
else
|
||||||
ClearBar(cb)
|
ClearBar(cb)
|
||||||
end
|
end
|
||||||
|
|||||||
+44
-7
@@ -89,6 +89,13 @@ pfUI:RegisterModule("energytick", function()
|
|||||||
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
|
return C.unitframes.player.pwidth ~= "-1" and C.unitframes.player.pwidth or C.unitframes.player.width
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local rawborder, default_border = GetBorderSize("unitframes")
|
||||||
|
local cbtexture = pfUI.media[C.appearance.castbar.texture]
|
||||||
|
|
||||||
|
-- style is read once: switching it in the GUI needs a /reload, same as the
|
||||||
|
-- other structural (not cosmetic) toggles in this file
|
||||||
|
local fsrOverlay = C.unitframes.player.fsrstyle == "1"
|
||||||
|
|
||||||
-- was this gain the regen tick? if so, re-anchor the sweep on it
|
-- was this gain the regen tick? if so, re-anchor the sweep on it
|
||||||
local function lockTick(frame)
|
local function lockTick(frame)
|
||||||
local now, period = GetTime(), getAdjustedTickTimer()
|
local now, period = GetTime(), getAdjustedTickTimer()
|
||||||
@@ -199,11 +206,21 @@ pfUI:RegisterModule("energytick", function()
|
|||||||
end
|
end
|
||||||
this.tick = GetTime() + 0.020 -- ~50 FPS
|
this.tick = GetTime() + 0.020 -- ~50 FPS
|
||||||
|
|
||||||
-- five-second rule drains to nothing
|
-- five-second rule drains to nothing. The display is cosmetic and can be
|
||||||
|
-- hidden on its own; the window itself (fsrEnd/fsrGain) keeps running
|
||||||
|
-- either way -- it still dims the sweep spark below.
|
||||||
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
|
local remaining = this.fsrEnd and (this.fsrEnd - GetTime()) or 0
|
||||||
if this.mode == "MANA" and remaining > 0 then
|
if this.mode == "MANA" and remaining > 0 then
|
||||||
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
|
if C.unitframes.player.showfsr == "1" then
|
||||||
this.fsrbar:Show()
|
if fsrOverlay then
|
||||||
|
this.fsrbar:SetWidth(getBarWidth() * remaining / FIVE_SECOND_RULE)
|
||||||
|
else
|
||||||
|
this.fsrbar:SetValue(remaining)
|
||||||
|
end
|
||||||
|
this.fsrbar:Show()
|
||||||
|
else
|
||||||
|
this.fsrbar:Hide()
|
||||||
|
end
|
||||||
else
|
else
|
||||||
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
|
this.fsrSpell, this.fsrEnd, this.fsrGain = nil, nil, nil
|
||||||
this.fsrbar:Hide()
|
this.fsrbar:Hide()
|
||||||
@@ -240,10 +257,26 @@ pfUI:RegisterModule("energytick", function()
|
|||||||
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
|
this.spark:SetPoint("LEFT", pos - ((C.unitframes.player.pheight + 5) / 2), 0)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
|
if fsrOverlay then
|
||||||
energytick.fsrbar:SetTexture(1, 1, 1, .15)
|
-- classic style: a translucent shade on top of the power bar's own fill
|
||||||
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
|
energytick.fsrbar = energytick:CreateTexture(nil, "ARTWORK")
|
||||||
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
|
energytick.fsrbar:SetTexture(1, 1, 1, .15)
|
||||||
|
energytick.fsrbar:SetPoint("TOPLEFT", 0, 0)
|
||||||
|
energytick.fsrbar:SetPoint("BOTTOMLEFT", 0, 0)
|
||||||
|
else
|
||||||
|
-- a real bar below the power bar, not an overlay on top of it -- the same
|
||||||
|
-- treatment nameplate castbars get relative to the health bar, so the
|
||||||
|
-- window can't be mistaken for missing mana
|
||||||
|
energytick.fsrbar = CreateFrame("StatusBar", nil, pfUI.uf.player)
|
||||||
|
energytick.fsrbar:SetStatusBarTexture(cbtexture)
|
||||||
|
energytick.fsrbar:SetMinMaxValues(0, FIVE_SECOND_RULE)
|
||||||
|
energytick.fsrbar:SetPoint("TOPLEFT", pfUI.uf.player.power.bar, "BOTTOMLEFT", 0, -default_border * 2)
|
||||||
|
energytick.fsrbar:SetPoint("TOPRIGHT", pfUI.uf.player.power.bar, "BOTTOMRIGHT", 0, -default_border * 2)
|
||||||
|
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
|
||||||
|
energytick.fsrbar:SetStatusBarColor(GetStringColor(C.appearance.castbar.fsrcolor))
|
||||||
|
CreateBackdrop(energytick.fsrbar, default_border)
|
||||||
|
CreateBackdropShadow(energytick.fsrbar)
|
||||||
|
end
|
||||||
energytick.fsrbar:Hide()
|
energytick.fsrbar:Hide()
|
||||||
|
|
||||||
energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
|
energytick.spark = energytick:CreateTexture(nil, "OVERLAY")
|
||||||
@@ -256,6 +289,10 @@ pfUI:RegisterModule("energytick", function()
|
|||||||
function pfUI.uf.player.UpdateConfig()
|
function pfUI.uf.player.UpdateConfig()
|
||||||
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
|
energytick.spark:SetHeight(C.unitframes.player.pheight + 15)
|
||||||
energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
|
energytick.spark:SetWidth(C.unitframes.player.pheight + 5)
|
||||||
|
if not fsrOverlay then
|
||||||
|
energytick.fsrbar:SetHeight(tonumber(C.unitframes.player.fsrheight) or 4)
|
||||||
|
energytick.fsrbar:SetStatusBarColor(GetStringColor(C.appearance.castbar.fsrcolor))
|
||||||
|
end
|
||||||
hookUpdateConfig(pfUI.uf.player)
|
hookUpdateConfig(pfUI.uf.player)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
+11
-2
@@ -2072,8 +2072,6 @@ pfUI:RegisterModule("gui", function ()
|
|||||||
CreateConfig(nil, T["Combopoint Width"], C.unitframes, "combowidth")
|
CreateConfig(nil, T["Combopoint Width"], C.unitframes, "combowidth")
|
||||||
CreateConfig(nil, T["Combopoint Height"], C.unitframes, "comboheight")
|
CreateConfig(nil, T["Combopoint Height"], C.unitframes, "comboheight")
|
||||||
CreateConfig(nil, T["Show Resting"], C.unitframes.player, "showRest", "checkbox")
|
CreateConfig(nil, T["Show Resting"], C.unitframes.player, "showRest", "checkbox")
|
||||||
CreateConfig(nil, T["Enable Energy Ticks"], C.unitframes.player, "energy", "checkbox")
|
|
||||||
CreateConfig(nil, T["Enable Mana Ticks"], C.unitframes.player, "manatick", "checkbox")
|
|
||||||
CreateConfig(nil, T["Detect Enemy Buffs"], C.unitframes, "buffdetect", "checkbox", nil, nil, nil, nil)
|
CreateConfig(nil, T["Detect Enemy Buffs"], C.unitframes, "buffdetect", "checkbox", nil, nil, nil, nil)
|
||||||
CreateConfig(nil, T["Raid Mark Icon Style"], C.unitframes, "blizzard_raidicons", "dropdown", function() return {"1:Original Blizzard", "0:pfUI Design"} end)
|
CreateConfig(nil, T["Raid Mark Icon Style"], C.unitframes, "blizzard_raidicons", "dropdown", function() return {"1:Original Blizzard", "0:pfUI Design"} end)
|
||||||
|
|
||||||
@@ -2320,6 +2318,15 @@ pfUI:RegisterModule("gui", function ()
|
|||||||
CreateConfig(U[c], T["Right Text Y Offset"], C.unitframes[c], "txtpowerrightoffy")
|
CreateConfig(U[c], T["Right Text Y Offset"], C.unitframes[c], "txtpowerrightoffy")
|
||||||
CreateConfig(U[c], T["Power Bar Anchor"], C.unitframes[c], "panchor", "dropdown", pfUI.gui.dropdowns.uf_powerbar_position)
|
CreateConfig(U[c], T["Power Bar Anchor"], C.unitframes[c], "panchor", "dropdown", pfUI.gui.dropdowns.uf_powerbar_position)
|
||||||
|
|
||||||
|
if c == "player" then
|
||||||
|
CreateConfig(nil, T["Energy/Mana Ticks"], nil, nil, "header")
|
||||||
|
CreateConfig(nil, T["Enable Energy Ticks"], C.unitframes[c], "energy", "checkbox")
|
||||||
|
CreateConfig(nil, T["Enable Mana Ticks"], C.unitframes[c], "manatick", "checkbox")
|
||||||
|
CreateConfig(nil, T["Show Five Second Rule"], C.unitframes[c], "showfsr", "checkbox")
|
||||||
|
CreateConfig(nil, T["Five Second Rule Style"], C.unitframes[c], "fsrstyle", "dropdown", function() return {"0:Separate Bar", "1:Overlay"} end)
|
||||||
|
CreateConfig(nil, T["Five Second Rule Height"], C.unitframes[c], "fsrheight")
|
||||||
|
end
|
||||||
|
|
||||||
CreateConfig(U[c], T["Combat Text"], nil, nil, "header")
|
CreateConfig(U[c], T["Combat Text"], nil, nil, "header")
|
||||||
CreateConfig(U[c], T["Show Combat Text"], C.unitframes[c], "hitindicator", "checkbox")
|
CreateConfig(U[c], T["Show Combat Text"], C.unitframes[c], "hitindicator", "checkbox")
|
||||||
CreateConfig(U[c], T["Combat Text Font"], C.unitframes[c], "hitindicatorfont", "dropdown", pfUI.gui.dropdowns.fonts)
|
CreateConfig(U[c], T["Combat Text Font"], C.unitframes[c], "hitindicatorfont", "dropdown", pfUI.gui.dropdowns.fonts)
|
||||||
@@ -2792,6 +2799,8 @@ pfUI:RegisterModule("gui", function ()
|
|||||||
CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox")
|
CreateConfig(nil, T["Use Unit Fonts"], C.castbar, "use_unitfonts", "checkbox")
|
||||||
CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color")
|
CreateConfig(nil, T["Casting Color"], C.appearance.castbar, "castbarcolor", "color")
|
||||||
CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color")
|
CreateConfig(nil, T["Channeling Color"], C.appearance.castbar, "channelcolor", "color")
|
||||||
|
CreateConfig(nil, T["Uninterruptible Color"], C.appearance.castbar, "shieldcolor", "color")
|
||||||
|
CreateConfig(nil, T["Five Second Rule Color"], C.appearance.castbar, "fsrcolor", "color")
|
||||||
CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
|
CreateConfig(nil, T["Castbar Texture"], C.appearance.castbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
|
||||||
CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox")
|
CreateConfig(nil, T["Disable Blizzard Castbar"], C.castbar.player, "hide_blizz", "checkbox")
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ pfUI:RegisterModule("nameplates", function ()
|
|||||||
-- vs channel itself. Never called per frame.
|
-- vs channel itself. Never called per frame.
|
||||||
local function PollCastInfo(unit)
|
local function PollCastInfo(unit)
|
||||||
if not unit then return nil end
|
if not unit then return nil end
|
||||||
local name, _, texture, startMs, endMs, _, _, _, spellID = C_Spell.UnitCastingInfo(unit)
|
local name, _, texture, startMs, endMs, _, _, noInterrupt, spellID = C_Spell.UnitCastingInfo(unit)
|
||||||
local isChannel
|
local isChannel
|
||||||
if not name then
|
if not name then
|
||||||
name, _, texture, startMs, endMs, _, _, spellID = C_Spell.UnitChannelInfo(unit)
|
name, _, texture, startMs, endMs, _, noInterrupt, spellID = C_Spell.UnitChannelInfo(unit)
|
||||||
isChannel = true
|
isChannel = true
|
||||||
end
|
end
|
||||||
if not name or not startMs or not endMs then return nil end
|
if not name or not startMs or not endMs then return nil end
|
||||||
@@ -92,6 +92,7 @@ pfUI:RegisterModule("nameplates", function ()
|
|||||||
endTime = endMs / 1000,
|
endTime = endMs / 1000,
|
||||||
duration = (endMs - startMs) / 1000,
|
duration = (endMs - startMs) / 1000,
|
||||||
isChannel = isChannel,
|
isChannel = isChannel,
|
||||||
|
noInterrupt = noInterrupt,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1751,7 +1752,7 @@ nameplates:RegisterEvent("PLAYER_GUILD_UPDATE")
|
|||||||
-- Relative 0..duration range to avoid float precision loss with large
|
-- Relative 0..duration range to avoid float precision loss with large
|
||||||
-- absolute timestamps.
|
-- absolute timestamps.
|
||||||
nameplate.castbar:SetMinMaxValues(0, duration)
|
nameplate.castbar:SetMinMaxValues(0, duration)
|
||||||
nameplate.castbar:SetStatusBarColor(GetStringColor(C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")]))
|
SetCastbarShield(nameplate.castbar, nameplate.castbar.icon, castInfo.noInterrupt, isChannel)
|
||||||
if castInfo.icon then
|
if castInfo.icon then
|
||||||
nameplate.castbar.icon.tex:SetTexture(castInfo.icon)
|
nameplate.castbar.icon.tex:SetTexture(castInfo.icon)
|
||||||
nameplate.castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)
|
nameplate.castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)
|
||||||
|
|||||||
+31
-174
@@ -1,10 +1,6 @@
|
|||||||
pfUI:RegisterModule("swingtimer", function ()
|
pfUI:RegisterModule("swingtimer", function ()
|
||||||
local rawborder, border = GetBorderSize()
|
local rawborder, border = GetBorderSize()
|
||||||
|
|
||||||
-- HitInfo flags (EVENTS.md)
|
|
||||||
local HITINFO_LEFTSWING = 4 -- 4: Off-hand attack
|
|
||||||
local HITINFO_NOACTION = 65536 -- 65536: server did not advance the swing clock
|
|
||||||
|
|
||||||
-- SPELL_QUEUE_EVENT codes (EVENTS.md)
|
-- SPELL_QUEUE_EVENT codes (EVENTS.md)
|
||||||
local ON_SWING_QUEUED = 0
|
local ON_SWING_QUEUED = 0
|
||||||
local ON_SWING_QUEUE_POPPED = 1
|
local ON_SWING_QUEUE_POPPED = 1
|
||||||
@@ -19,8 +15,6 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1,
|
lastMhMarkerX = -1, lastOhMarkerX = -1, lastRaMarkerX = -1,
|
||||||
autoAttackActive = false,
|
autoAttackActive = false,
|
||||||
inCombat = false,
|
inCombat = false,
|
||||||
pendingCastSpellId = nil,
|
|
||||||
mhFrozenAt = nil,
|
|
||||||
hsQueued = false, cleaveQueued = false, maulQueued = false,
|
hsQueued = false, cleaveQueued = false, maulQueued = false,
|
||||||
hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false,
|
hsSeenCurrent = false, cleaveSeenCurrent = false, maulSeenCurrent = false,
|
||||||
isWarrior = false,
|
isWarrior = false,
|
||||||
@@ -31,13 +25,6 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
onSwingCache = {},
|
onSwingCache = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Wand "Shoot" runs on the ranged bar but is INDEPENDENT of the melee
|
|
||||||
-- swing clock — casters melee-weave between mainhand swings and wand
|
|
||||||
-- fires, both timers tick concurrently. Every other ranged-auto-attack
|
|
||||||
-- (Hunter Auto Shot, any future auto-repeat ranged spell) replaces MH.
|
|
||||||
local WAND_SHOOT_SPELLID = 5019
|
|
||||||
local THROW_SPELLID = 2764 -- one-shot ranged, not auto-repeat
|
|
||||||
|
|
||||||
-- On-next-swing spells replace the next auto-attack swing.
|
-- On-next-swing spells replace the next auto-attack swing.
|
||||||
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
|
-- Covers Raptor Strike, Maul, Mongoose Bite, Holy Strike, etc. automatically.
|
||||||
local function IsOnSwingSpell(spellId)
|
local function IsOnSwingSpell(spellId)
|
||||||
@@ -304,45 +291,37 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
S.raSpeed = (rs and rs > 0) and rs or 0
|
S.raSpeed = (rs and rs > 0) and rs or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Reset MH countdown to full speed (server confirmed swing)
|
-- Start* restart a countdown from a PLAYER_SWING reset. `remaining` can be
|
||||||
local function ResetMH()
|
-- shorter than the weapon speed (parry haste, ranged wind-up), so the bar
|
||||||
if S.mhFrozenAt then
|
-- length stays the full swing and the fill starts part-way along.
|
||||||
S.mhFrozenAt = nil
|
local function StartMH(remaining)
|
||||||
end
|
|
||||||
UpdateWeaponSpeeds()
|
UpdateWeaponSpeeds()
|
||||||
pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide
|
pfUI.swingtimer.mhGraceAt = nil -- cancel any pending hide
|
||||||
S.mhTimerMax = S.mhSpeed
|
S.mhTimerMax = math.max(S.mhSpeed, remaining)
|
||||||
S.mhTimer = S.mhSpeed
|
S.mhTimer = remaining
|
||||||
S.mhActive = true
|
S.mhActive = true
|
||||||
pfUI.swingtimer.mainhand:Show()
|
pfUI.swingtimer.mainhand:Show()
|
||||||
pfUI.swingtimer:Show()
|
pfUI.swingtimer:Show()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Reset OH countdown to full speed
|
local function StartOH(remaining)
|
||||||
local function ResetOH()
|
|
||||||
UpdateWeaponSpeeds()
|
UpdateWeaponSpeeds()
|
||||||
|
-- The off-hand attack time stays set with a shield equipped
|
||||||
if S.ohSpeed <= 0 then return end
|
if S.ohSpeed <= 0 then return end
|
||||||
pfUI.swingtimer.ohGraceAt = nil -- cancel any pending hide
|
pfUI.swingtimer.ohGraceAt = nil -- cancel any pending hide
|
||||||
S.ohTimerMax = S.ohSpeed
|
S.ohTimerMax = math.max(S.ohSpeed, remaining)
|
||||||
S.ohTimer = S.ohSpeed
|
S.ohTimer = remaining
|
||||||
S.ohActive = true
|
S.ohActive = true
|
||||||
if sw_showoh then pfUI.swingtimer.offhand:Show() end
|
if sw_showoh then pfUI.swingtimer.offhand:Show() end
|
||||||
pfUI.swingtimer:Show()
|
pfUI.swingtimer:Show()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Reset ranged countdown. replaceMH=true (Hunter Auto Shot, Throw) stops
|
local function StartRanged(remaining)
|
||||||
-- the melee swing clock while ranged ticks; replaceMH=false (wand Shoot)
|
|
||||||
-- leaves it running for melee weaving.
|
|
||||||
local function ResetRanged(replaceMH)
|
|
||||||
if not sw_showranged then return end
|
if not sw_showranged then return end
|
||||||
UpdateWeaponSpeeds()
|
UpdateWeaponSpeeds()
|
||||||
if S.raSpeed <= 0 then return end
|
if S.raSpeed <= 0 then return end
|
||||||
if replaceMH then
|
S.raTimerMax = math.max(S.raSpeed, remaining)
|
||||||
S.mhActive = false
|
S.raTimer = remaining
|
||||||
pfUI.swingtimer.mainhand:Hide()
|
|
||||||
end
|
|
||||||
S.raTimerMax = S.raSpeed
|
|
||||||
S.raTimer = S.raSpeed
|
|
||||||
S.raActive = true
|
S.raActive = true
|
||||||
|
|
||||||
if isHunter then
|
if isHunter then
|
||||||
@@ -486,23 +465,17 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
local anyActive = false
|
local anyActive = false
|
||||||
|
|
||||||
-- Tick timers down. When a timer expires we give a short grace period before
|
-- Tick timers down. When a timer expires we give a short grace period before
|
||||||
-- hiding the bar, to bridge the 1-2 frame gap until AUTO_ATTACK_SELF arrives.
|
-- hiding the bar, to bridge the 1-2 frame gap until PLAYER_SWING arrives.
|
||||||
-- If AUTO_ATTACK_SELF arrives during the grace period it resets the timer
|
-- If PLAYER_SWING arrives during the grace period it resets the timer
|
||||||
-- normally and the grace timer is cleared. If not (e.g. auto-attack was
|
-- normally and the grace timer is cleared. If not (e.g. auto-attack was
|
||||||
-- turned off), the bar hides after the grace period ends.
|
-- turned off), the bar hides after the grace period ends.
|
||||||
local GRACE = 0.15 -- seconds to wait after timer hits 0 before hiding
|
local GRACE = 0.15 -- seconds to wait after timer hits 0 before hiding
|
||||||
|
|
||||||
if S.mhActive then
|
if S.mhActive then
|
||||||
-- Don't tick while frozen (swing-delay spell like Slam is being cast)
|
S.mhTimer = S.mhTimer - delta
|
||||||
if not S.mhFrozenAt then
|
|
||||||
S.mhTimer = S.mhTimer - delta
|
|
||||||
end
|
|
||||||
if S.mhTimer <= 0 then
|
if S.mhTimer <= 0 then
|
||||||
S.mhTimer = 0
|
S.mhTimer = 0
|
||||||
if S.mhFrozenAt then
|
if not pfUI.swingtimer.mhGraceAt then
|
||||||
-- Swing-delay spell cast in progress: bar holds at 0, skip grace/hide.
|
|
||||||
-- SPELL_GO handler will unfreeze and add cast duration to timer.
|
|
||||||
elseif not pfUI.swingtimer.mhGraceAt then
|
|
||||||
pfUI.swingtimer.mhGraceAt = GetTime() + GRACE
|
pfUI.swingtimer.mhGraceAt = GetTime() + GRACE
|
||||||
elseif GetTime() >= pfUI.swingtimer.mhGraceAt then
|
elseif GetTime() >= pfUI.swingtimer.mhGraceAt then
|
||||||
pfUI.swingtimer.mhGraceAt = nil
|
pfUI.swingtimer.mhGraceAt = nil
|
||||||
@@ -722,80 +695,13 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- SPELL_START_SELF: fires only for cast-time spells, never instants
|
-- SPELL_GO hook via libdebuff: an on-next-swing ability (HS / Cleave /
|
||||||
local spellStartFrame = CreateFrame("Frame")
|
-- Maul) resolving consumes the queue, so drop the queued color.
|
||||||
spellStartFrame:RegisterEvent("SPELL_START_SELF")
|
|
||||||
spellStartFrame:SetScript("OnEvent", function()
|
|
||||||
if not (arg1 and arg1 > 0) then return end
|
|
||||||
S.pendingCastSpellId = arg1
|
|
||||||
-- Freeze the swing timer for cast-time spells that DON'T reset auto-
|
|
||||||
-- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
|
|
||||||
-- let the swing resume from where it paused. C_Spell.ResetsMeleeSwing
|
|
||||||
-- mirrors the server rule; spells that reset don't need freezing (they
|
|
||||||
-- reset on SPELL_GO_SELF). Subsumes the old hardcoded swingDelaySpells
|
|
||||||
-- list (no list maintenance for new Slam-style spells).
|
|
||||||
if S.mhActive then
|
|
||||||
if not C_Spell.ResetsMeleeSwing(arg1) then
|
|
||||||
S.mhFrozenAt = GetTime()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Cast cancel/fail/interrupt: unfreeze swing timer if a delay spell was interrupted
|
|
||||||
local spellFailFrame = CreateFrame("Frame")
|
|
||||||
spellFailFrame:RegisterEvent("SPELLCAST_FAILED")
|
|
||||||
spellFailFrame:RegisterEvent("SPELLCAST_INTERRUPTED")
|
|
||||||
spellFailFrame:SetScript("OnEvent", function()
|
|
||||||
if S.mhFrozenAt then
|
|
||||||
S.mhFrozenAt = nil
|
|
||||||
end
|
|
||||||
S.pendingCastSpellId = nil
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- SPELL_GO hook via libdebuff
|
|
||||||
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
pfUI.libdebuff_spell_go_hooks = pfUI.libdebuff_spell_go_hooks or {}
|
||||||
pfUI.libdebuff_spell_go_hooks["swingtimer"] = function(spellId)
|
pfUI.libdebuff_spell_go_hooks["swingtimer"] = function(spellId)
|
||||||
-- C_Spell.IsRangedAutoAttackSpell catches both Auto Shot (75) and
|
if IsOnSwingSpell(spellId) then
|
||||||
-- wand Shoot (5019) via Spell.dbc's AUTO_REPEAT attribute (covers
|
|
||||||
-- any future auto-repeat ranged spell automatically). Wand is the
|
|
||||||
-- one independent of the MH swing — everything else replaces it.
|
|
||||||
-- Throw isn't auto-repeat (single-shot) so it's handled explicitly.
|
|
||||||
if C_Spell.IsRangedAutoAttackSpell(spellId) then
|
|
||||||
ResetRanged(spellId ~= WAND_SHOOT_SPELLID)
|
|
||||||
return
|
|
||||||
elseif spellId == THROW_SPELLID then
|
|
||||||
ResetRanged(true)
|
|
||||||
return
|
|
||||||
elseif IsOnSwingSpell(spellId) then
|
|
||||||
-- On-next-swing ability (HS / Cleave / Maul / Raptor Strike / etc.)
|
|
||||||
-- — the swing fires as the spell consumes it. Drop the queued color.
|
|
||||||
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
|
||||||
ResetMH()
|
|
||||||
else
|
|
||||||
-- C_Spell.ResetsMeleeSwing mirrors the server rule (Turtle's
|
|
||||||
-- Spell::IsMeleeAttackResetSpell): InterruptFlags has AUTOATTACK and
|
|
||||||
-- AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS. When the spell resets the
|
|
||||||
-- swing, snap the timers to full. Otherwise (elseif) a frozen-swing-
|
|
||||||
-- during-cast is a Slam-style cast — push the timer forward by the cast
|
|
||||||
-- duration so the bar resumes from where it paused.
|
|
||||||
if C_Spell.ResetsMeleeSwing(spellId) then
|
|
||||||
if S.mhActive and S.mhSpeed > 0 then
|
|
||||||
UpdateWeaponSpeeds()
|
|
||||||
S.mhTimerMax = S.mhSpeed
|
|
||||||
S.mhTimer = S.mhSpeed
|
|
||||||
end
|
|
||||||
if S.ohActive and S.ohSpeed > 0 then
|
|
||||||
S.ohTimerMax = S.ohSpeed
|
|
||||||
S.ohTimer = S.ohSpeed
|
|
||||||
end
|
|
||||||
elseif S.mhFrozenAt then
|
|
||||||
local castDuration = GetTime() - S.mhFrozenAt
|
|
||||||
S.mhTimer = S.mhTimer + castDuration
|
|
||||||
S.mhTimerMax = S.mhTimerMax + castDuration
|
|
||||||
S.mhFrozenAt = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
S.pendingCastSpellId = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
|
-- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
|
||||||
@@ -809,8 +715,7 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
|
|
||||||
|
|
||||||
local events = CreateFrame("Frame")
|
local events = CreateFrame("Frame")
|
||||||
events:RegisterEvent("AUTO_ATTACK_SELF")
|
events:RegisterEvent("PLAYER_SWING")
|
||||||
events:RegisterEvent("AUTO_ATTACK_OTHER")
|
|
||||||
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||||
events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: per-slot, equipment-only
|
events:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- ClassicAPI: per-slot, equipment-only
|
||||||
events:RegisterEvent("PLAYER_REGEN_DISABLED")
|
events:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||||
@@ -822,64 +727,16 @@ pfUI:RegisterModule("swingtimer", function ()
|
|||||||
events:RegisterEvent("STOP_AUTOATTACK")
|
events:RegisterEvent("STOP_AUTOATTACK")
|
||||||
|
|
||||||
events:SetScript("OnEvent", function()
|
events:SetScript("OnEvent", function()
|
||||||
if event == "AUTO_ATTACK_SELF" then
|
if event == "PLAYER_SWING" then
|
||||||
local hitInfo = arg4 or 0
|
-- ClassicAPI tracks the server's swing clock (white hits, on-next-swing
|
||||||
local isOffhand = bit.band(hitInfo, HITINFO_LEFTSWING) ~= 0
|
-- abilities, swing-reset casts, parry haste, extra attacks, weapon
|
||||||
local noAction = bit.band(hitInfo, HITINFO_NOACTION) ~= 0
|
-- swaps) and hands over the seconds left until the next swing.
|
||||||
-- NOACTION means server did not advance swing clock (dodge/parry/miss).
|
if arg2 == Enum.PlayerSwingType.MainHand then
|
||||||
-- Only skip if the timer is already running - if it's not active yet
|
StartMH(arg1)
|
||||||
-- (first swing ever), we still want to start it so the bar appears.
|
elseif arg2 == Enum.PlayerSwingType.OffHand then
|
||||||
if noAction then
|
StartOH(arg1)
|
||||||
if isOffhand and S.ohActive then return end
|
elseif arg2 == Enum.PlayerSwingType.Ranged then
|
||||||
if not isOffhand and S.mhActive then return end
|
StartRanged(arg1)
|
||||||
end
|
|
||||||
|
|
||||||
-- Extra attack detection: if timer still has >20% remaining for that hand,
|
|
||||||
-- the server did NOT reset the swing clock -> this is an extra attack, skip.
|
|
||||||
-- Use 20% here (SP_SwingTimer's ShouldResetTimer threshold).
|
|
||||||
-- Exception: if timer is already at 0 (expired), always accept.
|
|
||||||
if isOffhand then
|
|
||||||
local pct = S.ohActive and (S.ohTimer / S.ohTimerMax) or 0
|
|
||||||
if S.ohActive and S.ohTimer > 0 and pct > 0.20 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
ResetOH()
|
|
||||||
else
|
|
||||||
local pct = S.mhActive and (S.mhTimer / S.mhTimerMax) or 0
|
|
||||||
if S.mhActive and S.mhTimer > 0 and pct > 0.20 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
ResetMH()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif event == "AUTO_ATTACK_OTHER" then
|
|
||||||
-- Parry haste: enemy attacked the player and player parried
|
|
||||||
local targetGuid = arg2
|
|
||||||
if not targetGuid or not IsPlayerGuid(targetGuid) then return end
|
|
||||||
local victimState = arg5 or 0
|
|
||||||
-- VICTIMSTATE_PARRY = 3
|
|
||||||
-- Vanilla: parry reduces the NEXT swing timer by 40% of weapon speed,
|
|
||||||
-- minimum 20% of weapon speed remaining (SP_SwingTimer approach)
|
|
||||||
if victimState == 3 then
|
|
||||||
-- Apply to whichever swing comes next (smallest % remaining = closest to firing)
|
|
||||||
if S.ohActive and S.ohSpeed > 0 and (S.ohTimer / S.ohTimerMax) < (S.mhTimer / S.mhTimerMax) then
|
|
||||||
local minimum = S.ohSpeed * 0.20
|
|
||||||
if S.ohTimer > minimum then
|
|
||||||
local reduct = S.ohSpeed * 0.40
|
|
||||||
local before = S.ohTimer
|
|
||||||
S.ohTimer = S.ohTimer - reduct
|
|
||||||
if S.ohTimer < minimum then S.ohTimer = minimum end
|
|
||||||
end
|
|
||||||
elseif S.mhActive and S.mhSpeed > 0 then
|
|
||||||
local minimum = S.mhSpeed * 0.20
|
|
||||||
if S.mhTimer > minimum then
|
|
||||||
local reduct = S.mhSpeed * 0.40
|
|
||||||
local before = S.mhTimer
|
|
||||||
S.mhTimer = S.mhTimer - reduct
|
|
||||||
if S.mhTimer < minimum then S.mhTimer = minimum end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event == "SPELL_QUEUE_EVENT" then
|
elseif event == "SPELL_QUEUE_EVENT" then
|
||||||
|
|||||||
Reference in New Issue
Block a user