19 Commits
Author SHA1 Message Date
Brues 5f61f94a24 Texture ArenaFrame 2026-06-27 23:31:02 -05:00
Brues 65678da3d2 tooltip: optional movement-speed line via GetUnitSpeed
New tooltip.movespeed config knob (default off, checkbox in the GUI's
tooltip page). When on, the unit tooltip gains a "Speed: N%" line where
N is the unit's run speed normalized to vanilla's 7.0 yd/s base — 100
unmounted, 160 on a 60% mount, 200 on epic, less under snares.

Uses runSpeed (return 2 of GetUnitSpeed), not currentSpeed, so the
number reflects what the unit *would* be running at — visible even
while they're standing still. runSpeed is 0 for out-of-range units, so
the line is skipped in that case.
2026-06-27 19:28:40 -05:00
Brues 9b7aca8541 show CLASSIC_API_VERSION in /pfdll 2026-06-27 19:20:06 -05:00
Brues 60525eec3a Clean up throttle 2026-06-27 19:19:51 -05:00
Brues c3829c2bd2 player: drop talent-side modCastingTime fudge from haste display
The player frame's "Effective Haste" mode was hardcoded talent-position
scrapes: GetTalentInfo(1, 16) for the Mage "Accelerated Arcana"
(flat 5%) and GetTalentInfo(1, 14) for the Warlock "Rapid Deterioration"
(3% per rank), folded into the displayed haste % via
`(1 / (modCastSpeed * modCastingTime) - 1) * 100`.

That's two problems in one:
- Hardcoded talent indices and effect percentages — brittle to any
  Turtle tree reshuffle or retune.
- Conceptually muddled: it folds gear-haste and talent-cast-reduction
  into one number that's hard to read as anything specific. The actual
  effective cast time is already shown on the cast bar via
  C_Spell.UnitCastingInfo (engine helper accounts for SpellMod op 10).

Drop modCastingTime, the LEARNED_SPELL_IN_TAB watcher frame that
maintained it, the per-class talent scrape, and the hasteMode == "2"
display branch. Collapse the now-binary "display_haste" config from a
3-option dropdown to a checkbox. Users on legacy "2" will see the
checkbox unchecked once and can re-enable with a single click.
2026-06-27 19:13:49 -05:00
Brues 28de6fd835 finish UnitInRaid("player") → IsInRaid() sweep
Four more sites: GetUnbuffedRoster + SendChatMessageWide in api.lua,
the loot menu's inRaid local, and the raid module's early-return
guard. Same intent, named helper.
2026-06-27 18:57:21 -05:00
Brues fc75e091a4 finish IsInRaid sweep across remaining "raid count > 0" sites
Three more `if GetNumRaidMembers() > 0 then` raid-vs-party branches
switched to `if IsInRaid()`. Same intent, named helper. Repo is now
clean of the legacy idiom (verified with a final grep).

Loops that actually need the count (`for i = 1, GetNumRaidMembers() do
GetRaidRosterInfo(i)`) keep the call — only the boolean form changes.
2026-06-27 18:56:50 -05:00
Brues ef478b0552 thirdparty-vanilla: collapse solo check to not IsInGroup()
The HealComm self-message guard was `not UnitInRaid("player") and
GetNumPartyMembers() < 1` — the long form of "in no group at all."
2026-06-27 18:55:49 -05:00
Brues 760d8992b6 switch group-membership checks to IsInGroup / IsInRaid
ClassicAPI ships modern IsInGroup() / IsInRaid() backports — drop the
GetNumPartyMembers() > 0 and GetNumRaidMembers() > 0 idioms (and the
GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0 conflation) for the
named-intent variants. UnitInRaid("player") → IsInRaid() at the same
sites.
2026-06-27 18:54:04 -05:00
Brues 62a99ac410 mouseover/libpredict: drop legacy spell-target plumbing
With Nampower as a hard dep, /pfcast for spell names always takes the
early CastSpellByName(msg, unit) path. The fallback branch that did the
SpellTargetUnit dance (resolve a friendly unit token, disable AutoSelf
Cast, call SpellTargetUnit) hasn't been reachable in a while, and it
dragged a pile of supporting infrastructure with it.

modules/mouseover.lua:
- Drop the st_units token list, GetUnitString helper, and the
  UnitTokenFromGUID rewrite of GetUnitString — all only used by the
  dead fallback.
- Drop the NoSelfCast helper (only the dead fallback called it).
- Drop the pfMouseOver frame; its only purpose was to hold a .unit
  field the dead fallback wrote and libpredict's hook read.
- The macro path collapses to: if not the current target, swap target,
  run the loadstring'd func, restore the previous target.
- 99 lines → 34.

libs/libpredict.lua:
- Drop the dead `local mouseover = pfUI.uf.mouseover.unit` plumbing in
  the CastSpellByName hook — pfUI.uf.mouseover is gone and the field
  was permanently nil anyway. The three `target or mouseover or default`
  fallback chains collapse to `target or default`.

Modern mouseover/click-to-cast detection in libpredict goes through
pfUI.libpredict_pending_cast (populated by libdebuff from Nampower's
SPELL_CAST_EVENT) — that path is GUID-based, server-authoritative, and
untouched.
2026-06-27 18:41:11 -05:00
Brues deeec89955 predict + libdebuff + swingtimer: drop hardcoded spell data
Replace per-locale name tables and per-rank ID lists with single
canonical-rank lookups through C_Spell. Spell.dbc bits hoisted to
named constants at module top.

libpredict:
- Four 7-locale tables (PRAYER_OF_HEALING / REJUVENATION / RENEW /
  REGROWTH) collapsed to one C_Spell.GetSpellName(rank1id) call each.
- 25-entry SPELL_IDS (all ranks of Rejuv + Renew) for SPELL_GO_SELF
  HoT detection replaced by name comparison against REJUVENATION /
  RENEW. No per-rank ID maintenance.

libdebuff:
- GetSpellRecField(id, "name") → C_Spell.GetSpellName(id) at all call
  sites; the presence-guard pattern is gone (ClassicAPI is a hard
  dep, per memory).
- GetSpellRecField(id, "rank") → C_Spell.GetSpellSubtext(id).

swingtimer:
- Hoist FLAG_AUTOATTACK / ATTR_KEEP_SWINGS / ATTR_ON_NEXT_SWING to
  module-top constants via tonumber("0xNN", 16) so the SPELL_GO_SELF
  hot path stops re-parsing them on every call. Lua 5.0 has no hex
  number literals; strtoul-backed tonumber handles the "0x" prefix.
2026-06-27 18:22:03 -05:00
Brues cca9771ef0 swingtimer: derive spell categories from Spell.dbc attributes
Drop four hardcoded spell-ID tables (swingDelaySpells, hsSpellIDs,
cleaveSpellIDs, maulSpellIDs) and the broad interruptFlags > 0 reset
heuristic in favor of server-parity checks against Spell.dbc bits via
nampower's GetSpellRecField:

- "Resets the swing on cast complete" gated on InterruptFlags's
  SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08) and AttributesEx2's lack of
  SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS (0x20000) — mirrors the server's
  Spell::IsMeleeAttackResetSpell. Reset OH alongside MH to match.

- "Slam-style delay" derived from absent 0x08 + a cast time existing
  (implicit since SPELL_START_SELF only fires for cast-time spells).
  Freezes the swing timer at SPELL_START and adds cast duration on
  SPELL_GO instead of resetting.

- HS / Cleave / Maul classification via IsOnSwingSpell (ATTR_ON_NEXT_
  SWING bit 0x04) + name comparison against rank-1 canonical names,
  centralized into ClassifyOnSwingSpell + SetQueuedKind helpers.

Switch the lone GetSpellRec table-allocator call to GetSpellRecField for
consistency (single-field hash lookup, no shared-table reuse caveat).
2026-06-27 17:52:31 -05:00
Brues 371872bb90 swingtimer: route wand Shoot to ranged bar without resetting MH
Wand Shoot (spellID 5019) wasn't recognized as ranged anywhere, so it
fell through every branch of the SPELL_GO_SELF dispatch and hit the
catch-all interruptFlags > 0 reset, wiping the mainhand swing on every
shot. Casters melee-weaving between MH swings and wand fires lost their
swing visualization.

Replace the hardcoded RANGED_SPELLIDS / WAND_SHOOT_SPELLIDS tables with
C_Spell.IsRangedAutoAttackSpell (Spell.dbc AUTO_REPEAT bit) — catches
both Auto Shot and Shoot today, plus any future auto-repeat ranged
spell. ResetRanged now takes a replaceMH flag: true for Auto Shot /
Throw (Hunter ranged replaces melee), false for wand (independent
timers, both tick concurrently).

