Compare commits

..
13 Commits
Author SHA1 Message Date
Brues 8f1b3dc39b Cooldowns: size the numbers from OnSizeChanged, not timer starts
Buff buttons start their timers before they are sized, so their numbers took
the height-0 font and kept it until a timer restarted.
2026-11-02 01:18:59 -06:00
Brues 23d25c4791 Focus target: follow its token, not its GUID
The focus target frame filtered its unit events on the unit's GUID, which
only arrive while nampower fires its raw-GUID events (NP_EnableUnitEventsGuid
on, NP_EnableUnitEventsGuidFiltering off) and SuperWoW watches every unit.
Without them the bars froze until the focus switched targets (#76).

ClassicAPI fires the focus target's unit events as "focustarget" and the
focus's UNIT_TARGET as "focus" whatever nampower does, so the frame follows
those tokens. The swap check keeps its own GUID cache, since eventunit is
no longer a GUID for that frame.
2026-11-02 01:06:17 -06:00
Brues ebb4b056c3 Nameplates: drive every plate from unit events, drop the per-tick loop
The module polled: a central OnUpdate at 100 Hz over every visible plate,
a full OnDataChanged refresh of each plate every 0.5 s, and per-tick reads
of the engine's name/level regions, frame size, mouse state and
SpellIsTargeting. ClassicAPI feeds every unit event to the nameplateN
tokens, so none of that is needed.

Each bound plate now registers its unit events on its own frame against
its token (UNIT_HEALTH/MAXHEALTH, UNIT_AURA_DELTA, UNIT_FLAGS,
UNIT_DYNAMIC_FLAGS, UNIT_FACTION, UNIT_LEVEL, UNIT_CLASSIFICATION_CHANGED,
UNIT_NAME_UPDATE, UNIT_TARGET, the casts) and each handler updates only
what that event can change; the module-level events (target, combat,
level-up, roster, guild, spell targeting) fan out to the plates they
concern. Health comes from UnitHealth on the token instead of the
engine bar's OnValueChanged hook; name, level and difficulty colour from
UnitName/UnitLevel/GetDifficultyColor instead of the blanked engine
regions (UnitLevel's -1 is exactly the engine's skull case, so "??B"
survives). The engine resets the plate's size at every bind, so the
engine frame's size and click routing are re-applied from its OnShow
instead of being checked every tick; the overlay steps aside for
ground-target spells on CURRENT_SPELL_CAST_CHANGED.

The only OnUpdate left is a driver frame, shown while a castbar sweeps or
the target zoom animates and hidden when both sets are empty; the zoom
is time-based now rather than per-tick multiplication. The
nameplates/nameplates_target/nameplates_mass throttle categories no
longer mean anything and are gone from libthrottle and the GUI;
nameplates_castbar stays as the sweep rate of non-target castbars.

Mouseover tracking is gone (the token already names the unit), and
libhealth's "known" return now decides whether absolute health is shown,
so a unit whose health is only known as a percentage no longer prints
"57 | 57%".
2026-11-01 21:40:50 -06:00
Brues b17f5bcd3d Unit frames: only the player's pet wakes them with UNIT_HAPPINESS
Shown frames registered UNIT_HAPPINESS unfiltered, so every hunter pet in view woke them. It is now a unit event for "pet".
2026-11-01 21:08:57 -06:00
Brues e60f9eb410 libpredict: drop the always-running cleanup OnUpdate
The events table it pruned was never read; expired heals already drop out of UnitGetIncomingHeals by their own timeout. Resurrection offers are now checked by a ticker that runs only while an offer is open.
2026-11-01 20:57:55 -06:00
Brues 66171d5354 libpredict: stop the sender's per-frame work
The Regrowth HoT now goes out on a one-shot timer instead of an always-running OnUpdate, and other units' spellcast events are filtered to the group's tokens, the only casters libpredict can resolve; solo, no one else's casts arrive.
2026-11-01 20:54:40 -06:00
Brues 8b239a5532 Castbars: hide idle bars instead of fading them to alpha 0
An idle bar stayed shown at alpha 0, so its OnUpdate ran every frame. The fade-out now ends in Hide, a cast shows the bar, and unlock mode keeps it shown for dragging.
2026-11-01 20:44:24 -06:00
Brues b6d570df97 Unit frames: hidden frames only hear the events that can show them
Every frame registered the same global events whether shown or not. A frame now keeps only the visibility events for the unit it was created for, and registers the display-only events while shown; OnShow does a full refresh to catch up on what it missed.
2026-11-01 20:44:24 -06:00
Brues 698dedcb7a Chat: cut the per-line work of the AddMessage hook
- Save the chat history from the frames when leaving the world, through
  GetMessageInfo, instead of on every line. Lines written back from the
  history carry a mark, so they are saved as they are. History is no longer
  recorded while it is off, keeps 30 lines instead of 29, and whispers are
  dimmed in the whisper colour (gsub's match count was taken as red).
- Skip the CLINK and URL passes on lines that cannot hold them.
- Colour player links in one gsub with a callback instead of a gsub per name.
- Read a chat line that several frames show only once, by its line ID.
- Build the timestamp once per second.
- Lay the tabs out on hover only when their bar comes back, not 10 times a
  second.
- Drop ChatFrame_OnUpdate: it only blinks the flash of the bottom button
  pfUI hides. The lines fade in the engine's update.
2026-11-01 19:59:53 -06:00
AbyssandBrues e609809fff Raid unitframes: add option to reverse group order (#75)
* Add raidreverse default to unitframes config

Defaults to off, so existing layouts are unchanged.

* Raid frames: add option to reverse group order

Read the new raidreverse setting and mirror the column index when placing raid frames, so group 1 sits on the far right and the following groups grow to the left.

* Add "Reverse Group Order" checkbox to raid settings

Placed under "Raid Fill Direction" in the raid frame options.
/pfui > Settings > Unit Frames > Raid > Layout

* Raid frames: rename reverse option to "Grow Right To Left"

The option mirrors columns in both fill directions, so name it for what
it does. Mirror across the columns maxraid can fill instead of the whole
layout, so smaller raids stay next to the anchor. Lay the raid grid out
through SlotToCoord like the pet grid, move the default next to the other
raid layout keys, and add the label to the locale tables.

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-11-01 18:02:31 -06:00
Brues 3651341697 Scroll every chat copy box to the newest line when it opens
The caret at the end scrolled only the box with the focus, and each
visible chat frame's box takes the focus as it opens, so only the last
one shown reached the bottom. Even that one stopped short: 1.12's
OnCursorChanged has no input context, so ScrollCursorIntoView lets the
last line clip.

Each box now measures its scroll box and scrolls it to the end on the
next frame, after the edit box has laid the new text out.
2026-11-01 17:22:06 -06:00
Antoine ChedinandBrues 77be27094a Add Offsets to unit frame buff/debuff (#74)
* Add anchor offset for unitframe buff or defuff

* Fix default values

* Stack a shared-corner aura block past the other block's Y offset, read the offsets without a table per icon

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-11-01 16:39:48 -06:00
Brues 3e50a0b34f Turn on countdownForCooldowns when migrating the old cooldown settings
pfUI drew its own cooldown numbers before; the native countdown shows
them only with the countdownForCooldowns cvar on, and only the first-run
setup set it. Upgraded installs showed no numbers at all, so the
cooldown settings migration turns the cvar on as it removes
appearance.cd.
2026-11-01 00:23:39 -05:00
20 changed files with 1603 additions and 1887 deletions
+9
View File
@@ -460,6 +460,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", "raid", "raidlayout", "8x5") pfUI:UpdateConfig("unitframes", "raid", "raidlayout", "8x5")
pfUI:UpdateConfig("unitframes", "raid", "raidpadding", "3") pfUI:UpdateConfig("unitframes", "raid", "raidpadding", "3")
pfUI:UpdateConfig("unitframes", "raid", "raidfill", "VERTICAL") pfUI:UpdateConfig("unitframes", "raid", "raidfill", "VERTICAL")
pfUI:UpdateConfig("unitframes", "raid", "raidgrowleft", "0")
pfUI:UpdateConfig("unitframes", "raid", "collapse", "0") pfUI:UpdateConfig("unitframes", "raid", "collapse", "0")
pfUI:UpdateConfig("unitframes", "raid", "raidgrouplabel", "0") pfUI:UpdateConfig("unitframes", "raid", "raidgrouplabel", "0")
pfUI:UpdateConfig("unitframes", "raid", "grouplabelxoff", "0") pfUI:UpdateConfig("unitframes", "raid", "grouplabelxoff", "0")
@@ -550,6 +551,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "bufflimit", "32") pfUI:UpdateConfig("unitframes", unit, "bufflimit", "32")
pfUI:UpdateConfig("unitframes", unit, "buffperrow", "8") pfUI:UpdateConfig("unitframes", unit, "buffperrow", "8")
pfUI:UpdateConfig("unitframes", unit, "ownbuffsize", "-1") pfUI:UpdateConfig("unitframes", unit, "ownbuffsize", "-1")
pfUI:UpdateConfig("unitframes", unit, "buffoffx", "0")
pfUI:UpdateConfig("unitframes", unit, "buffoffy", "0")
pfUI:UpdateConfig("unitframes", unit, "buffspacingx", "1") pfUI:UpdateConfig("unitframes", unit, "buffspacingx", "1")
pfUI:UpdateConfig("unitframes", unit, "buffspacingy", "1") pfUI:UpdateConfig("unitframes", unit, "buffspacingy", "1")
pfUI:UpdateConfig("unitframes", unit, "debuffs", "TOPLEFT") pfUI:UpdateConfig("unitframes", unit, "debuffs", "TOPLEFT")
@@ -557,6 +560,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "debufflimit", "32") pfUI:UpdateConfig("unitframes", unit, "debufflimit", "32")
pfUI:UpdateConfig("unitframes", unit, "debuffperrow", "8") pfUI:UpdateConfig("unitframes", unit, "debuffperrow", "8")
pfUI:UpdateConfig("unitframes", unit, "owndebuffsize", "-1") pfUI:UpdateConfig("unitframes", unit, "owndebuffsize", "-1")
pfUI:UpdateConfig("unitframes", unit, "debuffoffx", "0")
pfUI:UpdateConfig("unitframes", unit, "debuffoffy", "0")
pfUI:UpdateConfig("unitframes", unit, "debuffspacingx", "1") pfUI:UpdateConfig("unitframes", unit, "debuffspacingx", "1")
pfUI:UpdateConfig("unitframes", unit, "debuffspacingy", "1") pfUI:UpdateConfig("unitframes", unit, "debuffspacingy", "1")
pfUI:UpdateConfig("unitframes", unit, "invert_healthbar", "0") pfUI:UpdateConfig("unitframes", unit, "invert_healthbar", "0")
@@ -1453,6 +1458,10 @@ function pfUI:MigrateConfig()
cooldown.blizzard.text = cooldown.foreign.text cooldown.blizzard.text = cooldown.foreign.text
end end
-- pfUI drew its own numbers before; the native countdown shows them
-- only with this cvar on, which only the first-run setup sets
SetCVar("countdownForCooldowns", "1")
pfUI_config.appearance.cd = nil pfUI_config.appearance.cd = nil
end end
+12 -11
View File
@@ -58,14 +58,17 @@ function pfUI.api.GetCooldownFontSize(height)
return size return size
end end
-- sizes the numbers to the cooldown's parent; the size follows its height, -- sizes the numbers to the height of the frame the cooldown covers
-- so a timer start calls this again whenever that height changed local function UpdateFont(cooldown, height)
local function UpdateFont(cooldown)
local height = (cooldown:GetParent() or cooldown):GetHeight()
cooldown.pfCountdownHeight = height
cooldown:GetCountdownFontString():SetFont(pfUI.media[C.cooldown.font], pfUI.api.GetCooldownFontSize(height), "OUTLINE") cooldown:GetCountdownFontString():SetFont(pfUI.media[C.cooldown.font], pfUI.api.GetCooldownFontSize(height), "OUTLINE")
end end
-- a pfUI cooldown covers its parent, so it resizes with it; a hidden one is
-- laid out, and this runs, as it shows
local function OnSizeChanged(cooldown, width, height)
UpdateFont(cooldown, height)
end
-- applies the settings of its kind to one of pfUI's cooldowns -- applies the settings of its kind to one of pfUI's cooldowns
local function SetupCooldown(cooldown) local function SetupCooldown(cooldown)
local kind = cooldown.pfCooldownKind local kind = cooldown.pfCooldownKind
@@ -78,7 +81,7 @@ local function SetupCooldown(cooldown)
cooldown:SetReverse(config.reverse == "1") cooldown:SetReverse(config.reverse == "1")
cooldown:SetMinimumCountdownDuration(spell[kind] and tonumber(C.cooldown.threshold) * 1000 or 0) cooldown:SetMinimumCountdownDuration(spell[kind] and tonumber(C.cooldown.threshold) * 1000 or 0)
cooldown:SetCountdownFormatter(GetFormatter()) cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown) UpdateFont(cooldown, cooldown:GetParent():GetHeight())
end end
-- applies the "foreign" settings to another addon's cooldown. They only take -- applies the "foreign" settings to another addon's cooldown. They only take
@@ -100,18 +103,15 @@ local function SetupForeign(cooldown)
cooldown:SetMinimumCountdownDuration(tonumber(C.cooldown.threshold) * 1000) cooldown:SetMinimumCountdownDuration(tonumber(C.cooldown.threshold) * 1000)
cooldown:SetCountdownFormatter(GetFormatter()) cooldown:SetCountdownFormatter(GetFormatter())
UpdateFont(cooldown) UpdateFont(cooldown, (cooldown:GetParent() or cooldown):GetHeight())
end end
-- runs as a pfUI cooldown's timer starts: a timer with a total brings back -- runs as a pfUI cooldown's timer starts: a timer with a total brings back
-- the sweep a time-left-only one took away, and the font follows a changed -- the sweep a time-left-only one took away
-- height
local function StartTimer(cooldown, timeleftOnly) local function StartTimer(cooldown, timeleftOnly)
if cooldown.pfTimeLeftOnly ~= timeleftOnly then if cooldown.pfTimeLeftOnly ~= timeleftOnly then
cooldown.pfTimeLeftOnly = timeleftOnly cooldown.pfTimeLeftOnly = timeleftOnly
SetupCooldown(cooldown) SetupCooldown(cooldown)
elseif cooldown:GetParent():GetHeight() ~= cooldown.pfCountdownHeight then
UpdateFont(cooldown)
end end
end end
@@ -140,6 +140,7 @@ end)
function pfUI.api.CreateCooldown(parent, kind, name) function pfUI.api.CreateCooldown(parent, kind, name)
local cooldown = CreateFrame("Cooldown", name, parent, "CooldownFrameTemplate") local cooldown = CreateFrame("Cooldown", name, parent, "CooldownFrameTemplate")
cooldown.pfCooldownKind = kind cooldown.pfCooldownKind = kind
cooldown:SetScript("OnSizeChanged", OnSizeChanged)
table.insert(cooldowns, cooldown) table.insert(cooldowns, cooldown)
SetupCooldown(cooldown) SetupCooldown(cooldown)
return cooldown return cooldown
+83 -30
View File
@@ -19,11 +19,11 @@ local auraCorners = {
} }
-- Flows the shown icons of an aura block outward from the frame edge, starting -- Flows the shown icons of an aura block outward from the frame edge, starting
-- `offset` away from it (its own row gap when nil): `perrow` icons per row, -- `offset` away from it (its own row gap when nil) and shifted on screen by
-- each row as tall as its largest icon. The first `icons.own` icons are the -- `offx`/`offy`: `perrow` icons per row, each row as tall as its largest icon.
-- player's (C_UnitAuras lists them first) and use `icons.ownsize`. Returns the -- The first `icons.own` icons are the player's (C_UnitAuras lists them first)
-- offset just past the block's last row, where a block sharing the same corner -- and use `icons.ownsize`. Returns the offset just past the block's last
-- continues. -- shifted row, where a block sharing the same corner continues.
local function LayoutAuraBlock(unit, icons, offset) local function LayoutAuraBlock(unit, icons, offset)
local corner = auraCorners[icons.pos] local corner = auraCorners[icons.pos]
local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit local anchor = unit.config.portraitheight ~= "-1" and unit.hp or unit
@@ -42,7 +42,7 @@ local function LayoutAuraBlock(unit, icons, offset)
local size = i <= icons.own and icons.ownsize or icons.size local size = i <= icons.own and icons.ownsize or icons.size
icons[i]:SetSize(size, size) icons[i]:SetSize(size, size)
icons[i]:ClearAllPoints() icons[i]:ClearAllPoints()
icons[i]:SetPoint(corner[1], anchor, icons.pos, corner[2] * x, corner[3] * offset) icons[i]:SetPoint(corner[1], anchor, icons.pos, icons.offx + corner[2] * x, icons.offy + corner[3] * offset)
x = x + size + icons.gapx x = x + size + icons.gapx
if size > rowsize then rowsize = size end if size > rowsize then rowsize = size end
@@ -50,7 +50,7 @@ local function LayoutAuraBlock(unit, icons, offset)
i = i + 1 i = i + 1
end end
if col > 0 then offset = offset + rowsize + icons.gapy end if col > 0 then offset = offset + rowsize + icons.gapy + corner[3] * icons.offy end
return offset return offset
end end
@@ -248,6 +248,48 @@ local VISIBILITY_EVENTS = {
UNIT_TARGET = true, -- a link of a chained frame's unit retargeted UNIT_TARGET = true, -- a link of a chained frame's unit retargeted
} }
-- The units of a chain that unit events name by token, whatever nampower does
-- with its GUID events: the focus, and the focus's target, whose unit events
-- ClassicAPI fires as "focustarget" from the unit's own fields.
local CHAIN_TOKENS = { focus = true, focustarget = true }
-- The events a frame hears hidden or not: the visibility events that concern
-- the unit it was created for (raid.lua remaps a raid frame's label, so this is
-- decided once, at creation), and the logout guard. UNIT_TARGET belongs to
-- RegisterChainEvents, and focus.lua drives the focus frames itself.
local function GetIdleEvents(label, fname)
local events = { PLAYER_ENTERING_WORLD = true, PLAYER_LOGOUT = true }
if label:find("^party") or label:find("^raid")
or fname == "Group0" or fname == "PartyPet0" or fname == "Party0Target" then
events.PARTY_MEMBERS_CHANGED = true
events.RAID_ROSTER_UPDATE = true
end
if label:find("^target") then events.PLAYER_TARGET_CHANGED = true end
if label:find("pet") then events.UNIT_PET = true end
return events
end
-- The events that only redraw what a frame shows. A hidden frame drops them,
-- and OnShow's full refresh catches up on whatever it missed.
local SHOWN_EVENTS = {
"PLAYER_AURAS_CHANGED", -- label=player && frame=buff
"PLAYER_EQUIPMENT_CHANGED", -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs)
"PARTY_MEMBERS_CHANGED", -- frame=leaderIcon
"PARTY_LEADER_CHANGED", -- frame=leaderIcon
"RAID_ROSTER_UPDATE", -- frame=raidIcon
"PLAYER_UPDATE_RESTING", -- frame=restIcon
"PARTY_LOOT_METHOD_CHANGED", -- frame=lootIcon
"RAID_TARGET_UPDATE", -- frame=raidIcon
}
local function RegisterShownEvents(frame)
for i = 1, table.getn(SHOWN_EVENTS) do
frame:RegisterEvent(SHOWN_EVENTS[i])
end
-- label=pet; the player's own pet only, not every pet in view
frame:RegisterUnitEvent("UNIT_HAPPINESS", "pet")
end
-- ============================================================================ -- ============================================================================
-- GetUnitStats - health + power for a unit token. -- GetUnitStats - health + power for a unit token.
-- Returns: hp, maxHp, power, maxPower, powerType -- Returns: hp, maxHp, power, maxPower, powerType
@@ -445,7 +487,9 @@ function pfUI.uf:UpdateVisibility()
-- --
-- A chained unit -- another unit's target, "targettarget" or "party1target" -- A chained unit -- another unit's target, "targettarget" or "party1target"
-- -- has no token that unit events name, so the frame follows it by GUID, -- -- has no token that unit events name, so the frame follows it by GUID,
-- which every unit event also fires with. It follows the unit only while -- which nampower fires every unit event with too, unless its GUID events
-- are off or filtered. The focus's target is named (CHAIN_TOKENS), so that
-- frame follows its token instead. It follows the unit only while
-- the client has it: a link's target can be out of sight, and its coming -- the client has it: a link's target can be out of sight, and its coming
-- back is then a new unit too. The unit changes with no event of its own -- back is then a new unit too. The unit changes with no event of its own
-- whenever a link of the chain retargets; UNIT_TARGET on every link reports -- whenever a link of the chain retargets; UNIT_TARGET on every link reports
@@ -454,8 +498,9 @@ function pfUI.uf:UpdateVisibility()
self.chain = visibility ~= "hide" and unitstr:match("^(.+)target$") or nil self.chain = visibility ~= "hide" and unitstr:match("^(.+)target$") or nil
if self.chain then if self.chain then
local guid = UnitName(unitstr) and UnitGUID(unitstr) local guid = UnitName(unitstr) and UnitGUID(unitstr)
if guid ~= self.eventunit then self.update_full = true end if guid ~= self.unitguid then self.update_full = true end
self:RegisterUnitEvents(guid) self.unitguid = guid
self:RegisterUnitEvents(guid and CHAIN_TOKENS[unitstr] and unitstr or guid)
else else
self:RegisterUnitEvents(visibility ~= "hide" and unitstr or nil) self:RegisterUnitEvents(visibility ~= "hide" and unitstr or nil)
end end
@@ -877,6 +922,8 @@ function pfUI.uf:UpdateConfig()
f.buffs.size = tonumber(f.config.buffsize) f.buffs.size = tonumber(f.config.buffsize)
f.buffs.ownsize = f.config.ownbuffsize == "-1" and f.buffs.size or tonumber(f.config.ownbuffsize) f.buffs.ownsize = f.config.ownbuffsize == "-1" and f.buffs.size or tonumber(f.config.ownbuffsize)
f.buffs.perrow = tonumber(f.config.buffperrow) f.buffs.perrow = tonumber(f.config.buffperrow)
f.buffs.offx = tonumber(f.config.buffoffx)
f.buffs.offy = tonumber(f.config.buffoffy)
f.buffs.gapx = aura_border + tonumber(f.config.buffspacingx) f.buffs.gapx = aura_border + tonumber(f.config.buffspacingx)
f.buffs.gapy = aura_border + tonumber(f.config.buffspacingy) f.buffs.gapy = aura_border + tonumber(f.config.buffspacingy)
f.buffs.own = 0 f.buffs.own = 0
@@ -927,6 +974,8 @@ function pfUI.uf:UpdateConfig()
f.debuffs.size = tonumber(f.config.debuffsize) f.debuffs.size = tonumber(f.config.debuffsize)
f.debuffs.ownsize = f.config.owndebuffsize == "-1" and f.debuffs.size or tonumber(f.config.owndebuffsize) f.debuffs.ownsize = f.config.owndebuffsize == "-1" and f.debuffs.size or tonumber(f.config.owndebuffsize)
f.debuffs.perrow = tonumber(f.config.debuffperrow) f.debuffs.perrow = tonumber(f.config.debuffperrow)
f.debuffs.offx = tonumber(f.config.debuffoffx)
f.debuffs.offy = tonumber(f.config.debuffoffy)
f.debuffs.gapx = aura_border + tonumber(f.config.debuffspacingx) f.debuffs.gapx = aura_border + tonumber(f.config.debuffspacingx)
f.debuffs.gapy = aura_border + tonumber(f.config.debuffspacingy) f.debuffs.gapy = aura_border + tonumber(f.config.debuffspacingy)
f.debuffs.own = 0 f.debuffs.own = 0
@@ -980,8 +1029,17 @@ function pfUI.uf:UpdateConfig()
end end
function pfUI.uf.OnShow() function pfUI.uf.OnShow()
pfUI.uf:RefreshUnit(this, "portrait") RegisterShownEvents(this)
pfUI.uf:RefreshUnit(this, "base") pfUI.uf:RefreshUnit(this, "all")
end
function pfUI.uf.OnHide()
for i = 1, table.getn(SHOWN_EVENTS) do
if not this.idleevents[SHOWN_EVENTS[i]] then
this:UnregisterEvent(SHOWN_EVENTS[i])
end
end
this:UnregisterEvent("UNIT_HAPPINESS")
end end
function pfUI.uf.OnEvent() function pfUI.uf.OnEvent()
@@ -1309,8 +1367,8 @@ local UNIT_EVENTS = {
-- Auras and power. ClassicAPI's UNIT_AURA_DELTA also reports refreshes and new -- Auras and power. ClassicAPI's UNIT_AURA_DELTA also reports refreshes and new
-- durations and says what changed; its generic power events name the power, so -- durations and says what changed; its generic power events name the power, so
-- a frame skips the ones it does not draw. Both fire for tokens only, so a -- a frame skips the ones it does not draw. Both fire for tokens only, and not
-- chained unit, followed by GUID, keeps 1.12's events instead. A group -- as "focustarget", so a chained unit keeps 1.12's events instead. A group
-- member's connection and sight come with them, and a chained unit has -- member's connection and sight come with them, and a chained unit has
-- neither event. -- neither event.
local TOKEN_EVENTS = { local TOKEN_EVENTS = {
@@ -1375,16 +1433,18 @@ end
-- unit, whose `owner` is the unit it is the target of -- for -- unit, whose `owner` is the unit it is the target of -- for
-- "targettargettarget" the links are "targettarget" and "target" -- or drop it -- "targettargettarget" the links are "targettarget" and "target" -- or drop it
-- when `owner` is nil. By GUID, since the inner links are no token that events -- when `owner` is nil. By GUID, since the inner links are no token that events
-- name. Cheap to call repeatedly: it re-registers only when a link changed. -- name, save the ones in CHAIN_TOKENS. Cheap to call repeatedly: it
-- re-registers only when a link changed.
local chainlinks = {} local chainlinks = {}
function pfUI.uf:RegisterChainEvents(owner) function pfUI.uf:RegisterChainEvents(owner)
local n, key = 0, nil local n, key = 0, nil
while owner do while owner do
local guid = UnitGUID(owner) local guid = UnitGUID(owner)
if guid then if guid then
local link = CHAIN_TOKENS[owner] and owner or guid
n = n + 1 n = n + 1
chainlinks[n] = guid chainlinks[n] = link
key = key and key .. guid or guid key = key and key .. link or link
end end
owner = owner:match("^(.+)target$") owner = owner:match("^(.+)target$")
end end
@@ -1403,19 +1463,10 @@ end
function pfUI.uf:EnableEvents() function pfUI.uf:EnableEvents()
local f = self local f = self
f:RegisterEvent("PLAYER_ENTERING_WORLD") for event in pairs(f.idleevents) do
f:RegisterEvent("PLAYER_LOGOUT") f:RegisterEvent(event)
f:RegisterEvent("PLAYER_AURAS_CHANGED") -- label=player && frame=buff end
f:RegisterEvent("PLAYER_EQUIPMENT_CHANGED") -- label=player && frame=buff (ClassicAPI: weapon-enchant buffs) if f:IsShown() then RegisterShownEvents(f) end
f:RegisterEvent("PARTY_MEMBERS_CHANGED") -- label=party, frame=leaderIcon
f:RegisterEvent("PARTY_LEADER_CHANGED") -- frame=leaderIcon
f:RegisterEvent("RAID_ROSTER_UPDATE") -- label=raidIcon
f:RegisterEvent("PLAYER_UPDATE_RESTING") -- label=restIcon
f:RegisterEvent("PLAYER_TARGET_CHANGED") -- label=target
f:RegisterEvent("PARTY_LOOT_METHOD_CHANGED") -- frame=lootIcon
f:RegisterEvent("RAID_TARGET_UPDATE") -- frame=raidIcon
f:RegisterEvent("UNIT_PET")
f:RegisterEvent("UNIT_HAPPINESS")
f:RegisterForClicks('LeftButtonUp', 'RightButtonUp', f:RegisterForClicks('LeftButtonUp', 'RightButtonUp',
'MiddleButtonUp', 'Button4Up', 'Button5Up') 'MiddleButtonUp', 'Button4Up', 'Button5Up')
@@ -1481,6 +1532,7 @@ function pfUI.uf:EnableScripts()
end end
f:SetScript("OnShow", pfUI.uf.OnShow) f:SetScript("OnShow", pfUI.uf.OnShow)
f:SetScript("OnHide", pfUI.uf.OnHide)
f:SetScript("OnEvent", pfUI.uf.OnEvent) f:SetScript("OnEvent", pfUI.uf.OnEvent)
f:SetScript("OnUpdate", pfUI.uf.OnUpdate) f:SetScript("OnUpdate", pfUI.uf.OnUpdate)
@@ -1528,6 +1580,7 @@ function pfUI.uf:CreateUnitFrame(unit, id, config, tick)
f.id = id f.id = id
f.config = config or pfUI_config.unitframes.fallback f.config = config or pfUI_config.unitframes.fallback
f.tick = tick f.tick = tick
f.idleevents = GetIdleEvents(f.label, fname)
CreateBackdropShadow(f) CreateBackdropShadow(f)
+1 -2
View File
@@ -359,6 +359,7 @@ pfUI_translation["deDE"] = {
["Group Options"] = nil, ["Group Options"] = nil,
["Group-Pet"] = nil, ["Group-Pet"] = nil,
["Group-Target"] = nil, ["Group-Target"] = nil,
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = nil, ["Guild Online"] = nil,
@@ -908,8 +909,6 @@ pfUI_translation["deDE"] = {
["Panel Alignment"] = "Panel Ausrichtung", ["Panel Alignment"] = "Panel Ausrichtung",
["Nameplate Update Rate"] = "Namensplaketten Aktualisierungsrate", ["Nameplate Update Rate"] = "Namensplaketten Aktualisierungsrate",
["Target/Casting Plates"] = "Ziel/Cast Plaketten", ["Target/Casting Plates"] = "Ziel/Cast Plaketten",
["Normal Plates"] = "Normale Plaketten",
["Mass Pulls (20+ Plates)"] = "Große Pulls (20+ Plaketten)",
["Tooltip Update Rate"] = "Tooltip Aktualisierungsrate", ["Tooltip Update Rate"] = "Tooltip Aktualisierungsrate",
["Cursor Follow"] = "Cursor Folgen", ["Cursor Follow"] = "Cursor Folgen",
["Health/Status Bar"] = "Leben/Status Leiste", ["Health/Status Bar"] = "Leben/Status Leiste",
+5 -2
View File
@@ -90,6 +90,8 @@ pfUI_translation["enUS"] = {
["Buffs"] = nil, ["Buffs"] = nil,
["Buff Size"] = nil, ["Buff Size"] = nil,
["Buff Spacing"] = nil, ["Buff Spacing"] = nil,
["Buff X-Offset"] = nil,
["Buff Y-Offset"] = nil,
["Buff X-Spacing"] = nil, ["Buff X-Spacing"] = nil,
["Buff Y-Spacing"] = nil, ["Buff Y-Spacing"] = nil,
["Buffs Per Row"] = nil, ["Buffs Per Row"] = nil,
@@ -196,6 +198,8 @@ pfUI_translation["enUS"] = {
["Debuff Position"] = nil, ["Debuff Position"] = nil,
["Debuffs"] = nil, ["Debuffs"] = nil,
["Debuff Size"] = nil, ["Debuff Size"] = nil,
["Debuff X-Offset"] = nil,
["Debuff Y-Offset"] = nil,
["Debuff X-Spacing"] = nil, ["Debuff X-Spacing"] = nil,
["Debuff Y-Spacing"] = nil, ["Debuff Y-Spacing"] = nil,
["Debuffs Per Row"] = nil, ["Debuffs Per Row"] = nil,
@@ -369,6 +373,7 @@ pfUI_translation["enUS"] = {
["Group Options"] = nil, ["Group Options"] = nil,
["Group-Pet"] = nil, ["Group-Pet"] = nil,
["Group-Target"] = nil, ["Group-Target"] = nil,
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = nil, ["Guild Online"] = nil,
@@ -936,8 +941,6 @@ pfUI_translation["enUS"] = {
["Panel Alignment"] = nil, ["Panel Alignment"] = nil,
["Nameplate Update Rate"] = nil, ["Nameplate Update Rate"] = nil,
["Target/Casting Plates"] = nil, ["Target/Casting Plates"] = nil,
["Normal Plates"] = nil,
["Mass Pulls (20+ Plates)"] = nil,
["Tooltip Update Rate"] = nil, ["Tooltip Update Rate"] = nil,
["Cursor Follow"] = nil, ["Cursor Follow"] = nil,
["Health/Status Bar"] = nil, ["Health/Status Bar"] = nil,
+1
View File
@@ -359,6 +359,7 @@ pfUI_translation["esES"] = {
["Group Options"] = "Opciones de grupo", ["Group Options"] = "Opciones de grupo",
["Group-Pet"] = "Mascota-Grupo", ["Group-Pet"] = "Mascota-Grupo",
["Group-Target"] = "Objetivo-Grupo", ["Group-Target"] = "Objetivo-Grupo",
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = "Conectados en hermandad", ["Guild Online"] = "Conectados en hermandad",
+1
View File
@@ -359,6 +359,7 @@ pfUI_translation["frFR"] = {
["Group Options"] = "Options de groupe", ["Group Options"] = "Options de groupe",
["Group-Pet"] = "Groupe des familiers", ["Group-Pet"] = "Groupe des familiers",
["Group-Target"] = "Groupe des cibles", ["Group-Target"] = "Groupe des cibles",
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = "Membres de guilde connectés", ["Guild Online"] = "Membres de guilde connectés",
+1
View File
@@ -359,6 +359,7 @@ pfUI_translation["koKR"] = {
["Group Options"] = nil, ["Group Options"] = nil,
["Group-Pet"] = "파티-팻", ["Group-Pet"] = "파티-팻",
["Group-Target"] = "파티-타켓", ["Group-Target"] = "파티-타켓",
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = "접속중인 길드원", ["Guild Online"] = "접속중인 길드원",
+1
View File
@@ -359,6 +359,7 @@ pfUI_translation["ruRU"] = {
["Group Options"] = "Настройки группы", ["Group Options"] = "Настройки группы",
["Group-Pet"] = "Группа-питомец", ["Group-Pet"] = "Группа-питомец",
["Group-Target"] = "Группа-цель", ["Group-Target"] = "Группа-цель",
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = "Члены гильдии в сети", ["Guild Online"] = "Члены гильдии в сети",
+1
View File
@@ -359,6 +359,7 @@ pfUI_translation["zhCN"] = {
["Group Options"] = "队伍选项", ["Group Options"] = "队伍选项",
["Group-Pet"] = "小队宠物", ["Group-Pet"] = "小队宠物",
["Group-Target"] = "小队目标", ["Group-Target"] = "小队目标",
["Grow Right To Left"] = nil,
["Gryphon"] = "狮鹫", ["Gryphon"] = "狮鹫",
["Gryphons"] = "狮鹫(多个)", ["Gryphons"] = "狮鹫(多个)",
["Guild Online"] = "公会在线", ["Guild Online"] = "公会在线",
+1
View File
@@ -359,6 +359,7 @@ pfUI_translation["zhTW"] = {
["Group Options"] = nil, ["Group Options"] = nil,
["Group-Pet"] = "小隊寵物", ["Group-Pet"] = "小隊寵物",
["Group-Target"] = "小隊目標", ["Group-Target"] = "小隊目標",
["Grow Right To Left"] = nil,
["Gryphon"] = nil, ["Gryphon"] = nil,
["Gryphons"] = nil, ["Gryphons"] = nil,
["Guild Online"] = "公會線上", ["Guild Online"] = "公會線上",
+54 -86
View File
@@ -25,7 +25,7 @@ setfenv(1, pfUI:GetEnvironment())
-- return instantly when another libpredict is already active -- return instantly when another libpredict is already active
if pfUI.api.libpredict then return end if pfUI.api.libpredict then return end
local heals, events, hots = {}, {}, {} local heals, hots = {}, {}
local spell_queue = { "DUMMY", "DUMMYRank 9", "TARGET" } local spell_queue = { "DUMMY", "DUMMYRank 9", "TARGET" }
local player = UnitName("player") local player = UnitName("player")
local cache, gear_string = {}, "" local cache, gear_string = {}, ""
@@ -134,19 +134,6 @@ local function OnOwnCastStart(spellId, castGUID)
local amount = cache[spell_queue[2]][1] local amount = cache[spell_queue[2]][1]
local casttime = castTime local casttime = castTime
if spellName == REGROWTH then
local fullSpell = spell_queue[2]
local _, _, rankStr = fullSpell and string.find(fullSpell, "Rank (%d+)")
local rankNum = rankStr and tonumber(rankStr) or nil
if libpredict.sender.regrowth_timer then
libpredict.sender.regrowth_target_next = target
libpredict.sender.regrowth_rank_next = rankNum
else
libpredict.sender.regrowth_target = target
libpredict.sender.regrowth_rank = rankNum
end
end
if spellName == PRAYER_OF_HEALING then if spellName == PRAYER_OF_HEALING then
-- target is already correctly set from spell_queue[3]: -- target is already correctly set from spell_queue[3]:
-- selfcast (ALT) = player, otherwise = current target -- selfcast (ALT) = player, otherwise = current target
@@ -207,34 +194,35 @@ local function OnOwnCastSucceeded(spellId, castGUID)
libpredict:HealStop(player) libpredict:HealStop(player)
local spellName = C_Spell.GetSpellName(spellId) local spellName = C_Spell.GetSpellName(spellId)
if spellName == REGROWTH then
local now = pfUI.uf.now or GetTime()
if libpredict.sender.regrowth_timer then
libpredict.sender.regrowth_start_next = now
else
libpredict.sender.regrowth_start = now
end
libpredict.sender.regrowth_timer = now + 0.1
end
libpredict.sender.castGUID = nil libpredict.sender.castGUID = nil
libpredict.sender.current_cast = nil libpredict.sender.current_cast = nil
libpredict.sender.current_cast_target = nil libpredict.sender.current_cast_target = nil
if not target then return end if not target then return end
-- Instant HoTs — classify by name (rank-independent) instead of a -- HoTs — classify by name (rank-independent) instead of a hardcoded
-- hardcoded per-rank ID table. -- per-rank ID table.
local hotType local hotType
if spellName == REJUVENATION then hotType = "Reju" if spellName == REJUVENATION then hotType = "Reju"
elseif spellName == RENEW then hotType = "Renew" elseif spellName == RENEW then hotType = "Renew"
elseif spellName == REGROWTH then hotType = "Regr"
end end
if hotType then if hotType then
local duration = hotType == "Reju" and rejuvDuration or renewDuration
local rank = 0 local rank = 0
local rankSub = C_Spell.GetSpellSubtext(spellId) local rankSub = C_Spell.GetSpellSubtext(spellId)
if rankSub and rankSub ~= "" then if rankSub and rankSub ~= "" then
rank = tonumber((string.gsub(rankSub, "Rank ", ""))) or 0 rank = tonumber((string.gsub(rankSub, "Rank ", ""))) or 0
end end
if hotType == "Regr" then
-- Regrowth's HoT follows its direct heal a moment later
local start = GetTime()
C_Timer.After(0.1, function()
libpredict:Hot(player, target, hotType, 20, start, "UNIT_SPELLCAST_SUCCEEDED", rank)
libpredict.sender:SendHealCommMsg(hotType .. "/" .. target .. "/20/" .. rank .. "/")
end)
return
end
local duration = hotType == "Reju" and rejuvDuration or renewDuration
libpredict:Hot(player, target, hotType, duration, nil, "UNIT_SPELLCAST_SUCCEEDED", rank) libpredict:Hot(player, target, hotType, duration, nil, "UNIT_SPELLCAST_SUCCEEDED", rank)
libpredict.sender:SendHealCommMsg(hotType .. "/" .. target .. "/" .. duration .. "/" .. rank .. "/") libpredict.sender:SendHealCommMsg(hotType .. "/" .. target .. "/" .. duration .. "/" .. rank .. "/")
end end
@@ -326,20 +314,11 @@ local function RefreshResFrames(guid)
end end
end end
libpredict:SetScript("OnUpdate", function() -- Drop resurrection offers the target accepted or let run out. Nothing reports
-- throttle cleanup - no need to check every frame -- either, so a ticker checks while any offer is open.
local now = pfUI.uf.now or GetTime() local resTicker
if (this.tick or 0) > now then return end local function CheckResOffers()
this.tick = now + pfUI.throttle:Get("libpredict") -- Default: Normal (10 FPS) local now = GetTime()
-- update on timeout events
for timestamp, targets in pairs(events) do
if now >= timestamp then
events[timestamp] = nil
end
end
-- drop resurrection offers the target accepted or let run out
for guid, expiry in pairs(resOffers) do for guid, expiry in pairs(resOffers) do
local unit = UnitTokenFromGUID(guid) local unit = UnitTokenFromGUID(guid)
if now >= expiry or not (unit and UnitIsDeadOrGhost(unit)) then if now >= expiry or not (unit and UnitIsDeadOrGhost(unit)) then
@@ -347,7 +326,11 @@ libpredict:SetScript("OnUpdate", function()
RefreshResFrames(guid) RefreshResFrames(guid)
end end
end end
end) if not next(resOffers) then
resTicker:Cancel()
resTicker = nil
end
end
function libpredict:ParseComm(sender, msg) function libpredict:ParseComm(sender, msg)
local msgtype, target, heal, time, rank local msgtype, target, heal, time, rank
@@ -496,11 +479,6 @@ function libpredict:ParseChatMessage(sender, msg)
end end
end end
function libpredict:AddEvent(time, target)
events[time] = events[time] or {}
table.insert(events[time], target)
end
function libpredict:Heal(sender, target, amount, duration, senderGuid) function libpredict:Heal(sender, target, amount, duration, senderGuid)
if not sender or not target or not amount or not duration then if not sender or not target or not amount or not duration then
return return
@@ -511,7 +489,6 @@ function libpredict:Heal(sender, target, amount, duration, senderGuid)
heals[target] = heals[target] or {} heals[target] = heals[target] or {}
heals[target][sender] = { amount, timeout } heals[target][sender] = { amount, timeout }
if senderGuid then healGuidToName[senderGuid] = sender end if senderGuid then healGuidToName[senderGuid] = sender end
libpredict:AddEvent(timeout, target)
end end
-- Debug flag -- Debug flag
@@ -596,7 +573,6 @@ function libpredict:HealDelay(sender, delay)
for tsender, amount in pairs(heals[target]) do for tsender, amount in pairs(heals[target]) do
if sender == tsender then if sender == tsender then
amount[2] = amount[2] + delay amount[2] = amount[2] + delay
libpredict:AddEvent(amount[2], target)
end end
end end
end end
@@ -932,39 +908,33 @@ libpredict.sender.SendResCommMsg = function(self, msg)
-- BG groups are handled by RAID channel -- BG groups are handled by RAID channel
end end
libpredict.sender:SetScript("OnUpdate", function() -- Own casts arrive as "player". Other casters only matter as group members
-- trigger delayed regrowth timers -- (the only names resolveNameFromGuid knows), so their events are filtered to
local now = pfUI.uf.now or GetTime() -- the group's tokens, and solo nobody else's casts arrive at all. A member in
if this.regrowth_timer and now > this.regrowth_timer then -- a raid also fires as raidN for its own casts; the handler drops those by
local target = this.regrowth_target or player -- UnitIsUnit, and other repeats by castGUID.
local duration = 20 local GROUP_CAST_EVENTS = {
local startTime = this.regrowth_start "UNIT_SPELLCAST_START", "UNIT_SPELLCAST_SUCCEEDED", "UNIT_SPELLCAST_INTERRUPTED",
local rank = this.regrowth_rank }
local castunits = {}
libpredict:Hot(player, target, "Regr", duration, startTime, "OnUpdate", rank) local function RegisterCastEvents()
local rankStr = rank and tostring(rank) or "0" local n, token = 0, IsInRaid() and "raid" or "party"
libpredict.sender:SendHealCommMsg("Regr/"..target.."/"..duration.."/"..rankStr.."/") castunits[1] = "player"
for i = 1, IsInRaid() and GetNumRaidMembers() or GetNumPartyMembers() do
-- Apply next queued Regrowth if available castunits[i + 1] = token .. i
this.regrowth_target = this.regrowth_target_next n = i
this.regrowth_start = this.regrowth_start_next
this.regrowth_rank = this.regrowth_rank_next
this.regrowth_target_next = nil
this.regrowth_start_next = nil
this.regrowth_rank_next = nil
this.regrowth_timer = nil
end end
end) for i = table.getn(castunits), n + 2, -1 do castunits[i] = nil end
for i = 1, table.getn(GROUP_CAST_EVENTS) do
-- Own casts arrive as "player"; every other caster fires once per unit token libpredict.sender:RegisterUnitEvent(GROUP_CAST_EVENTS[i], unpack(castunits))
-- it currently holds (raidN, target, nameplateN, ...), so other-unit handlers end
-- drop the repeats by castGUID. end
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SENT") RegisterCastEvents()
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_START") libpredict.sender:RegisterUnitEvent("UNIT_SPELLCAST_SENT", "player")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_DELAYED") libpredict.sender:RegisterUnitEvent("UNIT_SPELLCAST_DELAYED", "player")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED") libpredict.sender:RegisterUnitEvent("UNIT_SPELLCAST_FAILED", "player")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_FAILED") libpredict.sender:RegisterEvent("PARTY_MEMBERS_CHANGED")
libpredict.sender:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED") libpredict.sender:RegisterEvent("RAID_ROSTER_UPDATE")
libpredict.sender:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED") libpredict.sender:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
-- Shared cleanup helper for failed/interrupted casts -- Shared cleanup helper for failed/interrupted casts
@@ -980,12 +950,6 @@ local function onCastFailed()
s:SendResCommMsg("RESNO " .. (target or "")) s:SendResCommMsg("RESNO " .. (target or ""))
s.resurrecting = nil s.resurrecting = nil
end end
if s.current_cast == REGROWTH then
s.regrowth_timer = nil
s.regrowth_start = nil
s.regrowth_target_next = nil
s.regrowth_start_next = nil
end
s.castGUID = nil s.castGUID = nil
s.current_cast = nil s.current_cast = nil
s.current_cast_target = nil s.current_cast_target = nil
@@ -1032,6 +996,7 @@ local function OnResurrect()
-- arg8=destGUID -- arg8=destGUID
resOffers[arg8] = GetTime() + RES_OFFER_TIMEOUT resOffers[arg8] = GetTime() + RES_OFFER_TIMEOUT
RefreshResFrames(arg8) RefreshResFrames(arg8)
resTicker = resTicker or C_Timer.NewTicker(.1, CheckResOffers)
end end
local lastOtherCast = {} -- [event] = castGUID of the last other-unit event handled local lastOtherCast = {} -- [event] = castGUID of the last other-unit event handled
@@ -1041,6 +1006,9 @@ libpredict.sender:SetScript("OnEvent", function()
if arg2 == "SPELL_HEAL" then OnHeal() if arg2 == "SPELL_HEAL" then OnHeal()
elseif arg2 == "SPELL_RESURRECT" then OnResurrect() end elseif arg2 == "SPELL_RESURRECT" then OnResurrect() end
return return
elseif event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" then
RegisterCastEvents()
return
end end
-- UNIT_SPELLCAST_SENT: arg1=unit, arg2=target token, arg3=castGUID, arg4=spellID -- UNIT_SPELLCAST_SENT: arg1=unit, arg2=target token, arg3=castGUID, arg4=spellID
-6
View File
@@ -31,10 +31,7 @@ libthrottle.presetNames = {
-- Default throttle categories -- Default throttle categories
libthrottle.defaults = { libthrottle.defaults = {
nameplates = "custom",
nameplates_target = "custom",
nameplates_castbar = "custom", nameplates_castbar = "custom",
nameplates_mass = "custom",
tooltip_cursor = "fastest", tooltip_cursor = "fastest",
chat_tab = "custom", chat_tab = "custom",
swingtimer = "custom", swingtimer = "custom",
@@ -158,10 +155,7 @@ libthrottle:SetScript("OnEvent", function()
end end
-- Set defaults for custom fields if missing -- Set defaults for custom fields if missing
if not _G.pfUI_throttle.nameplates_target_custom then _G.pfUI_throttle.nameplates_target_custom = "50" end
if not _G.pfUI_throttle.nameplates_custom then _G.pfUI_throttle.nameplates_custom = "10" end
if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end if not _G.pfUI_throttle.nameplates_castbar_custom then _G.pfUI_throttle.nameplates_castbar_custom = "100" end
if not _G.pfUI_throttle.nameplates_mass_custom then _G.pfUI_throttle.nameplates_mass_custom = "7" end
if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end if not _G.pfUI_throttle.tooltip_cursor_custom then _G.pfUI_throttle.tooltip_cursor_custom = "10" end
if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end if not _G.pfUI_throttle.chat_tab_custom then _G.pfUI_throttle.chat_tab_custom = "10" end
if not _G.pfUI_throttle.swingtimer_custom then _G.pfUI_throttle.swingtimer_custom = "50" end if not _G.pfUI_throttle.swingtimer_custom then _G.pfUI_throttle.swingtimer_custom = "50" end
+11 -8
View File
@@ -36,7 +36,7 @@ pfUI:RegisterModule("castbar", function ()
cb.bar:SetValue(1) cb.bar:SetValue(1)
end end
if cb.bar.spark then cb.bar.spark:Hide() end if cb.bar.spark then cb.bar.spark:Hide() end
if cb:GetAlpha() > 0 and not cb.fade:IsPlaying() then cb.fade:Play() end if cb:IsShown() and not cb.fade:IsPlaying() then cb.fade:Play() end
end end
-- "Spellname (N)" label for an active tradeskill merge — N is the number -- "Spellname (N)" label for an active tradeskill merge — N is the number
@@ -95,7 +95,7 @@ pfUI:RegisterModule("castbar", function ()
cb.isTradeskill = isTradeskill cb.isTradeskill = isTradeskill
cb.delay = (delayMs or 0) / 1000 cb.delay = (delayMs or 0) / 1000
cb.fade:Stop() cb.fade:Stop()
cb:SetAlpha(1) cb:Show()
-- Shield an uninterruptible cast. Not on the player's own bar: the flag is -- 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 -- measured against the reader's own interrupts and silences, so on your own
@@ -247,12 +247,16 @@ pfUI:RegisterModule("castbar", function ()
cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0) cb.bar.spark:SetPoint("BOTTOM", cb.bar, "BOTTOMLEFT", 0, 0)
cb.bar.spark:Hide() cb.bar.spark:Hide()
-- Fade-out after a cast ends. The bar stays at alpha 0 while idle. -- Fade-out after a cast ends. The bar is hidden while idle, so its OnUpdate
-- only runs during a cast; unlock mode keeps it shown for dragging.
local function unlocked() return cb.drag and cb.drag:IsShown() end
cb.fade = cb:CreateAnimationGroup() cb.fade = cb:CreateAnimationGroup()
local fade = cb.fade:CreateAnimation("Alpha") local fade = cb.fade:CreateAnimation("Alpha")
fade:SetChange(-1) fade:SetChange(-1)
fade:SetDuration(.4) fade:SetDuration(.4)
cb.fade:SetScript("OnFinished", function() cb:SetAlpha(0) end) cb.fade:SetScript("OnFinished", function()
if not unlocked() then cb:Hide() end
end)
-- OnUpdate animates the bar fill. All state transitions (cast start/stop/interrupt, channel start/stop, -- OnUpdate animates the bar fill. All state transitions (cast start/stop/interrupt, channel start/stop,
-- pushback) come from the event handler below — we never poll C_Spell -- pushback) come from the event handler below — we never poll C_Spell
@@ -261,16 +265,15 @@ pfUI:RegisterModule("castbar", function ()
if (this.tick or 0) > GetTime() then return end if (this.tick or 0) > GetTime() then return end
this.tick = GetTime() + 0.020 -- ~50 FPS this.tick = GetTime() + 0.020 -- ~50 FPS
if this.drag and this.drag:IsShown() then if unlocked() then
this.fade:Stop() this.fade:Stop()
this:SetAlpha(1)
return return
end end
if this.fade:IsPlaying() then return end if this.fade:IsPlaying() then return end
if not this.endTime then if not this.endTime then
if this:GetAlpha() ~= 0 then this:SetAlpha(0) end this:Hide()
return return
end end
@@ -443,7 +446,7 @@ pfUI:RegisterModule("castbar", function ()
end end
end) end)
cb:SetAlpha(0) cb:Hide()
return cb return cb
end end
+150 -109
View File
@@ -30,39 +30,6 @@ pfUI:RegisterModule("chat", function ()
end end
end) end)
local realm = GetRealmName()
local player = UnitName("player")
local history
local function SaveChatHistory(id, msg, r, g, b)
-- create cache tables if not existing
pfUI_cache = pfUI_cache or {}
pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {}
pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {}
pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {}
pfUI_cache["chathistory"][realm][player][id] = pfUI_cache["chathistory"][realm][player][id] or {}
if r and g and b then
local color = rgbhex(r*.5+.2, g*.5+.2, b*.5+.2)
msg = string.gsub(msg, "^", color)
msg = string.gsub(msg, "|r", "|r" .. color)
end
history = pfUI_cache["chathistory"][realm][player][id]
table.insert(history, 1, msg)
if history[30] then table.remove(history, 30) end
end
local function GetChatHistory(id)
-- create cache tables if not existing
pfUI_cache = pfUI_cache or {}
pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {}
pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {}
pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {}
pfUI_cache["chathistory"][realm][player][id] = pfUI_cache["chathistory"][realm][player][id] or {}
return pfUI_cache["chathistory"][realm][player][id]
end
-- [ Chat Panel Colors ] -- [ Chat Panel Colors ]
-- The panels normally inherit pfUI's global appearance theme; custom colors let chat -- The panels normally inherit pfUI's global appearance theme; custom colors let chat
-- deviate from it. CreateBackdrop is re-run first to restore the theme, so toggling -- deviate from it. CreateBackdrop is re-run first to restore the theme, so toggling
@@ -456,15 +423,20 @@ pfUI:RegisterModule("chat", function ()
if pfUI.chat.hideLock then return end if pfUI.chat.hideLock then return end
-- the dock lays the tabs out again only when their bar comes back
if pfUI.chat.left:IsMouseOver(10, -10, -10, 10) then if pfUI.chat.left:IsMouseOver(10, -10, -10, 10) then
pfUI.chat.left.panelTop:Show() if not pfUI.chat.left.panelTop:IsShown() then
FCF_DockUpdate() pfUI.chat.left.panelTop:Show()
FCF_DockUpdate()
end
elseif pfUI.chat.right:IsMouseOver(10, -10, -10, 10) then elseif pfUI.chat.right:IsMouseOver(10, -10, -10, 10) then
-- disable while dock is active -- disable while dock is active
if pfUI.chat.right:GetAlpha() == 0 then return end if pfUI.chat.right:GetAlpha() == 0 then return end
pfUI.chat.right.panelTop:Show() if not pfUI.chat.right.panelTop:IsShown() then
FCF_DockUpdate() pfUI.chat.right.panelTop:Show()
FCF_DockUpdate()
end
else else
pfUI.chat.left.panelTop:Hide() pfUI.chat.left.panelTop:Hide()
pfUI.chat.right.panelTop:Hide() pfUI.chat.right.panelTop:Hide()
@@ -587,6 +559,9 @@ pfUI:RegisterModule("chat", function ()
end end
for i=1, NUM_CHAT_WINDOWS do for i=1, NUM_CHAT_WINDOWS do
-- ChatFrame_OnUpdate only blinks the flash of the bottom button hidden
-- below; the lines fade in the engine's own update
_G["ChatFrame" .. i]:SetScript("OnUpdate", nil)
_G["ChatFrame" .. i .. "UpButton"]:Hide() _G["ChatFrame" .. i .. "UpButton"]:Hide()
_G["ChatFrame" .. i .. "UpButton"].Show = function() return end _G["ChatFrame" .. i .. "UpButton"].Show = function() return end
_G["ChatFrame" .. i .. "DownButton"]:Hide() _G["ChatFrame" .. i .. "DownButton"]:Hide()
@@ -732,6 +707,62 @@ pfUI:RegisterModule("chat", function ()
return pfUI_playerDB[name].level return pfUI_playerDB[name].level
end end
-- the sender of the line AddMessage is reading, from its chat event
local lineSender, lineSenderClass, lineSenderRace, lineSenderSex
-- [ FormatPlayerLink ]
-- The gsub callback for a player link: the name in its class colour, with
-- the level and icons the settings ask for. Returns the link unchanged when
-- there is nothing to add, as a Lua 5.0 callback that returns nil deletes
-- the match.
local function FormatPlayerLink(link, name, shown)
local real = strsplit(":", name)
if shown ~= real then return link end
local color, icon = "", ""
if C.chat.text.classcolor == "1" then
local _, class, raceKey, sex
if lineSenderClass and real == lineSender then
class, raceKey, sex = lineSenderClass, lineSenderRace, lineSenderSex
else
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
end
if not class then
class = GetUnitInfo(real)
end
if class and class ~= UNKNOWN then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
if C.chat.text.factionicons == "1" then
icon = GetFactionIcon(raceKey)
end
if C.chat.text.raceicons == "1" then
icon = icon .. GetRaceIcon(raceKey, sex)
end
elseif C.chat.text.tintunknown == "1" then
color = unknowncolorhex
end
end
local suffix = ""
local level = C.chat.text.playerlevel == "1" and GetPlayerLevel(real)
if level and level > 0 then
suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
elseif level then
-- Show ?? for unknown levels (e.g. -1 from UnitLevel)
suffix = " |cffff0000??|r"
end
if color == "" and suffix == "" then return link end
return left .. icon .. color .. "|Hplayer:" .. name .. "|h" .. color .. real .. "|h|r" .. suffix .. right
end
-- the timestamp of the current second
local stampTime, stamp
-- the last chat line read and what it became. A line shown in several
-- frames is read once: nothing in the result depends on the frame.
local lastLineID, lastText, lastResult
-- Passes every argument after the text on: GetMessageInfo returns them all, -- Passes every argument after the text on: GetMessageInfo returns them all,
-- and a hook that drops some loses them for every frame after it. -- and a hook that drops some loses them for every frame after it.
local function AddMessage(frame, text, ...) local function AddMessage(frame, text, ...)
@@ -744,76 +775,40 @@ pfUI:RegisterModule("chat", function ()
-- payload of the CHAT_MSG event printing this line; nil for lines -- payload of the CHAT_MSG event printing this line; nil for lines
-- that don't come from one (system text, addon output, history) -- that don't come from one (system text, addon output, history)
local event, _, sender, _, _, _, _, _, _, _, _, _, guid = GetCurrentChatEvent() local event, _, sender, _, _, _, _, _, _, _, _, lineID, guid = GetCurrentChatEvent()
-- Remove prat CLINKs -- text emotes all have line 0
text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- tbc if lineID and lineID ~= 0 and lineID == lastLineID and text == lastText then
text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- vanilla return frame:HookAddMessage(lastResult, ...)
end
local input = text
-- Remove chatter CLINKs -- Remove prat and chatter CLINKs
text = gsub(text, "{CLINK:item:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") if text:find("{CLINK:", 1, true) then
text = gsub(text, "{CLINK:enchant:(%x+):([%d-]-):([^}]-)}", "|c%1|Henchant:%2|h[%3]|h|r") text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- tbc
text = gsub(text, "{CLINK:spell:(%x+):([%d-]-):([^}]-)}", "|c%1|Hspell:%2|h[%3]|h|r") text = gsub(text, "{CLINK:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r") -- vanilla
text = gsub(text, "{CLINK:quest:(%x+):([%d-]-):([%d-]-):([^}]-)}", "|c%1|Hquest:%2:%3|h[%4]|h|r") text = gsub(text, "{CLINK:item:(%x+):([%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-:[%d-]-):([^}]-)}", "|c%1|Hitem:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:enchant:(%x+):([%d-]-):([^}]-)}", "|c%1|Henchant:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:spell:(%x+):([%d-]-):([^}]-)}", "|c%1|Hspell:%2|h[%3]|h|r")
text = gsub(text, "{CLINK:quest:(%x+):([%d-]-):([%d-]-):([^}]-)}", "|c%1|Hquest:%2:%3|h[%4]|h|r")
end
-- detect urls -- detect urls; every pattern needs a dot between name characters or a "://"
if C.chat.text.detecturl == "1" then if C.chat.text.detecturl == "1" and (text:find("[%w_-]%.[%w_-]") or text:find("://", 1, true)) then
text = pfUI.chat:HandleLink(text) text = pfUI.chat:HandleLink(text)
end end
-- display class colors and player levels if already indexed -- display class colors and player levels if already indexed
local classcolor = C.chat.text.classcolor == "1" local classcolor = C.chat.text.classcolor == "1"
local playerlevel = C.chat.text.playerlevel == "1" if classcolor or C.chat.text.playerlevel == "1" then
if classcolor or playerlevel then
-- the engine name-queries a chat sender before the event fires, -- the engine name-queries a chat sender before the event fires,
-- so the sender's GUID always resolves; other names in the text -- so the sender's GUID always resolves; other names in the text
-- fall back to the name-keyed lookups -- fall back to the name-keyed lookups
local _, senderClass, senderRace, senderSex lineSender, lineSenderClass, lineSenderRace, lineSenderSex = sender, nil, nil, nil
if classcolor and guid and guid ~= "" then if classcolor and guid and guid ~= "" then
_, senderClass, _, senderRace, senderSex = GetPlayerInfoByGUID(guid) _, lineSenderClass, _, lineSenderRace, lineSenderSex = GetPlayerInfoByGUID(guid)
end
for name in gfind(text, "|Hplayer:(.-)|h") do
local real, _ = strsplit(":", name)
local color, icon = "", ""
if classcolor then
local class, raceKey, sex
if senderClass and real == sender then
class, raceKey, sex = senderClass, senderRace, senderSex
else
_, class, _, raceKey, sex = C_PlayerCache.GetPlayerInfoByName(real)
end
if not class then
class = GetUnitInfo(real)
end
if class and class ~= UNKNOWN then
color = "|c" .. PFUI_CLASS_COLORS[class].colorStr
if C.chat.text.factionicons == "1" then
icon = GetFactionIcon(raceKey)
end
if C.chat.text.raceicons == "1" then
icon = icon .. GetRaceIcon(raceKey, sex)
end
elseif C.chat.text.tintunknown == "1" then
color = unknowncolorhex
end
end
local suffix = ""
local level = playerlevel and GetPlayerLevel(real)
if level and level > 0 then
suffix = " " .. rgbhex(GetDifficultyColor(level)) .. level .. "|r"
elseif level then
-- Show ?? for unknown levels (e.g. -1 from UnitLevel)
suffix = " |cffff0000??|r"
end
if color ~= "" or suffix ~= "" then
text = string.gsub(text, "|Hplayer:"..name.."|h%["..real.."%]|h(.-:-)",
left..icon..color.."|Hplayer:"..name.."|h" .. color .. real .. "|h|r"..suffix..right.."%1")
end
end end
text = text:gsub("(|Hplayer:([^|]*)|h%[([^%]]*)%]|h)", FormatPlayerLink)
end end
-- reduce channel name to number -- reduce channel name to number
@@ -823,9 +818,7 @@ pfUI:RegisterModule("chat", function ()
-- detect the whisper prefix BEFORE prepending a timestamp; the timestamp -- detect the whisper prefix BEFORE prepending a timestamp; the timestamp
-- pushes the whisper colour code off position 1 and the find below fails -- pushes the whisper colour code off position 1 and the find below fails
local isWhisper = C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 if C.chat.global.whispermod == "1" and string.find(text, wcol, 1) == 1 then
if isWhisper then
-- patch incoming whisper string to match the colors, before the -- patch incoming whisper string to match the colors, before the
-- timestamp so it keeps the form StripTimestamp looks for -- timestamp so it keeps the form StripTimestamp looks for
text = string.gsub(text, "|r", "|r" .. wcol) text = string.gsub(text, "|r", "|r" .. wcol)
@@ -833,19 +826,66 @@ pfUI:RegisterModule("chat", function ()
-- show timestamp in chat -- show timestamp in chat
if C.chat.text.time == "1" then if C.chat.text.time == "1" then
text = timestampStart .. date(C.chat.text.timeformat) .. timestampEnd .. text local now = time()
end if now ~= stampTime then
stampTime, stamp = now, timestampStart .. date(C.chat.text.timeformat) .. timestampEnd
-- save chat history end
if isWhisper then text = stamp .. text
SaveChatHistory(frame:GetID(), string.gsub(text, wcol, ""), cr, cg, cb)
else
SaveChatHistory(frame:GetID(), text, ...)
end end
lastLineID, lastText, lastResult = lineID, input, text
frame:HookAddMessage(text, ...) frame:HookAddMessage(text, ...)
end end
-- [ Chat History ]
-- The newest lines of each chat frame, kept per character and written back
-- at login, newest first and dimmed. They are read from the frames when
-- leaving the world, which logout and reload both do before the saved
-- variables are written.
local HISTORY_LINES = 30
local function GetChatHistory()
local realm, player = GetRealmName(), UnitName("player")
pfUI_cache["chathistory"] = pfUI_cache["chathistory"] or {}
pfUI_cache["chathistory"][realm] = pfUI_cache["chathistory"][realm] or {}
pfUI_cache["chathistory"][realm][player] = pfUI_cache["chathistory"][realm][player] or {}
return pfUI_cache["chathistory"][realm][player]
end
-- a line dimmed in its colour, which also comes back after every |r in it
local function DimHistoryLine(text, r, g, b)
local color = rgbhex(r*.5+.2, g*.5+.2, b*.5+.2)
return color .. text:gsub("|r", "|r" .. color)
end
local function SaveChatHistory()
local history = GetChatHistory()
for i=1, NUM_CHAT_WINDOWS do
local frame = _G["ChatFrame"..i]
if not frame.pfCombatLog then
local lines = {}
local num = frame:GetNumMessages()
for index = num, math.max(1, num - HISTORY_LINES + 1), -1 do
local text, r, g, b, _, restored = frame:GetMessageInfo(index)
-- lines written back from the history are dimmed already
if not restored then
-- an incoming whisper is in the whisper colour, not its own
if C.chat.global.whispermod == "1" and pfUI.chat.StripTimestamp(text):sub(1, #wcol) == wcol then
text, r, g, b = text:gsub(wcol, ""), cr, cg, cb
end
text = DimHistoryLine(text, r, g, b)
end
table.insert(lines, text)
end
history[i] = lines
end
end
end
if C.chat.text.history == "1" then
pfUI.events:RegisterFrameEventAndCallback("PLAYER_LEAVING_WORLD", SaveChatHistory)
end
for i=1,NUM_CHAT_WINDOWS do for i=1,NUM_CHAT_WINDOWS do
if C.chat.global.maxlines ~= "128" then if C.chat.global.maxlines ~= "128" then
_G["ChatFrame"..i]:SetMaxLines(tonumber(C.chat.global.maxlines) or 128) _G["ChatFrame"..i]:SetMaxLines(tonumber(C.chat.global.maxlines) or 128)
@@ -853,13 +893,14 @@ pfUI:RegisterModule("chat", function ()
if not _G["ChatFrame"..i].HookAddMessage then if not _G["ChatFrame"..i].HookAddMessage then
if C.chat.text.history == "1" then if C.chat.text.history == "1" then
-- write history to chat, newest first, above the lines already there -- write history to chat, newest first, above the lines already there;
for _, msg in ipairs(GetChatHistory(i)) do -- after the colour come the id slot and the mark SaveChatHistory reads
_G["ChatFrame"..i]:BackFillMessage(msg, .7,.7,.7) for _, msg in ipairs(GetChatHistory()[i] or {}) do
_G["ChatFrame"..i]:BackFillMessage(msg, .7,.7,.7, nil, true)
end end
end end
-- add chat parse and history hooks -- add chat parse hooks
_G["ChatFrame"..i].HookAddMessage = _G["ChatFrame"..i].AddMessage _G["ChatFrame"..i].HookAddMessage = _G["ChatFrame"..i].AddMessage
_G["ChatFrame"..i].AddMessage = AddMessage _G["ChatFrame"..i].AddMessage = AddMessage
end end
+8 -2
View File
@@ -85,10 +85,16 @@ pfUI:RegisterModule("chatcopy", function ()
scroll:SetFont(frame:GetFont()) scroll:SetFont(frame:GetFont())
scroll:SetText(text) scroll:SetText(text)
-- the cursor at the end scrolls the box to the newest line
scroll:SetFocus() scroll:SetFocus()
scroll:SetCursorPosition(#text) scroll:SetCursorPosition(#text)
-- the edit box lays the text out in this frame's update, so scroll to
-- the newest line after it; the caret only scrolls the focused box
RunNextFrame(function()
local scrollBox = scroll:GetScrollBox()
scrollBox:FullUpdate(ScrollBoxConstants.UpdateImmediately)
scrollBox:ScrollToEnd(ScrollBoxConstants.NoScrollInterpolation)
end)
end) end)
frame.scroll = scroll frame.scroll = scroll
+4 -3
View File
@@ -17,9 +17,10 @@ pfUI:RegisterModule("focus", function ()
-- PLAYER_FOCUS_CHANGED drives immediate refresh on focus assign / clear. -- PLAYER_FOCUS_CHANGED drives immediate refresh on focus assign / clear.
-- Between events, ClassicAPI fires UNIT_* (health/mana/aura/...) with -- Between events, ClassicAPI fires UNIT_* (health/mana/aura/...) with
-- arg1 == "focus", so the bars update event-driven like target. -- arg1 == "focus", so the bars update event-driven like target.
-- focustarget is a chained unit like targettarget: UNIT_TARGET follows the -- focustarget is a chained unit like targettarget, but ClassicAPI fires its
-- focus retargeting, and a new focus only needs its visibility re-checked, -- UNIT_* as "focustarget", and UNIT_TARGET on "focus" follows the focus
-- which queues the full refresh when its target is a different unit. -- retargeting. A new focus only needs its visibility re-checked, which
-- queues the full refresh when its target is a different unit.
pfUI.events:RegisterFrameEventAndCallback("PLAYER_FOCUS_CHANGED", function() pfUI.events:RegisterFrameEventAndCallback("PLAYER_FOCUS_CHANGED", function()
pfUI.uf:RefreshUnit(pfUI.uf.focus, "all") pfUI.uf:RefreshUnit(pfUI.uf.focus, "all")
pfUI.uf.focustarget:UpdateVisibility() pfUI.uf.focustarget:UpdateVisibility()
+18 -163
View File
@@ -1670,115 +1670,6 @@ pfUI:RegisterModule("gui", function ()
header:GetParent().objectCount = header:GetParent().objectCount - 1 header:GetParent().objectCount = header:GetParent().objectCount - 1
header:SetHeight(20) header:SetHeight(20)
local targetCustom -- declare first so callback can use it
CreateConfig(function()
-- Callback when dropdown changes - update custom field immediately
if targetCustom and targetCustom.input then
local isCustom = pfUI.throttle:IsCustom("nameplates_target")
if not isCustom then
-- Preset selected - show FPS from preset, make readonly
targetCustom.input:EnableMouse(false)
targetCustom.input:EnableKeyboard(false)
targetCustom.input:ClearFocus()
targetCustom.input:SetTextColor(.5,.5,.5,1)
targetCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_target")))
else
-- Custom selected - make editable
targetCustom.input:EnableMouse(true)
targetCustom.input:EnableKeyboard(true)
targetCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_target_custom then
targetCustom.input:SetText(_G.pfUI_throttle.nameplates_target_custom)
end
end
end
end, T["Target Plates"], _G.pfUI_throttle, "nameplates_target", "dropdown", {
"very_slow:" .. T["Very Slow"] .. " (2 FPS)",
"slow:" .. T["Slow"] .. " (5 FPS)",
"normal:" .. T["Normal"] .. " (10 FPS)",
"fast:" .. T["Fast"] .. " (20 FPS)",
"very_fast:" .. T["Very Fast"] .. " (30 FPS)",
"fastest:" .. T["Fastest"] .. " (50 FPS)",
"custom:" .. T["Custom"],
})
-- Now create custom field AFTER dropdown
targetCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_target_custom")
-- Set initial state
local isCustom = pfUI.throttle:IsCustom("nameplates_target")
if not isCustom then
targetCustom.input:EnableMouse(false)
targetCustom.input:EnableKeyboard(false)
targetCustom.input:SetTextColor(.5,.5,.5,1)
targetCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_target")))
else
targetCustom.input:EnableMouse(true)
targetCustom.input:EnableKeyboard(true)
targetCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_target_custom then
targetCustom.input:SetText(_G.pfUI_throttle.nameplates_target_custom)
end
end
-- Spacer after custom field
local spacer1 = CreateConfig(nil, " ", nil, nil, "header")
spacer1:GetParent().objectCount = spacer1:GetParent().objectCount - 1
spacer1:SetHeight(5)
local normalCustom
CreateConfig(function()
if normalCustom and normalCustom.input then
local isCustom = pfUI.throttle:IsCustom("nameplates")
if not isCustom then
normalCustom.input:EnableMouse(false)
normalCustom.input:EnableKeyboard(false)
normalCustom.input:ClearFocus()
normalCustom.input:SetTextColor(.5,.5,.5,1)
normalCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates")))
else
normalCustom.input:EnableMouse(true)
normalCustom.input:EnableKeyboard(true)
normalCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_custom then
normalCustom.input:SetText(_G.pfUI_throttle.nameplates_custom)
end
end
end
end, T["Normal Plates"], _G.pfUI_throttle, "nameplates", "dropdown", {
"very_slow:" .. T["Very Slow"] .. " (2 FPS)",
"slow:" .. T["Slow"] .. " (5 FPS)",
"normal:" .. T["Normal"] .. " (10 FPS)",
"fast:" .. T["Fast"] .. " (20 FPS)",
"very_fast:" .. T["Very Fast"] .. " (30 FPS)",
"fastest:" .. T["Fastest"] .. " (50 FPS)",
"custom:" .. T["Custom"],
})
normalCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_custom")
local isCustom2 = pfUI.throttle:IsCustom("nameplates")
if not isCustom2 then
normalCustom.input:EnableMouse(false)
normalCustom.input:EnableKeyboard(false)
normalCustom.input:SetTextColor(.5,.5,.5,1)
normalCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates")))
else
normalCustom.input:EnableMouse(true)
normalCustom.input:EnableKeyboard(true)
normalCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_custom then
normalCustom.input:SetText(_G.pfUI_throttle.nameplates_custom)
end
end
-- Spacer after custom field
local spacer2 = CreateConfig(nil, " ", nil, nil, "header")
spacer2:GetParent().objectCount = spacer2:GetParent().objectCount - 1
spacer2:SetHeight(5)
local castbarCustom local castbarCustom
CreateConfig(function() CreateConfig(function()
@@ -1811,54 +1702,18 @@ pfUI:RegisterModule("gui", function ()
castbarCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_castbar_custom") castbarCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_castbar_custom")
local spacer3 = CreateConfig(nil, " ", nil, nil, "header") local isCustom = pfUI.throttle:IsCustom("nameplates_castbar")
spacer3:GetParent().objectCount = spacer3:GetParent().objectCount - 1 if not isCustom then
spacer3:SetHeight(5) castbarCustom.input:EnableMouse(false)
castbarCustom.input:EnableKeyboard(false)
local massCustom castbarCustom.input:SetTextColor(.5,.5,.5,1)
castbarCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_castbar")))
CreateConfig(function()
if massCustom and massCustom.input then
local isCustom = pfUI.throttle:IsCustom("nameplates_mass")
if not isCustom then
massCustom.input:EnableMouse(false)
massCustom.input:EnableKeyboard(false)
massCustom.input:ClearFocus()
massCustom.input:SetTextColor(.5,.5,.5,1)
massCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_mass")))
else
massCustom.input:EnableMouse(true)
massCustom.input:EnableKeyboard(true)
massCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_mass_custom then
massCustom.input:SetText(_G.pfUI_throttle.nameplates_mass_custom)
end
end
end
end, T["Mass Pulls (20+ Plates)"], _G.pfUI_throttle, "nameplates_mass", "dropdown", {
"very_slow:" .. T["Very Slow"] .. " (2 FPS)",
"slow:" .. T["Slow"] .. " (5 FPS)",
"normal:" .. T["Normal"] .. " (10 FPS)",
"fast:" .. T["Fast"] .. " (20 FPS)",
"very_fast:" .. T["Very Fast"] .. " (30 FPS)",
"fastest:" .. T["Fastest"] .. " (50 FPS)",
"custom:" .. T["Custom"],
})
massCustom = CreateConfig(nil, T["Custom FPS"], _G.pfUI_throttle, "nameplates_mass_custom")
local isCustom3 = pfUI.throttle:IsCustom("nameplates_mass")
if not isCustom3 then
massCustom.input:EnableMouse(false)
massCustom.input:EnableKeyboard(false)
massCustom.input:SetTextColor(.5,.5,.5,1)
massCustom.input:SetText(tostring(pfUI.throttle:GetFps("nameplates_mass")))
else else
massCustom.input:EnableMouse(true) castbarCustom.input:EnableMouse(true)
massCustom.input:EnableKeyboard(true) castbarCustom.input:EnableKeyboard(true)
massCustom.input:SetTextColor(.2,1,.8,1) castbarCustom.input:SetTextColor(.2,1,.8,1)
if _G.pfUI_throttle.nameplates_mass_custom then if _G.pfUI_throttle.nameplates_castbar_custom then
massCustom.input:SetText(_G.pfUI_throttle.nameplates_mass_custom) castbarCustom.input:SetText(_G.pfUI_throttle.nameplates_castbar_custom)
end end
end end
@@ -1869,13 +1724,8 @@ pfUI:RegisterModule("gui", function ()
-- Reset to defaults button -- Reset to defaults button
CreateConfig(nil, T["Reset to Defaults"], nil, nil, "button", function() CreateConfig(nil, T["Reset to Defaults"], nil, nil, "button", function()
pfUI.throttle:ResetToDefault("nameplates_target") pfUI.throttle:ResetToDefault("nameplates_castbar")
pfUI.throttle:ResetToDefault("nameplates") _G.pfUI_throttle.nameplates_castbar_custom = "100"
pfUI.throttle:ResetToDefault("nameplates_mass")
-- Also reset custom fields to their default FPS values
_G.pfUI_throttle.nameplates_target_custom = "50"
_G.pfUI_throttle.nameplates_custom = "10"
_G.pfUI_throttle.nameplates_mass_custom = "7"
Reload() Reload()
end, true) end, true)
end) end)
@@ -2294,6 +2144,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding") CreateConfig(U["raid"], T["Raid Padding"], C.unitframes[c], "raidpadding")
CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout) CreateConfig(U["raid"], T["Raid Layout"], C.unitframes[c], "raidlayout", "dropdown", pfUI.gui.dropdowns.uf_raidlayout)
CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation) CreateConfig(U["raid"], T["Raid Fill Direction"], C.unitframes[c], "raidfill", "dropdown", pfUI.gui.dropdowns.orientation)
CreateConfig(U["raid"], T["Grow Right To Left"], C.unitframes[c], "raidgrowleft", "checkbox")
CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox") CreateConfig(U["raid"], T["Collapse Empty Slots"], C.unitframes[c], "collapse", "checkbox")
elseif c == "raidpet" then elseif c == "raidpet" then
CreateConfig(U[c], T["Layout"], nil, nil, "header") CreateConfig(U[c], T["Layout"], nil, nil, "header")
@@ -2360,6 +2211,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Buff Limit"], C.unitframes[c], "bufflimit") CreateConfig(U[c], T["Buff Limit"], C.unitframes[c], "bufflimit")
CreateConfig(U[c], T["Buffs Per Row"], C.unitframes[c], "buffperrow") CreateConfig(U[c], T["Buffs Per Row"], C.unitframes[c], "buffperrow")
CreateConfig(U[c], T["Own Buff Size"], C.unitframes[c], "ownbuffsize") CreateConfig(U[c], T["Own Buff Size"], C.unitframes[c], "ownbuffsize")
CreateConfig(U[c], T["Buff X-Offset"], C.unitframes[c], "buffoffx")
CreateConfig(U[c], T["Buff Y-Offset"], C.unitframes[c], "buffoffy")
CreateConfig(U[c], T["Buff X-Spacing"], C.unitframes[c], "buffspacingx") CreateConfig(U[c], T["Buff X-Spacing"], C.unitframes[c], "buffspacingx")
CreateConfig(U[c], T["Buff Y-Spacing"], C.unitframes[c], "buffspacingy") CreateConfig(U[c], T["Buff Y-Spacing"], C.unitframes[c], "buffspacingy")
@@ -2369,6 +2222,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Debuff Limit"], C.unitframes[c], "debufflimit") CreateConfig(U[c], T["Debuff Limit"], C.unitframes[c], "debufflimit")
CreateConfig(U[c], T["Debuffs Per Row"], C.unitframes[c], "debuffperrow") CreateConfig(U[c], T["Debuffs Per Row"], C.unitframes[c], "debuffperrow")
CreateConfig(U[c], T["Own Debuff Size"], C.unitframes[c], "owndebuffsize") CreateConfig(U[c], T["Own Debuff Size"], C.unitframes[c], "owndebuffsize")
CreateConfig(U[c], T["Debuff X-Offset"], C.unitframes[c], "debuffoffx")
CreateConfig(U[c], T["Debuff Y-Offset"], C.unitframes[c], "debuffoffy")
CreateConfig(U[c], T["Debuff X-Spacing"], C.unitframes[c], "debuffspacingx") CreateConfig(U[c], T["Debuff X-Spacing"], C.unitframes[c], "debuffspacingx")
CreateConfig(U[c], T["Debuff Y-Spacing"], C.unitframes[c], "debuffspacingy") CreateConfig(U[c], T["Debuff Y-Spacing"], C.unitframes[c], "debuffspacingy")
+1215 -1432
View File
File diff suppressed because it is too large Load Diff
+15 -21
View File
@@ -30,8 +30,8 @@ pfUI:RegisterModule("raid", function ()
-- flat pool of pet frames, laid out by LayoutPets (mirror or collapsed) -- flat pool of pet frames, laid out by LayoutPets (mirror or collapsed)
pfUI.uf.raid.pets = {} pfUI.uf.raid.pets = {}
-- 1-based grid slot -> (row, col) for the current fill direction, matching -- 1-based grid slot -> (col, row) for the current fill direction, shared by
-- the raid grid's own fill loops. -- the raid and pet grids.
local function SlotToCoord(slot, fill, x, y) local function SlotToCoord(slot, fill, x, y)
slot = slot - 1 slot = slot - 1
if fill == "VERTICAL" then if fill == "VERTICAL" then
@@ -75,12 +75,12 @@ pfUI:RegisterModule("raid", function ()
end end
end end
local i = 1
local width = pfUI.uf.raid[1]:GetWidth()+2*default_border local width = pfUI.uf.raid[1]:GetWidth()+2*default_border
local height = pfUI.uf.raid[1]:GetHeight()+2*default_border local height = pfUI.uf.raid[1]:GetHeight()+2*default_border
local layout = pfUI.uf.raid[1].config.raidlayout local layout = pfUI.uf.raid[1].config.raidlayout
local padding = tonumber(pfUI.uf.raid[1].config.raidpadding)*GetPerfectPixel() local padding = tonumber(pfUI.uf.raid[1].config.raidpadding)*GetPerfectPixel()
local fill = pfUI.uf.raid[1].config.raidfill local fill = pfUI.uf.raid[1].config.raidfill
local growleft = pfUI.uf.raid[1].config.raidgrowleft == "1"
local _, _, x, y = string.find(layout,"(.+)x(.+)") local _, _, x, y = string.find(layout,"(.+)x(.+)")
x, y = tonumber(x), tonumber(y) x, y = tonumber(x), tonumber(y)
@@ -95,24 +95,18 @@ pfUI:RegisterModule("raid", function ()
} }
end end
if fill == "VERTICAL" then -- right to left mirrors across the columns maxraid can fill, so a smaller
for r=1, x do for g=1, y do -- raid stays next to the anchor instead of the far edge of the layout
if pfUI.uf.raid[i] then local cols = min(x, fill == "VERTICAL" and ceil(maxraid / y) or maxraid)
pfUI.uf.raid[i]:ClearAllPoints()
pfUI.uf.raid[i]:SetPoint("BOTTOMLEFT", (r-1)*(padding+width), (g-1)*(padding+height)) for i=1, x*y do
UpdateMovable(pfUI.uf.raid[i], true) if pfUI.uf.raid[i] then
end local r, g = SlotToCoord(i, fill, x, y)
i = i + 1 if growleft then r = cols - r + 1 end
end end pfUI.uf.raid[i]:ClearAllPoints()
else pfUI.uf.raid[i]:SetPoint("BOTTOMLEFT", (r-1)*(padding+width), (g-1)*(padding+height))
for g=1, y do for r=1, x do UpdateMovable(pfUI.uf.raid[i], true)
if pfUI.uf.raid[i] then end
pfUI.uf.raid[i]:ClearAllPoints()
pfUI.uf.raid[i]:SetPoint("BOTTOMLEFT", (r-1)*(padding+width), (g-1)*(padding+height))
UpdateMovable(pfUI.uf.raid[i], true)
end
i = i + 1
end end
end end
self:LayoutPets() self:LayoutPets()