Closes #5.
2026-06-27 17:14:48 -05:00
Brues 591045a606 config: stamp sentinel version on dev builds to stop migration re-fire
Dev / git-cloned installs carry "@project-version@" in the toc, which
pfUI normalizes to pfUI.version.string = "dev". MigrateConfig was
writing that literal back into pfUI_config.version; on the next /reload
checkversion() parsed "dev" through tonumber() → nil → 0, evaluated
curversion as 0/0/0, and re-fired every version-gated migration block.
The >3.6.1 buff migration unconditionally rewrites buffs.{buffs,debuffs,
weapons} from the legacy global.hide{,w}buff knobs, so user toggles for
those checkboxes were getting wiped on every reload.

Stamp "999.999.999" on dev builds so subsequent comparisons turn false.
Affected users will need to re-toggle the buffs/debuffs/weapon-buffs
checkboxes once after this update.

Refs #7.
2026-06-27 16:46:56 -05:00
Brues f657a9e7e8 drop MobHealth3 / MobHealthFrame fallbacks
libhealth ships with pfUI and Nampower's GetUnitField (hard-dep) covers
the real-HP read; the MobHealth integration was a chained last-resort
fallback that could never actually win, and on the nameplate path it
also mixed percentage and real-value scales into the bar's SetMinMaxValues
since it overwrote hp/hpmax without hpmin.
2026-06-27 16:08:39 -05:00
Brues 0cbd9ecf98 Revert "nameplates: drop redundant name check from plate-reassignment gate"
This reverts commit 6ca482fcaa.
2026-06-27 16:01:24 -05:00
Brues 6ca482fcaa nameplates: drop redundant name check from plate-reassignment gate
GUID is the unique identity; the name check was leftover from the
name-only era and became dead weight once cachedGuid (NAME_PLATE_UNIT_
ADDED) was added alongside it.
2026-06-27 15:59:16 -05:00
Brues 8902d43f40 nameplates: wipe full plate.cache on reassignment
The plate-reuse reset only cleared name/guid/player/cdCache, leaving
hp/hpmax/rgb/namecolor/levelcolor/target/mouseover/inCombat behind. The
PERF gates downstream ("only update X when X changed") then skipped bar
fill / color / text updates when the new occupant happened to share a
cached value with the previous one — most easily reproduced on
plate-pool reuse in starting zones (mobs churn fast, percentage values
collide). Nuke the whole cache via table.wipe.

Refs #8.
2026-06-27 15:48:53 -05:00
brues-code 8f006c0643 Update issue templates 2026-06-27 15:07:04 -05:00
21 changed files with 237 additions and 383 deletions
+7 -6
View File
@@ -4,16 +4,18 @@ about: Report a bug or issue with pfUI
title: "[Bug Description] " title: "[Bug Description] "
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
<!-- Please fill out ALL fields below. Issues without this info may be closed. --> <!-- Please fill out ALL fields below. Issues without this info may be closed. -->
**pfUI Branch:** **pfUI Version:**
- [ ] Master
- [ ] experiment **Your ClassicAPI Version:**
**Your Nampower Version:**
**Your Nampower Version:** (e.g. latest, or specific commit/date)
<!-- Skip if using Compat branch -->
**Other Addons installed:** **Other Addons installed:**
<!-- List any other addons you are using, or "none" --> <!-- List any other addons you are using, or "none" -->
@@ -45,4 +47,3 @@ Paste error log here
## Screenshot / Video ## Screenshot / Video
<!-- Attach a screenshot or video showing the issue. This is REQUIRED. --> <!-- Attach a screenshot or video showing the issue. This is REQUIRED. -->
<!-- Drag & drop images here or paste a link --> <!-- Drag & drop images here or paste a link -->
+1 -1
View File
@@ -4,6 +4,7 @@ about: Suggest a new feature or improvement for pfUI
title: "[Feature] " title: "[Feature] "
labels: enhancement labels: enhancement
assignees: '' assignees: ''
--- ---
## Feature Description ## Feature Description
@@ -20,4 +21,3 @@ assignees: ''
## Examples ## Examples
<!-- If possible, show examples from other addons or screenshots/mockups of how it could look --> <!-- If possible, show examples from other addons or screenshots/mockups of how it could look -->
+2 -2
View File
@@ -208,7 +208,7 @@ function pfUI.api.GetUnbuffedRoster(name)
end end
end end
if UnitInRaid("player") then if IsInRaid() then
for i=1,40 do check("raid"..i) end for i=1,40 do check("raid"..i) end
else else
check("player") check("player")
@@ -485,7 +485,7 @@ end
-- 'msg' [string] the message to send -- 'msg' [string] the message to send
function pfUI.api.SendChatMessageWide(msg) function pfUI.api.SendChatMessageWide(msg)
local channel = "SAY" local channel = "SAY"
if UnitInRaid("player") then if IsInRaid() then
if ( IsRaidLeader() or IsRaidOfficer() ) then if ( IsRaidLeader() or IsRaidOfficer() ) then
channel = "RAID_WARNING" channel = "RAID_WARNING"
else else
+14 -1
View File
@@ -748,6 +748,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("tooltip", nil, "cursoroffset", "20") pfUI:UpdateConfig("tooltip", nil, "cursoroffset", "20")
pfUI:UpdateConfig("tooltip", nil, "extguild", "1") pfUI:UpdateConfig("tooltip", nil, "extguild", "1")
pfUI:UpdateConfig("tooltip", nil, "itemid", "0") pfUI:UpdateConfig("tooltip", nil, "itemid", "0")
pfUI:UpdateConfig("tooltip", nil, "movespeed", "0")
pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8") pfUI:UpdateConfig("tooltip", nil, "alpha", "0.8")
pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0") pfUI:UpdateConfig("tooltip", nil, "alwaysperc", "0")
pfUI:UpdateConfig("tooltip", "compare", "basestats", "1") pfUI:UpdateConfig("tooltip", "compare", "basestats", "1")
@@ -1387,5 +1388,17 @@ function pfUI:MigrateConfig()
end end
end end
pfUI_config.version = pfUI.version.string -- Stamp the version we've migrated to. Dev / git-cloned builds carry
-- "@project-version@" in the toc, which pfUI.lua normalizes to "dev" —
-- writing that here would have checkversion() parse it back as 0/0/0 on
-- the next /reload and re-fire every migration block, clobbering user
-- toggles (notably the >3.6.1 buff migration that rewrites
-- buffs.{buffs,debuffs,weapons} from the deprecated global.hide{,w}buff
-- knobs). Stamp a far-future sentinel instead so checkversion() can't
-- flip true again on subsequent loads.
if pfUI.version.string == "dev" then
pfUI_config.version = "999.999.999"
else
pfUI_config.version = pfUI.version.string
end
end end
+5 -7
View File
@@ -305,12 +305,12 @@ function pfUI.uf:UpdateVisibility()
local id = self.cache_raid local id = self.cache_raid
-- always show self in raidframes -- always show self in raidframes
if not UnitInRaid("player") and GetNumPartyMembers() == 0 and C.unitframes.selfinraid == "1" and id == 1 then if not IsInRaid() and IsInGroup() and C.unitframes.selfinraid == "1" and id == 1 then
self.id = "" self.id = ""
self.label = "player" self.label = "player"
-- use raidframes for groups -- use raidframes for groups
elseif not UnitInRaid("player") and GetNumPartyMembers() > 0 and C.unitframes.raidforgroup == "1" then elseif not IsInRaid() and IsInGroup() and C.unitframes.raidforgroup == "1" then
if id == 1 then if id == 1 then
self.id = "" self.id = ""
self.label = "player" self.label = "player"
@@ -347,12 +347,12 @@ function pfUI.uf:UpdateVisibility()
-- frame shall not be visible -- frame shall not be visible
visibility = "hide" visibility = "hide"
self.visible = nil self.visible = nil
elseif C["unitframes"]["group"]["hide_in_raid"] == "1" and self.label and strsub(self.label,0,5) == "party" and UnitInRaid("player") then elseif C["unitframes"]["group"]["hide_in_raid"] == "1" and self.label and strsub(self.label,0,5) == "party" and IsInRaid() then
-- hide group while in raid and option is set -- hide group while in raid and option is set
visibility = "hide" visibility = "hide"
self.visible = nil self.visible = nil
elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" ) elseif ( self.fname == "Group0" or self.fname == "PartyPet0" or self.fname == "Party0Target" )
and (GetNumPartyMembers() <= 0 or (C["unitframes"]["group"]["hide_in_raid"] == "1" and UnitInRaid("player"))) then and (not IsInGroup() or (C["unitframes"]["group"]["hide_in_raid"] == "1" and IsInRaid())) then
-- hide self in group if solo or hide in raid is set -- hide self in group if solo or hide in raid is set
visibility = "hide" visibility = "hide"
self.visible = nil self.visible = nil
@@ -1773,7 +1773,7 @@ function pfUI.uf:RefreshIndicators(unit)
local unitstr = unit.label .. unit.id local unitstr = unit.label .. unit.id
if unit.leaderIcon then -- Leader Icon if unit.leaderIcon then -- Leader Icon
if unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and ( GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0 ) then if unit.config.leadericon == "1" and UnitIsPartyLeader(unitstr) and IsInGroup() then
unit.leaderIcon:Show() unit.leaderIcon:Show()
else else
unit.leaderIcon:Hide() unit.leaderIcon:Hide()
@@ -2838,8 +2838,6 @@ function pfUI.uf:GetStatusValue(unit, pos)
-- Use libhealth for mob health estimation (overrides Nampower/Standard) -- Use libhealth for mob health estimation (overrides Nampower/Standard)
if pfUI.libhealth and pfUI.libhealth.enabled then if pfUI.libhealth and pfUI.libhealth.enabled then
rhp, rhpmax = pfUI.libhealth:GetUnitHealth(unitstr) rhp, rhpmax = pfUI.libhealth:GetUnitHealth(unitstr)
elseif unit.label == "target" and (MobHealth3 or MobHealthFrame) and MobHealth_GetTargetCurHP() then
rhp, rhpmax = MobHealth_GetTargetCurHP(), MobHealth_GetTargetMaxHP()
end end
if config == "unit" then if config == "unit" then
+13 -13
View File
@@ -427,7 +427,7 @@ local function GetDebuffSlotMap(guid)
local spellId = auras[auraSlot] local spellId = auras[auraSlot]
if spellId and spellId > 0 then if spellId and spellId > 0 then
displaySlot = displaySlot + 1 displaySlot = displaySlot + 1
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
local texture = libdebuff:GetSpellIcon(spellId) local texture = libdebuff:GetSpellIcon(spellId)
local stacks = (auraApps and auraApps[auraSlot] or 0) + 1 local stacks = (auraApps and auraApps[auraSlot] or 0) + 1
local dtype = nil local dtype = nil
@@ -1024,7 +1024,7 @@ if hasNampower then
local targetGuid = arg4 local targetGuid = arg4
local numHit = arg6 or 0 local numHit = arg6 or 0
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, for swing reset)
if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then if event == "SPELL_GO_SELF" and pfUI.libdebuff_spell_go_hooks then
@@ -1035,15 +1035,15 @@ if hasNampower then
if numMissed > 0 or numHit == 0 then return end if numMissed > 0 or numHit == 0 then return end
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
local spellRankString = GetSpellRecField and GetSpellRecField(spellId, "rank")
if not spellName then return end if not spellName then return end
local spellRankString = C_Spell.GetSpellSubtext(spellId)
local castRank = 0 local castRank = 0
if spellRankString and spellRankString ~= "" then if spellRankString and spellRankString ~= "" then
castRank = tonumber((string.gsub(spellRankString, "Rank ", ""))) or 0 castRank = tonumber((string.gsub(spellRankString, "Rank ", ""))) or 0
end end
-- Store in pendingCasts for DEBUFF_ADDED correlation. -- Store in pendingCasts for DEBUFF_ADDED correlation.
-- If this cast is a downrank of an already active debuff, fire the downrank blocked hook -- If this cast is a downrank of an already active debuff, fire the downrank blocked hook
-- so external addons (e.g. SuperCleveRoidMacros) don't need to re-implement this check. -- so external addons (e.g. SuperCleveRoidMacros) don't need to re-implement this check.
@@ -1209,7 +1209,7 @@ if hasNampower then
if not spellId then return end if not spellId then return end
if not targetGuid or targetGuid == "" or targetGuid == "0x0000000000000000" then return end if not targetGuid or targetGuid == "" or targetGuid == "0x0000000000000000" then return end
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
-- Deduplicate: Ignore if we processed this exact cast recently (within 100ms) -- Deduplicate: Ignore if we processed this exact cast recently (within 100ms)
@@ -1469,7 +1469,7 @@ if hasNampower then
-- Invalidate slot map cache for this GUID -- Invalidate slot map cache for this GUID
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
if debugStats.enabled then if debugStats.enabled then
@@ -1599,9 +1599,9 @@ if hasNampower then
local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil local auraSlot = auraSlot_0based and (auraSlot_0based + 1) or nil
-- Invalidate slot map cache for this GUID -- Invalidate slot map cache for this GUID
local spellName = (GetSpellRecField and GetSpellRecField(spellId, "name")) or "?" local spellName = C_Spell.GetSpellName(spellId) or "?"
if debugStats.enabled then if debugStats.enabled then
debugStats.debuff_removed = debugStats.debuff_removed + 1 debugStats.debuff_removed = debugStats.debuff_removed + 1
if IsCurrentTarget(guid) then if IsCurrentTarget(guid) then
@@ -1609,13 +1609,13 @@ if hasNampower then
GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName)) GetDebugTimestamp(), displaySlot, auraSlot or -1, auraSlot_0based or -1, spellName))
end end
end end
-- If unit is dead, cleanup all -- If unit is dead, cleanup all
if UnitIsDead and UnitIsDead(guid) then if UnitIsDead and UnitIsDead(guid) then
CleanupUnit(guid) CleanupUnit(guid)
return return
end end
-- Get auraSlot from event parameter (Nampower 2.29+) -- Get auraSlot from event parameter (Nampower 2.29+)
-- Fallback to displayToAura mapping if not available -- Fallback to displayToAura mapping if not available
local foundAuraSlot = auraSlot local foundAuraSlot = auraSlot
+32 -94
View File
@@ -44,78 +44,13 @@ local healGuidToName = {} -- [casterGuid] = casterName, for SPELL_FAILED_OTHER c
local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window) local ress_timers = {} -- [target][sender] = expiry_timestamp (60s rez window)
local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s local RESS_TIMEOUT = 60 -- Vanilla: rez offer expires after 60s
local PRAYER_OF_HEALING -- Localized spell names resolved once from canonical rank-1 spellIDs.
do -- Prayer of Healing -- Every rank shares the same name, so per-rank comparisons elsewhere can
local locales = { -- be done against these constants without per-locale or per-rank tables.
["deDE"] = "Gebet der Heilung", local PRAYER_OF_HEALING = C_Spell.GetSpellName(596) -- Prayer of Healing (Rank 1)
["enUS"] = "Prayer of Healing", local REJUVENATION = C_Spell.GetSpellName(774) -- Rejuvenation (Rank 1)
["esES"] = "Rezo de curación", local RENEW = C_Spell.GetSpellName(139) -- Renew (Rank 1)
["frFR"] = "Prière de soins", local REGROWTH = C_Spell.GetSpellName(8936) -- Regrowth (Rank 1)
["koKR"] = "치유의 기원",
["ruRU"] = "Молитва исцеления",
["zhCN"] = "治疗祷言",
}
PRAYER_OF_HEALING = locales[GetLocale()] or locales["enUS"]
end
local REJUVENATION
do -- Rejuvenation
local locales = {
["deDE"] = "Verjüngung",
["enUS"] = "Rejuvenation",
["esES"] = "Rejuvenecimiento",
["frFR"] = "Récupération",
["koKR"] = "회복",
["ruRU"] = "Омоложение",
["zhCN"] = "回春术",
}
REJUVENATION = locales[GetLocale()] or locales["enUS"]
end
local RENEW
do -- Renew
local locales = {
["deDE"] = "Erneuerung",
["enUS"] = "Renew",
["esES"] = "Renovar",
["frFR"] = "Rénovation",
["koKR"] = "소생",
["ruRU"] = "Обновление",
["zhCN"] = "恢复",
}
RENEW = locales[GetLocale()] or locales["enUS"]
end
local REGROWTH
do -- Regrowth
local locales = {
["deDE"] = "Nachwachsen",
["enUS"] = "Regrowth",
["esES"] = "Recrecimiento",
["frFR"] = "Rétablissement",
["koKR"] = "재생",
["ruRU"] = "Восстановление",
["zhCN"] = "愈合",
}
REGROWTH = locales[GetLocale()] or locales["enUS"]
end
-- Spell IDs for SPELL_GO_SELF callback (Nampower) - Instant HoT detection
local SPELL_IDS = {
-- Rejuvenation (all ranks)
[774] = "Reju", [1058] = "Reju", [1430] = "Reju", [2090] = "Reju", [2091] = "Reju",
[3627] = "Reju", [8910] = "Reju", [9839] = "Reju", [9840] = "Reju", [9841] = "Reju",
[25299] = "Reju", [26981] = "Reju", [26982] = "Reju",
-- Renew (all ranks)
[139] = "Renew", [6074] = "Renew", [6075] = "Renew", [6076] = "Renew", [6077] = "Renew",
[6078] = "Renew", [10927] = "Renew", [10928] = "Renew", [10929] = "Renew", [25315] = "Renew",
[25221] = "Renew", [25222] = "Renew",
}
local libpredict = CreateFrame("Frame") local libpredict = CreateFrame("Frame")
libpredict:RegisterEvent("UNIT_HEALTH") libpredict:RegisterEvent("UNIT_HEALTH")
@@ -173,7 +108,7 @@ end
local function isRezSpell(spellId) local function isRezSpell(spellId)
if not L["resurrections"] then return false end if not L["resurrections"] then return false end
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
return spellName and L["resurrections"][spellName] return spellName and L["resurrections"][spellName]
end end
@@ -192,7 +127,7 @@ end)
-- SPELL_START_SELF: own cast started (heals + rez) -- SPELL_START_SELF: own cast started (heals + rez)
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {} pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime) pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local pendingTarget = nil local pendingTarget = nil
@@ -255,7 +190,7 @@ pfUI.libdebuff_spell_start_self_hooks["libpredict"] = function(spellId, casterGu
-- selfcast (ALT) = player, otherwise = current target -- selfcast (ALT) = player, otherwise = current target
-- Use this to find the correct group to heal -- Use this to find the correct group to heal
local pohTarget = target or player local pohTarget = target or player
if GetNumRaidMembers() > 0 then if IsInRaid() then
-- Raid: find pohTarget's subgroup and heal only those members -- Raid: find pohTarget's subgroup and heal only those members
-- (Turtle WoW changed PoH to heal the target's group, not the caster's group) -- (Turtle WoW changed PoH to heal the target's group, not the caster's group)
local targetGroup local targetGroup
@@ -319,7 +254,7 @@ end
-- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer) -- SPELL_GO_SELF: own cast landed (HealStop + Regrowth timer)
pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId) pfUI.libdebuff_spell_go_hooks["libpredict_sender"] = function(spellId)
libpredict:HealStop(player) libpredict:HealStop(player)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if spellName == REGROWTH then if spellName == REGROWTH then
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
if libpredict.sender.regrowth_timer then if libpredict.sender.regrowth_timer then
@@ -337,7 +272,7 @@ end
-- Signature: fn(spellId, casterGuid, targetGuid, castTime) -- Signature: fn(spellId, casterGuid, targetGuid, castTime)
pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {} pfUI.libdebuff_spell_start_other_hooks = pfUI.libdebuff_spell_start_other_hooks or {}
pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime) pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterGuid, targetGuid, castTime)
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local casterName = resolveNameFromGuid(casterGuid) local casterName = resolveNameFromGuid(casterGuid)
@@ -364,7 +299,7 @@ pfUI.libdebuff_spell_start_other_hooks["libpredict"] = function(spellId, casterG
-- Prayer of Healing: heal entire subgroup of the target -- Prayer of Healing: heal entire subgroup of the target
if spellName == PRAYER_OF_HEALING then if spellName == PRAYER_OF_HEALING then
if GetNumRaidMembers() > 0 then if IsInRaid() then
local targetGroup local targetGroup
for i = 1, GetNumRaidMembers() do for i = 1, GetNumRaidMembers() do
local rname, _, subgroup = GetRaidRosterInfo(i) local rname, _, subgroup = GetRaidRosterInfo(i)
@@ -404,8 +339,13 @@ end
-- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7) -- Signature: fn(spellId, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
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["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7) pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4, a5, a6, a7)
-- Instant HoTs -- Instant HoTs — classify by name (rank-independent) instead of a
local hotType = SPELL_IDS[spellId] -- hardcoded per-rank ID table.
local spellName = C_Spell.GetSpellName(spellId)
local hotType
if spellName == REJUVENATION then hotType = "Reju"
elseif spellName == RENEW then hotType = "Renew"
end
if hotType then if hotType then
local targetGuid = a4 local targetGuid = a4
local targetName = resolveNameFromGuid(targetGuid) local targetName = resolveNameFromGuid(targetGuid)
@@ -426,9 +366,9 @@ pfUI.libdebuff_spell_go_hooks["libpredict"] = function(spellId, a1, a2, a3, a4,
local rankStr = tostring(rank) local rankStr = tostring(rank)
if libpredict.sender and libpredict.sender.SendHealCommMsg then if libpredict.sender and libpredict.sender.SendHealCommMsg then
libpredict.sender:SendHealCommMsg(hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/") libpredict.sender:SendHealCommMsg(hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/")
elseif GetNumRaidMembers() > 0 then elseif IsInRaid() then
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "RAID") SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "RAID")
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "PARTY") SendAddonMessage("HealComm", hotType .. "/" .. targetName .. "/" .. duration .. "/" .. rankStr .. "/", "PARTY")
end end
end end
@@ -1019,8 +959,6 @@ hooksecurefunc("CastSpellByName", function(effect, target)
if not libpredict.sender.enabled then return end if not libpredict.sender.enabled then return end
local effect, rank = libspell.GetSpellInfo(effect) local effect, rank = libspell.GetSpellInfo(effect)
if not effect then return end if not effect then return end
local mouseover = pfUI and pfUI.uf and pfUI.uf.mouseover and pfUI.uf.mouseover.unit
mouseover = mouseover and UnitCanAssist("player", mouseover) and UnitName(mouseover)
local default = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player") local default = UnitName("target") and UnitCanAssist("player", "target") and UnitName("target") or UnitName("player")
@@ -1039,13 +977,13 @@ hooksecurefunc("CastSpellByName", function(effect, target)
if not libpredict.sender.current_cast then if not libpredict.sender.current_cast then
spell_queue[1] = effect spell_queue[1] = effect
spell_queue[2] = effect.. ( rank or "" ) spell_queue[2] = effect.. ( rank or "" )
spell_queue[3] = target or mouseover or default spell_queue[3] = target or default
end end
-- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback -- Instant HoTs: libdebuff/Nampower via GetHotDuration, hook method as fallback
if effect == REJUVENATION then if effect == REJUVENATION then
local hotTarget = target or mouseover or default local hotTarget = target or default
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
local key = "Reju" .. hotTarget local key = "Reju" .. hotTarget
@@ -1062,7 +1000,7 @@ hooksecurefunc("CastSpellByName", function(effect, target)
local rankStr = rankNum and tostring(rankNum) or "0" local rankStr = rankNum and tostring(rankNum) or "0"
libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/") libpredict.sender:SendHealCommMsg("Reju/"..hotTarget.."/"..rejuvDuration.."/"..rankStr.."/")
elseif effect == RENEW then elseif effect == RENEW then
local hotTarget = target or mouseover or default local hotTarget = target or default
local now = pfUI.uf.now or GetTime() local now = pfUI.uf.now or GetTime()
local key = "Renew" .. hotTarget local key = "Renew" .. hotTarget
@@ -1145,10 +1083,10 @@ libpredict.sender = CreateFrame("Frame", "pfPredictionSender", UIParent)
libpredict.sender.enabled = true libpredict.sender.enabled = true
libpredict.sender.SendHealCommMsg = function(self, msg) libpredict.sender.SendHealCommMsg = function(self, msg)
-- Smart channel selection: Only send to relevant channel to avoid duplicates -- Smart channel selection: Only send to relevant channel to avoid duplicates
if GetNumRaidMembers() > 0 then if IsInRaid() then
-- In raid: Only send to RAID (includes all raid members) -- In raid: Only send to RAID (includes all raid members)
SendAddonMessage("HealComm", msg, "RAID") SendAddonMessage("HealComm", msg, "RAID")
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
-- In party: Only send to PARTY -- In party: Only send to PARTY
SendAddonMessage("HealComm", msg, "PARTY") SendAddonMessage("HealComm", msg, "PARTY")
end end
@@ -1157,10 +1095,10 @@ libpredict.sender.SendHealCommMsg = function(self, msg)
end end
libpredict.sender.SendResCommMsg = function(self, msg) libpredict.sender.SendResCommMsg = function(self, msg)
-- Smart channel selection: Only send to relevant channel to avoid duplicates -- Smart channel selection: Only send to relevant channel to avoid duplicates
if GetNumRaidMembers() > 0 then if IsInRaid() then
-- In raid: Only send to RAID (includes all raid members) -- In raid: Only send to RAID (includes all raid members)
SendAddonMessage("CTRA", msg, "RAID") SendAddonMessage("CTRA", msg, "RAID")
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
-- In party: Only send to PARTY -- In party: Only send to PARTY
SendAddonMessage("CTRA", msg, "PARTY") SendAddonMessage("CTRA", msg, "PARTY")
end end
@@ -1236,7 +1174,7 @@ libpredict.sender:SetScript("OnEvent", function()
local amount = arg4 local amount = arg4
local isCrit = arg5 == 1 local isCrit = arg5 == 1
local isPeriodic = arg6 == 1 local isPeriodic = arg6 == 1
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if spellName and spell_queue[1] == spellName then if spellName and spell_queue[1] == spellName then
UpdateCache(spell_queue[2], amount, isCrit) UpdateCache(spell_queue[2], amount, isCrit)
end end
@@ -1256,9 +1194,9 @@ libpredict.sender:SetScript("OnEvent", function()
local casterName = resolveNameFromGuid(casterGuid) local casterName = resolveNameFromGuid(casterGuid)
if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF if not casterName or casterName == player then return end -- own heals handled by SPELL_HEAL_BY_SELF
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") local spellName = C_Spell.GetSpellName(spellId)
if not spellName then return end if not spellName then return end
local rankStr = GetSpellRecField and GetSpellRecField(spellId, "rank") or "" local rankStr = C_Spell.GetSpellSubtext(spellId)
local spellKey = spellName .. (rankStr or "") local spellKey = spellName .. (rankStr or "")
foreignCache[casterName] = foreignCache[casterName] or {} foreignCache[casterName] = foreignCache[casterName] or {}
+2 -5
View File
@@ -2380,11 +2380,7 @@ pfUI:RegisterModule("gui", function ()
if c == "player" then if c == "player" then
CreateConfig(nil, T["Player SP/Haste Display"], nil, nil, "header") CreateConfig(nil, T["Player SP/Haste Display"], nil, nil, "header")
CreateConfig(nil, T["Haste Display"], C.unitframes[c], "display_haste", "dropdown", { CreateConfig(nil, T["Haste Display"], C.unitframes[c], "display_haste", "checkbox")
"0:"..T["None"],
"1:"..T["Haste (cast speed increase)"],
"2:"..T["Effective Haste (Haste * cast time reduction)"], -- Only affects mages/warlocks I believe
})
CreateConfig(nil, T["Haste Display Color"], C.unitframes[c], "display_haste_color", "color") CreateConfig(nil, T["Haste Display Color"], C.unitframes[c], "display_haste_color", "color")
CreateConfig(nil, T["Display Spell Power"], C.unitframes[c], "display_spellpower", "checkbox") CreateConfig(nil, T["Display Spell Power"], C.unitframes[c], "display_spellpower", "checkbox")
CreateConfig(nil, T["Use Custom Spell Power Color (unchecked = biggest school color)"], C.unitframes[c], "display_sp_color_override", "checkbox") CreateConfig(nil, T["Use Custom Spell Power Color (unchecked = biggest school color)"], C.unitframes[c], "display_sp_color_override", "checkbox")
@@ -2749,6 +2745,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Enable Extended Guild Information"], C.tooltip, "extguild", "checkbox") CreateConfig(nil, T["Enable Extended Guild Information"], C.tooltip, "extguild", "checkbox")
CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox") CreateConfig(nil, T["Always Show Health In Percent"], C.tooltip, "alwaysperc", "checkbox")
CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox") CreateConfig(nil, T["Show Item IDs"], C.tooltip, "itemid", "checkbox")
CreateConfig(nil, T["Show Movement Speed"], C.tooltip, "movespeed", "checkbox")
CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha") CreateConfig(nil, T["Custom Transparency"], C.tooltip, "alpha")
CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture) CreateConfig(nil, T["Status Bar Texture"], C.tooltip.statusbar, "texture", "dropdown", pfUI.gui.dropdowns.uf_bartexture)
CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox") CreateConfig(nil, T["Compare Item Base Stats"], C.tooltip.compare, "basestats", "checkbox")
+2 -2
View File
@@ -39,11 +39,11 @@ pfUI:RegisterModule("innervatecall", function ()
return "BATTLEGROUND" return "BATTLEGROUND"
end end
if GetNumRaidMembers() > 0 then if IsInRaid() then
return "RAID" return "RAID"
end end
if GetNumPartyMembers() > 0 then if IsInGroup() then
return "PARTY" return "PARTY"
end end
+1 -1
View File
@@ -323,7 +323,7 @@ pfUI:RegisterModule("loot", function ()
end end
function pfUI.loot:InitGroupDropDown() function pfUI.loot:InitGroupDropDown()
local inRaid = UnitInRaid("player") local inRaid = IsInRaid()
if UIDROPDOWNMENU_MENU_LEVEL == 1 then if UIDROPDOWNMENU_MENU_LEVEL == 1 then
if ( inRaid ) then if ( inRaid ) then
pfUI.loot:BuildRaidMenu(UIDROPDOWNMENU_MENU_LEVEL) pfUI.loot:BuildRaidMenu(UIDROPDOWNMENU_MENU_LEVEL)
+2 -2
View File
@@ -50,7 +50,7 @@ pfUI:RegisterModule("mapcolors", function ()
end end
local function UpdateUnitFrames(unit_button_name) local function UpdateUnitFrames(unit_button_name)
if GetNumRaidMembers() > 0 then if IsInRaid() then
for i=1, MAX_RAID_MEMBERS do for i=1, MAX_RAID_MEMBERS do
local frame_name = unit_button_name.."Raid"..i local frame_name = unit_button_name.."Raid"..i
local frame = _G[frame_name] local frame = _G[frame_name]
@@ -68,7 +68,7 @@ pfUI:RegisterModule("mapcolors", function ()
end end
end end
end end
elseif GetNumPartyMembers() > 0 then elseif IsInGroup() then
for i=1, MAX_PARTY_MEMBERS do for i=1, MAX_PARTY_MEMBERS do
local frame_name = unit_button_name.."Party"..i local frame_name = unit_button_name.."Party"..i
local frame = _G[frame_name] local frame = _G[frame_name]
+9 -75
View File
@@ -1,39 +1,6 @@
pfUI:RegisterModule("mouseover", function () pfUI:RegisterModule("mouseover", function ()
pfUI.uf.mouseover = CreateFrame("Frame", "pfMouseOver", UIParent)
-- Prepare a list of units that can be used via SpellTargetUnit
local st_units = { [1] = "player", [2] = "target", [3] = "mouseover" }
for i=1, MAX_PARTY_MEMBERS do table.insert(st_units, "party"..i) end
for i=1, MAX_RAID_MEMBERS do table.insert(st_units, "raid"..i) end
-- Try to find a valid (friendly) unitstring that can be used for
-- SpellTargetUnit(unit) to avoid another target switch
local function GetUnitString(unit)
for index, unitstr in pairs(st_units) do
if UnitIsUnit(unit, unitstr) then
return unitstr
end
end
return nil
end
-- Same as CastSpellByName but with disabled AutoSelfCast
local function NoSelfCast(spell, onself)
local cvar_selfcast = GetCVar("AutoSelfCast")
if cvar_selfcast ~= "0" then
SetCVar("AutoSelfCast", "0")
pcall(CastSpellByName, spell, onself)
SetCVar("AutoSelfCast", cvar_selfcast)
else
CastSpellByName(spell, onself)
end
end
_G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse" _G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse"
function SlashCmdList.PFCAST(msg) function SlashCmdList.PFCAST(msg)
local restore_target = true
local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg)
local unit = "mouseover" local unit = "mouseover"
@@ -50,51 +17,18 @@ pfUI:RegisterModule("mouseover", function ()
end end
end end
-- Nampower: CastSpellByName supports a second unit parameter directly. -- Spell-name path: Nampower's CastSpellByName takes a second unit
-- unit is already resolved to "mouseover", "target" or "player" at this point. -- parameter directly, no target swap dance required.
if not func and GetNampowerVersion then if not func then
CastSpellByName(msg, unit) CastSpellByName(msg, unit)
return return
end end
-- If target and mouseover are friendly units, we can't use spell target as it -- Macro path: switch target so the macro's spell calls land on `unit`,
-- would cast on the target instead of the mouseover. However, if the mouseover -- then restore.
-- is friendly and the target is not, we can try to obtain the best unitstring local restore_target = not UnitIsUnit("target", unit)
-- for the later SpellTargetUnit() call. if restore_target then TargetUnit(unit) end
local unitstr = not UnitCanAssist("player", "target") and UnitCanAssist("player", unit) and GetUnitString(unit) func()
if restore_target then TargetLastTarget() end
if UnitIsUnit("target", unit) or (not func and unitstr) then
-- no target change required, we can either use spell target
-- or the unit is already our current target.
restore_target = false
else
-- The spelltarget can't be used here, we need to switch
-- and restore the target during spell cast
TargetUnit(unit)
end
if func then
func()
else
-- write temporary unit name
pfUI.uf.mouseover.unit = unit
-- cast without self cast cvar setting
-- to allow spells to use spelltarget
NoSelfCast(msg)
-- set spell target to unitstring (or selfcast)
if SpellIsTargeting() then SpellTargetUnit(unitstr or "player") end
-- clean up spell target in error case
if SpellIsTargeting() then SpellStopTargeting() end
-- remove temporary mouseover unit
pfUI.uf.mouseover.unit = nil
end
if restore_target then
TargetLastTarget()
end
end end
end) end)
+6 -7
View File
@@ -937,10 +937,15 @@ end
-- name — name alone misses pool reuse between same-named units (e.g. plate -- name — name alone misses pool reuse between same-named units (e.g. plate
-- held a player "Ironforge Guard" and is now reassigned to the NPC by the -- held a player "Ironforge Guard" and is now reassigned to the NPC by the
-- same name), which would leak a stale "PLAYER" hint into GetUnitInfo. -- same name), which would leak a stale "PLAYER" hint into GetUnitInfo.
-- Wipe the whole cache table: the PERF gates below ("only update X when
-- X changed") would otherwise skip bar/color/text updates when the new
-- unit happens to share a cached value with the previous occupant
-- (e.g., both at 60% HP percentage on plate pool reuse → bar stays at
-- the old fill until the new mob actually changes HP).
if plate.cache.name ~= name or plate.cache.guid ~= plate.cachedGuid then if plate.cache.name ~= name or plate.cache.guid ~= plate.cachedGuid then
table.wipe(plate.cache)
plate.cache.name = name plate.cache.name = name
plate.cache.guid = plate.cachedGuid plate.cache.guid = plate.cachedGuid
plate.cache.player = nil
plate.cdCache = nil -- new unit, reset spell-keyed timer cache plate.cdCache = nil -- new unit, reset spell-keyed timer cache
plate.name:SetText(GetNameString(name)) plate.name:SetText(GetNameString(name))
end end
@@ -987,12 +992,6 @@ end
-- remove unitstr on unit name mismatch -- remove unitstr on unit name mismatch
if unitstr and UnitName(unitstr) ~= name then unitstr = nil end if unitstr and UnitName(unitstr) ~= name then unitstr = nil end
-- use mobhealth values if addon is running
if (MobHealth3 or MobHealthFrame) and target and name == UnitName('target') and MobHealth_GetTargetCurHP() then
hp = MobHealth_GetTargetCurHP() > 0 and MobHealth_GetTargetCurHP() or hp
hpmax = MobHealth_GetTargetMaxHP() > 0 and MobHealth_GetTargetMaxHP() or hpmax
end
-- always make sure to keep plate visible -- always make sure to keep plate visible
plate:Show() plate:Show()
+16 -49
View File
@@ -38,38 +38,6 @@ pfUI:RegisterModule("player", function ()
playerFrame.myclass = myclass playerFrame.myclass = myclass
playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER" playerFrame.isSpellCaster = myclass ~= "WARRIOR" and myclass ~= "ROGUE" and myclass ~= "HUNTER"
-- Compute class-based casting speed modifier and cache on the frame.
-- This is re-evaluated on LEARNED_SPELL_IN_TAB (with 1s delay) so talent changes are handled.
-- Not sure if there are any other effects that give % cast reduction time
local function UpdatePlayerModCastingTime()
playerFrame.modCastingTime = 1
if myclass == "MAGE" then
local _, _, _, _, acceleratedArcana = GetTalentInfo(1, 16)
if acceleratedArcana and acceleratedArcana > 0 then
playerFrame.modCastingTime = 0.95
end
elseif myclass == "WARLOCK" then
local _, _, _, _, rapidDeter = GetTalentInfo(1, 14)
if rapidDeter and rapidDeter > 0 then
playerFrame.modCastingTime = 1 - (rapidDeter * 0.03)
end
end
end
local talentFrame = CreateFrame("Frame")
talentFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
talentFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
talentFrame:SetScript("OnEvent", function()
-- Delay 1s for both PLAYER_ENTERING_WORLD and LEARNED_SPELL_IN_TAB
local checkAt = GetTime() + 1
talentFrame:SetScript("OnUpdate", function()
if GetTime() >= checkAt then
talentFrame:SetScript("OnUpdate", nil)
UpdatePlayerModCastingTime()
end
end)
end)
-- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset -- Convert "r,g,b,a" config color string to a 6-char hex string, or nil if unset
local function cfgColorToHex(colorStr) local function cfgColorToHex(colorStr)
if not colorStr or colorStr == "" then return nil end if not colorStr or colorStr == "" then return nil end
@@ -98,26 +66,25 @@ pfUI:RegisterModule("player", function ()
if not cfg then if not cfg then
return return
end end
local hasteMode = cfg.display_haste -- "0"=none, "1"=modCastSpeed, "2"=modCastSpeed*modCastingTime -- display_haste: "0"=hidden, "1"=show modCastSpeed (gear haste). Talent-
-- side cast-time reductions show up in the actual cast bar via
-- C_Spell.UnitCastingInfo; double-folding them into this overlay was
-- mixing two different concepts into one number.
local showHaste = cfg.display_haste == "1"
local showSP = cfg.display_spellpower == "1" local showSP = cfg.display_spellpower == "1"
local isSpellCaster = playerFrame.isSpellCaster local isSpellCaster = playerFrame.isSpellCaster
if (hasteMode == "0" or not isSpellCaster) and not showSP then if (not showHaste or not isSpellCaster) and not showSP then
playerFrame.infoTopCenterText:SetText("") playerFrame.infoTopCenterText:SetText("")
return return
end end
local haste = GetUnitField("player", "modCastSpeed") local haste = GetUnitField("player", "modCastSpeed")
local modCastingTime = playerFrame.modCastingTime or 1
local text = "" local text = ""
if isSpellCaster and haste then if showHaste and isSpellCaster and haste then
local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF" local hasteHex = cfgColorToHex(cfg.display_haste_color) or "FFFFFF"
if hasteMode == "1" then text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / haste - 1) * 100)
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / haste - 1) * 100)
elseif hasteMode == "2" then
text = string.format("|cff%s%.1f%%|r", hasteHex, (1 / (haste * modCastingTime) - 1) * 100)
end
end end
if showSP and isSpellCaster then if showSP and isSpellCaster then
@@ -151,21 +118,21 @@ pfUI:RegisterModule("player", function ()
end end
-- Add throttle to player frame OnUpdate -- Add throttle to player frame OnUpdate
-- Throttle the unit frame's existing OnUpdate to ~20 FPS so the per-frame
-- work stays cheap.
if pfUI.uf.player:GetScript("OnUpdate") then if pfUI.uf.player:GetScript("OnUpdate") then
local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate") local originalOnUpdate = pfUI.uf.player:GetScript("OnUpdate")
pfUI.uf.player:SetScript("OnUpdate", function() pfUI.uf.player:SetScript("OnUpdate", function()
if (this.throttleTick or 0) > GetTime() then if (this.throttleTick or 0) > GetTime() then return end
return this.throttleTick = GetTime() + 0.05
end
this.throttleTick = GetTime() + 0.05 -- Default: 20 FPS
originalOnUpdate() originalOnUpdate()
if (this.infoTextTick or 0) <= GetTime() then
this.infoTextTick = GetTime() + 0.25 -- Don't need to update haste/SP text as often
UpdateInfoText()
end
end) end)
end end
-- Haste / spell-power overlay text — refreshes 4×/sec on its own ticker,
-- independent of the unit frame's OnUpdate cadence.
C_Timer.NewTicker(0.25, UpdateInfoText)
-- Replace default's RESET_INSTANCES button with an always working one -- Replace default's RESET_INSTANCES button with an always working one
UnitPopupButtons["RESET_INSTANCES_FIX"] = { text = RESET_INSTANCES, dist = 0 } UnitPopupButtons["RESET_INSTANCES_FIX"] = { text = RESET_INSTANCES, dist = 0 }
for id, text in pairs(UnitPopupMenus["SELF"]) do for id, text in pairs(UnitPopupMenus["SELF"]) do
+1 -1
View File
@@ -106,7 +106,7 @@ pfUI:RegisterModule("raid", function ()
this.pendingUpdate = nil this.pendingUpdate = nil
-- don't proceed without raid -- don't proceed without raid
if not UnitInRaid("player") then return end if not IsInRaid() then return end
-- clear all existing frames -- clear all existing frames
for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end
+7
View File
@@ -23,6 +23,13 @@ SlashCmdList["PFDLLSTATUS"] = function()
chat:AddMessage(" |cffff0000Nampower|r: Not detected") chat:AddMessage(" |cffff0000Nampower|r: Not detected")
end end
-- ClassicAPI
if CLASSIC_API_VERSION then
chat:AddMessage(" |cff00ff00ClassicAPI|r: v" .. CLASSIC_API_VERSION)
else
chat:AddMessage(" |cffff0000ClassicAPI|r: Not detected")
end
-- Check if castbar exists for indicator positioning -- Check if castbar exists for indicator positioning
if pfUI.castbar and pfUI.castbar.player then if pfUI.castbar and pfUI.castbar.player then
chat:AddMessage(" |cff00ff00Castbar|r: Available for indicator anchoring") chat:AddMessage(" |cff00ff00Castbar|r: Available for indicator anchoring")
+101 -89
View File
@@ -10,6 +10,11 @@ pfUI:RegisterModule("swingtimer", function ()
local ON_SWING_QUEUED = 0 local ON_SWING_QUEUED = 0
local ON_SWING_QUEUE_POPPED = 1 local ON_SWING_QUEUE_POPPED = 1
-- Spell.dbc bits used to mirror server-side swing-reset rules.
local FLAG_AUTOATTACK = tonumber("0x08", 16) -- SPELL_INTERRUPT_FLAG_AUTOATTACK
local ATTR_KEEP_SWINGS = tonumber("0x20000", 16) -- SPELL_ATTR_EX2_NOT_RESET_AUTO_ACTIONS
local ATTR_ON_NEXT_SWING = tonumber("0x04", 16) -- SPELL_ATTR_ON_NEXT_SWING
-- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max) -- Consolidate state into a table to avoid Lua 5.0 upvalue limit (32 max)
local S = { local S = {
mhTimer = 0, mhTimerMax = 1, mhTimer = 0, mhTimerMax = 1,
@@ -32,51 +37,26 @@ pfUI:RegisterModule("swingtimer", function ()
onSwingCache = {}, onSwingCache = {},
} }
-- Ranged spell IDs -- Wand "Shoot" runs on the ranged bar but is INDEPENDENT of the melee
local RANGED_SPELLIDS = { -- swing clock — casters melee-weave between mainhand swings and wand
[75] = true, -- Auto Shot (Hunter) -- fires, both timers tick concurrently. Every other ranged-auto-attack
[2764] = true, -- Throw (Warrior/Rogue) -- (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
-- Spells that DELAY the swing timer by their cast duration but do NOT reset it. -- ATTR_ON_NEXT_SWING: spell replaces next auto-attack swing.
-- Slam: vanilla behavior on Turtle WoW - delays swing, does not reset.
-- Hammer of Wrath: Turtle WoW changed behavior - does not reset swing timer.
local swingDelaySpells = {
[1464] = true, [8820] = true, [11604] = true, [11605] = true, -- Slam R1-R4
[24275] = true, [24274] = true, [24239] = true, -- Hammer of Wrath R1-R3
}
-- SPELL_ATTR_ON_NEXT_SWING (bit 2, value 4): spell replaces 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 ATTR_ON_NEXT_SWING = 4
local function IsOnSwingSpell(spellId) local function IsOnSwingSpell(spellId)
if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end if S.onSwingCache[spellId] ~= nil then return S.onSwingCache[spellId] end
local rec = GetSpellRec(spellId) local attr = GetSpellRecField(spellId, "attributes") or 0
local result = rec and bit.band(rec.attributes, ATTR_ON_NEXT_SWING) ~= 0 or false local result = bit.band(attr, ATTR_ON_NEXT_SWING) ~= 0
S.onSwingCache[spellId] = result S.onSwingCache[spellId] = result
return result return result
end end
-- Heroic Strike spell IDs (all ranks) -- HS / Cleave / Maul are detected dynamically via IsOnSwingSpell (the
local hsSpellIDs = { -- SPELL_ATTR_ON_NEXT_SWING bit) + name comparison against the rank-1
[78] = true, [284] = true, [285] = true, [1608] = true, -- canonical names cached below — no per-rank ID maintenance.
[11564] = true, [11565] = true, [11566] = true, [11567] = true,
}
-- Cleave spell IDs (all ranks)
local cleaveSpellIDs = {
[845] = true, [7369] = true, [11608] = true, [11609] = true,
[20569] = true,
}
-- Maul spell IDs (all ranks 1 to 7)
local maulSpellIDs = {
[6807] = true,
[6808] = true,
[6809] = true,
[8972] = true,
[9745] = true, [9880] = true, [9881] = true,
}
-- Read config -- Read config
local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200 local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200
@@ -355,14 +335,17 @@ pfUI:RegisterModule("swingtimer", function ()
pfUI.swingtimer:Show() pfUI.swingtimer:Show()
end end
-- Reset ranged countdown -- Reset ranged countdown. replaceMH=true (Hunter Auto Shot, Throw) stops
local function ResetRanged() -- 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
-- Ranged replaces MH bar if replaceMH then
S.mhActive = false S.mhActive = false
pfUI.swingtimer.mainhand:Hide() pfUI.swingtimer.mainhand:Hide()
end
S.raTimerMax = S.raSpeed S.raTimerMax = S.raSpeed
S.raTimer = S.raSpeed S.raTimer = S.raSpeed
S.raActive = true S.raActive = true
@@ -406,11 +389,35 @@ pfUI:RegisterModule("swingtimer", function ()
pfUI.swingtimer:Hide() pfUI.swingtimer:Hide()
end end
-- HS/Cleave helpers. Canonical rank-1 spellIDs resolve to the localized -- HS/Cleave/Maul helpers. Canonical rank-1 spellIDs resolve to the
-- spell name once, so the per-slot comparison is locale-independent without -- localized spell name once, so per-spell classification is rank- and
-- per-rank hardcoding (every rank of Heroic Strike returns the same name). -- locale-independent without per-rank ID hardcoding (every rank of
local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1) -- Heroic Strike etc. returns the same name).
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1) local HS_NAME = C_Spell.GetSpellName(78) -- Heroic Strike (Rank 1)
local CLEAVE_NAME = C_Spell.GetSpellName(845) -- Cleave (Rank 1)
local MAUL_NAME = C_Spell.GetSpellName(6807) -- Maul (Rank 1)
-- Classify an on-next-swing spell as "hs" / "cleave" / "maul" / nil.
-- IsOnSwingSpell gates the family (ATTR_ON_NEXT_SWING bit 0x04) so
-- unrelated spells return nil cheaply.
local function ClassifyOnSwingSpell(spellId)
if not IsOnSwingSpell(spellId) then return nil end
local name = C_Spell.GetSpellName(spellId)
if name == HS_NAME then return "hs"
elseif name == CLEAVE_NAME then return "cleave"
elseif name == MAUL_NAME then return "maul"
end
end
-- Set the queue flags from a ClassifyOnSwingSpell result. nil = no-op
-- (preserves prior flag state when the queued spell isn't one we
-- color-code, matching the legacy table-lookup behavior).
local function SetQueuedKind(kind)
if not kind then return end
S.hsQueued = (kind == "hs")
S.cleaveQueued = (kind == "cleave")
S.maulQueued = (kind == "maul")
end
local function RebuildQueueSlotCache() local function RebuildQueueSlotCache()
if not S.isWarrior or not sw_hsqueue or S.useSpellQueueEvent then return end if not S.isWarrior or not sw_hsqueue or S.useSpellQueueEvent then return end
@@ -694,11 +701,17 @@ pfUI:RegisterModule("swingtimer", function ()
local spellStartFrame = CreateFrame("Frame") local spellStartFrame = CreateFrame("Frame")
spellStartFrame:RegisterEvent("SPELL_START_SELF") spellStartFrame:RegisterEvent("SPELL_START_SELF")
spellStartFrame:SetScript("OnEvent", function() spellStartFrame:SetScript("OnEvent", function()
if arg1 and arg1 > 0 then if not (arg1 and arg1 > 0) then return end
S.pendingCastSpellId = arg1 S.pendingCastSpellId = arg1
-- Slam (and other delay-only spells): freeze the swing timer during cast -- Freeze the swing timer for cast-time spells that DON'T reset auto-
-- so it pauses instead of ticking down and expiring mid-cast -- attack on completion (Slam, Hammer of Wrath on Turtle, etc.) — those
if swingDelaySpells[arg1] and S.mhActive then -- let the swing resume from where it paused. Detect dynamically via the
-- absent AUTOATTACK interrupt flag (8); spells with that bit reset on
-- SPELL_GO_SELF so freezing isn't necessary. Subsumes the old hardcoded
-- swingDelaySpells list (no list maintenance for new Slam-style spells).
if S.mhActive then
local iflags = GetSpellRecField(arg1, "interruptFlags") or 0
if bit.band(iflags, FLAG_AUTOATTACK) == 0 then
S.mhFrozenAt = GetTime() S.mhFrozenAt = GetTime()
end end
end end
@@ -718,52 +731,57 @@ pfUI:RegisterModule("swingtimer", function ()
-- SPELL_GO hook via libdebuff -- 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)
if RANGED_SPELLIDS[spellId] then -- C_Spell.IsRangedAutoAttackSpell catches both Auto Shot (75) and
ResetRanged() -- 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 return
elseif swingDelaySpells[spellId] then elseif spellId == THROW_SPELLID then
-- Swing-delay spells (Slam, Hammer of Wrath on Turtle WoW): ResetRanged(true)
-- Delay the swing timer by cast duration, do NOT reset it.
if S.mhFrozenAt then
local castDuration = GetTime() - S.mhFrozenAt
S.mhTimer = S.mhTimer + castDuration
S.mhTimerMax = S.mhTimerMax + castDuration
S.mhFrozenAt = nil
end
S.pendingCastSpellId = nil
return return
elseif hsSpellIDs[spellId] or cleaveSpellIDs[spellId] or maulSpellIDs[spellId] or IsOnSwingSpell(spellId) then elseif IsOnSwingSpell(spellId) then
S.hsQueued = false; S.cleaveQueued = false -- On-next-swing ability (HS / Cleave / Maul / Raptor Strike / etc.)
S.maulQueued = false -- — the swing fires as the spell consumes it. Drop the queued color.
ResetMH() S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
elseif cleaveSpellIDs[spellId] then
S.hsQueued = false; S.cleaveQueued = false
ResetMH() ResetMH()
else else
-- Any spell with interruptFlags > 0 resets the swing timer -- Mirror the server rule for "does this spell reset the auto-attack
-- (Moonfire, Faerie Fire, Wrath, Starfire etc. - NOT Insect Swarm which has flags=0) -- swing" (Spell::IsMeleeAttackResetSpell in Turtle's core):
local _rec = GetSpellRec(spellId) -- InterruptFlags has SPELL_INTERRUPT_FLAG_AUTOATTACK (0x08)
if _rec and _rec.interruptFlags and _rec.interruptFlags > 0 then -- AND AttributesEx2 lacks NOT_RESET_AUTO_ACTIONS (0x20000).
-- If neither path resets and we're holding a frozen-swing-during-cast
-- (mhFrozenAt set by SPELL_START_SELF for non-AUTOATTACK spells), this
-- is a Slam-style cast — push the timer forward by the cast duration
-- so the bar resumes from where it paused.
local iflags = GetSpellRecField(spellId, "interruptFlags") or 0
if bit.band(iflags, FLAG_AUTOATTACK) ~= 0
and bit.band(GetSpellRecField(spellId, "attributesEx2") or 0, ATTR_KEEP_SWINGS) == 0 then
if S.mhActive and S.mhSpeed > 0 then if S.mhActive and S.mhSpeed > 0 then
UpdateWeaponSpeeds() UpdateWeaponSpeeds()
S.mhTimerMax = S.mhSpeed S.mhTimerMax = S.mhSpeed
S.mhTimer = S.mhSpeed S.mhTimer = S.mhSpeed
end 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 end
S.pendingCastSpellId = nil S.pendingCastSpellId = nil
end end
-- SPELL_CAST_EVENT hook: HS/Cleave queue tracking -- SPELL_CAST_EVENT hook: HS/Cleave/Maul queue tracking
pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {} pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {}
pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId) pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId)
if hsSpellIDs[spellId] then SetQueuedKind(ClassifyOnSwingSpell(spellId))
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
elseif cleaveSpellIDs[spellId] then
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
elseif maulSpellIDs[spellId] then
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
end
end end
@@ -847,13 +865,7 @@ pfUI:RegisterModule("swingtimer", function ()
local spellId = arg2 or 0 local spellId = arg2 or 0
if eventCode == ON_SWING_QUEUED then if eventCode == ON_SWING_QUEUED then
S.useSpellQueueEvent = true S.useSpellQueueEvent = true
if hsSpellIDs[spellId] then SetQueuedKind(ClassifyOnSwingSpell(spellId))
S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false
elseif cleaveSpellIDs[spellId] then
S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false
elseif maulSpellIDs[spellId] then
S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false
end
elseif eventCode == ON_SWING_QUEUE_POPPED then elseif eventCode == ON_SWING_QUEUE_POPPED then
S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false
end end
+1 -1
View File
@@ -1182,7 +1182,7 @@ pfUI:RegisterModule("thirdparty-vanilla", function()
-- use pfUI frames to draw healComm predictions -- use pfUI frames to draw healComm predictions
local pfHookHealCommSendAddonMessage = HealComm.SendAddonMessage local pfHookHealCommSendAddonMessage = HealComm.SendAddonMessage
function HealComm.SendAddonMessage(this, msg) function HealComm.SendAddonMessage(this, msg)
if not UnitInRaid("player") and GetNumPartyMembers() < 1 then if not IsInGroup() then
libpredict:ParseChatMessage(UnitName("player"), msg, "HealComm") libpredict:ParseChatMessage(UnitName("player"), msg, "HealComm")
end end
pfHookHealCommSendAddonMessage(this, msg) pfHookHealCommSendAddonMessage(this, msg)
+8 -8
View File
@@ -146,14 +146,6 @@ pfUI:RegisterModule("tooltip", function ()
rhp, rhpmax = hp, hpmax rhp, rhpmax = hp, hpmax
elseif pfUI.libhealth and pfUI.libhealth.enabled then elseif pfUI.libhealth and pfUI.libhealth.enabled then
rhp, rhpmax, estimated = pfUI.libhealth:GetUnitHealthByName(this.name, this.level, tonumber(hp), tonumber(hpmax)) rhp, rhpmax, estimated = pfUI.libhealth:GetUnitHealthByName(this.name, this.level, tonumber(hp), tonumber(hpmax))
elseif MobHealthFrame then
local index = (this.name or "") .. ":" .. (this.level or "")
local ppp = MobHealth_PPP(index)
if perc and ppp and ppp > 0 and not UnitIsUnit("mouseover", "pet") then
rhp = round(hp * ppp)
rhpmax = round(100 * ppp)
estimated = true
end
end end
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
@@ -241,6 +233,14 @@ pfUI:RegisterModule("tooltip", function ()
end end
end end
if C.tooltip.movespeed == "1" then
local currentSpeed = GetUnitSpeed(unit)
if currentSpeed and currentSpeed > 0 then
local pct = floor(currentSpeed / 7 * 100 + 0.5)
GameTooltip:AddLine(T["Speed"] .. ": " .. pct .. "%", 0.7, 0.7, 1)
end
end
if hp and hpm then if hp and hpm then
if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end
if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end
+1 -1
View File
@@ -47,7 +47,7 @@ pfUI:RegisterModule("turtle-wow", function ()
-- Check if pfUI is handling group or raid for the current situation -- Check if pfUI is handling group or raid for the current situation
local function pfUIHandlesGroupOrRaid() local function pfUIHandlesGroupOrRaid()
local disabled = C["disabled"] or {} local disabled = C["disabled"] or {}
if GetNumRaidMembers() > 0 then if IsInRaid() then
if not C.unitframes.raid then return false end if not C.unitframes.raid then return false end
-- Hide Turtle frames if: pfUI raid module is active, OR player disabled visibility -- Hide Turtle frames if: pfUI raid module is active, OR player disabled visibility
if disabled["raid"] == "1" then if disabled["raid"] == "1" then
+6 -18
View File
@@ -2,25 +2,13 @@ pfUI:RegisterSkin("Character", function ()
local rawborder, border = GetBorderSize() local rawborder, border = GetBorderSize()
local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel() local bpad = rawborder > 1 and border - GetPerfectPixel() or GetPerfectPixel()
-- Compatibility -- Honor Tab
if PlayerTitleDropDown then -- tbc, wotlk StripTextures(HonorFrame)
-- Character Tab StripTextures(ArenaFrame)
SkinDropDown(PlayerTitleDropDown)
PlayerTitleDropDown:SetPoint("TOP", CharacterLevelText, "BOTTOM", 0, -2)
PlayerTitleDropDownText:SetPoint("LEFT", PlayerTitleDropDown.backdrop, "LEFT", 6, 2)
SkinDropDown(PlayerStatFrameLeftDropDown)
SkinDropDown(PlayerStatFrameRightDropDown)
-- Honor Tab HonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
StripTextures(PVPFrame) CreateBackdrop(HonorFrameProgressBar)
else -- vanilla HonorFrameProgressBar:SetHeight(24)
-- Honor Tab
StripTextures(HonorFrame)
HonorFrameProgressBar:SetStatusBarTexture(pfUI.media["img:bar"])
CreateBackdrop(HonorFrameProgressBar)
HonorFrameProgressBar:SetHeight(24)
end
local magicResTextureCords = { local magicResTextureCords = {
{0.21875, 0.78125, 0.25, 0.3203125}, {0.21875, 0.78125, 0.25, 0.3203125},