Files
moon-well-ui/data/addon/ForeverUI/RaidFrame.lua
T
Claude 2d815b1132 ForeverUI : cadres d'unités, points de combo, véhicules, incantation, raid
Points de combo : ComboFrame, le seul affichage de 3.3.5, était supprimé
sans remplaçant ; voleurs et druides félins n'en voyaient aucun. Il est
gardé et posé sur notre portrait, au décalage de ComboFrame.xml.

Joueur en véhicule : les barres montrent "vehicle", mais seuls les
événements de "player" étaient lus ; la vie et l'énergie du véhicule se
figeaient (Oculus, Ulduar, Joug-d'hiver). Les textes suivent aussi.
Groupe : un membre sorti de son véhicule gardait le jeton partypetN, et son
cadre ne se mettait plus jamais à jour ; le test porte sur partyN, et le nom
est celui du membre, comme le client.

Barre d'incantation : STOP, FAILED et INTERRUPTED ne visent plus que
l'incantation affichée (son castID), comme CastingBarFrame : une touche
pressée de nouveau pendant un sort affichait « Échec » et effaçait la
vraie barre ; un sort instantané refusé montrait une barre rouge.

Cible de la cible : "targettarget" n'a aucun événement en 3.3.5 ; elle est
interrogée cinq fois par seconde tant qu'elle est montrée (le client le
fait à chaque image). Après un rechargement en combat, les cadres joueur et
cible du client sont masqués à la sortie du combat.

Raid : chaque événement d'unité reconstruisait tout le bouton (barres,
rôle, auras, onze icônes réancrées, une table allouée) ; santé, ressource,
auras et menace ne refont plus que leur partie, et les grilles d'auras ne
bougent que si le décalage des icônes de dissipation change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KxRZNHkq453dyUMcWsyUf1
2026-10-01 23:24:53 +00:00

661 lines
22 KiB
Lua

-- ForeverUI: compact raid frames in the camelot style (CompactUnitFrame, CompactRaidGroup).
-- Eight SecureRaidGroupHeaderTemplate headers, one per group, place the players, even in combat.
-- 3.3.5 has no heal or absorb prediction, phase, summon or incoming resurrection icons, and
-- UnitAura does not flag boss debuffs, so all debuffs are 11 px.
local ForeverUI = ForeverUI or {}
_G.ForeverUI = ForeverUI
local SEP = string.char(92)
local L = ForeverUI.L
local LFG = "Interface" .. SEP .. "ForeverUI" .. SEP .. "lfgframe" .. SEP
-- Sizes from camelot (editmodepresetlayouts: 98 x 44; component scale min(44/36, 98/72)).
-- background: estimated, the source constant is missing. Outlines are nine-sliced with
-- corners measured on the art: stretched whole, their edges thicken.
local P = {
width = 98, height = 44, titleH = 14, groups = 8, perGroup = 5,
x = 22, y = -145,
resourceH = 8, auraBottom = 2, aura = 11, auraBorder = 5, dispel = 14,
buffs = 6, debuffs = 5, dispels = 3, perRow = 3, dispelGap = 2,
role = 17, call = 20, status = 12, readyCheckFinish = 11,
background = { 0.1, 0.1, 0.1 },
threatCorner = 10, targetCorner = 10, dispelCorner = 2,
outOfRangeAlpha = 0.5, rangePeriod = 0.5,
markers = {
ready = LFG .. "ui-lfg-readymark-raid",
notready = LFG .. "ui-lfg-declinemark-raid",
waiting = LFG .. "ui-lfg-pendingmark-raid",
},
roles = {
TANK = LFG .. "ui-lfg-roleicon-tank-micro-groupfinder",
HEALER = LFG .. "ui-lfg-roleicon-healer-micro-groupfinder",
DAMAGER = LFG .. "ui-lfg-roleicon-dps-micro-groupfinder",
},
borders = { Magic = "magic", Curse = "curse", Disease = "disease", Poison = "poison" },
}
P.scale = math.min(P.height / 36, P.width / 72)
local R = { buttons = {} }
ForeverUI.RaidFrame = R
-- ----------------------------------------------------------------- pieces
local function showRegion(slices, yes)
for _, t in ipairs(slices) do
if yes then t:Show() else t:Hide() end
end
end
local function applyTint(slices, r, g, b)
for _, t in ipairs(slices) do t:SetVertexColor(r, g, b) end
end
-- Crops the bar texture to fraction of isFull, the full width; hidden under half a pixel.
local function populate(tex, fraction, isFull)
if not fraction or fraction ~= fraction or fraction < 0 then fraction = 0 end
if fraction > 1 then fraction = 1 end
local width = isFull * fraction
if width < 0.5 then
tex:Hide()
return
end
tex:SetWidth(width)
tex:SetTexCoord(tex.u1, tex.u1 + (tex.u2 - tex.u1) * fraction, tex.v1, tex.v2)
tex:Show()
end
-- Bar texture from an atlas entry, keeping its tex coords for populate.
local function bar(parent, layer, atlas)
local t = parent:CreateTexture(nil, layer)
local e = ForeverUI.AtlasEntry(atlas)
t:SetTexture(e[1])
t.u1, t.u2, t.v1, t.v2 = e[2], e[3], e[4], e[5]
return t
end
-- Aura icon of size side, with cooldown and stack count; border: add a debuff border.
local function createAura(parent, side, border)
local a = CreateFrame("Frame", nil, parent)
a:SetWidth(side)
a:SetHeight(side)
a:EnableMouse(false)
a.icon = a:CreateTexture(nil, "ARTWORK")
a.icon:SetAllPoints(a)
if border then
a.border = a:CreateTexture(nil, "OVERLAY")
a.border:SetWidth(side + P.auraBorder)
a.border:SetHeight(side + P.auraBorder)
a.border:SetPoint("CENTER", a, "CENTER", 0, 0)
end
a.cooldown = CreateFrame("Cooldown", nil, a)
a.cooldown:SetReverse(true)
a.cooldown:SetPoint("TOPLEFT", a, "TOPLEFT", 0, -1)
a.cooldown:SetPoint("BOTTOMRIGHT", a, "BOTTOMRIGHT", 0, -1)
a.stack = a:CreateFontString(nil, "OVERLAY", "NumberFontNormal")
a.stack:SetPoint("BOTTOMRIGHT", a, "BOTTOMRIGHT", -2, 2)
a:Hide()
return a
end
-- 3-wide grids (AnchorUtil.CreateGridLayout): the k-th cell from corner.
-- directionX: 1 rightwards, -1 leftwards; x, y: first cell offset; side: cell size
local function placeGrid(auras, corner, directionX, x, y, side)
for k, a in ipairs(auras) do
local col, row = (k - 1) % P.perRow, math.floor((k - 1) / P.perRow)
a:ClearAllPoints()
a:SetPoint(corner, a:GetParent(), corner, x + directionX * col * side, y + row * side)
end
end
-- -------------------------------------------------------- player frame
local function buildButton(f)
f.background = f:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(f.background, "raidframe-hp-bg-white", true)
f.background:SetAllPoints(f)
f.background:SetVertexColor(P.background[1], P.background[2], P.background[3])
f.health = bar(f, "BORDER", "raidframe-hp-fill")
f.health:SetPoint("TOPLEFT", f, "TOPLEFT", 1, -1)
f.resourceBackground = bar(f, "BORDER", "_raidframe-resource-background")
f.resource = bar(f, "ARTWORK", "_raidframe-resource-fill")
f.threat = ForeverUI.CreateNineSlice(f, "raidframe-agroframe", P.threatCorner, { 0, 0, 0, 0 }, "ARTWORK") or {}
showRegion(f.threat, false)
-- dispel overlay
local overlay = CreateFrame("Frame", nil, f)
overlay:SetAllPoints(f)
overlay:SetFrameLevel(f:GetFrameLevel() + 1)
overlay.background = overlay:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(overlay.background, "raidframe-dispel-fill", true)
overlay.background:SetAllPoints(overlay)
overlay.background:SetAlpha(0.2)
overlay.gradient = overlay:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(overlay.gradient, "_raidframe-dispel-highlight-horizontal", true)
overlay.gradient:SetAllPoints(overlay)
overlay.edge = ForeverUI.CreateNineSlice(overlay, "raidframe-dispelhighlight", P.dispelCorner, { 0, 0, 0, 0 }, "ARTWORK") or {}
overlay:Hide()
f.overlay = overlay
-- text, role and auras above the bars
local hovered = CreateFrame("Frame", nil, f)
hovered:SetAllPoints(f)
hovered:SetFrameLevel(f:GetFrameLevel() + 2)
f.hovered = hovered
f.role = hovered:CreateTexture(nil, "ARTWORK")
f.role:SetWidth(P.role)
f.role:SetHeight(P.role)
f.role:SetPoint("TOPLEFT", f, "TOPLEFT", 3, -2)
f.name = hovered:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
f.name:SetPoint("TOPLEFT", f.role, "TOPRIGHT", 0, -1)
f.name:SetPoint("TOPRIGHT", f, "TOPRIGHT", -3, -3)
f.name:SetJustifyH("LEFT")
f.name:SetHeight(12)
f.status = hovered:CreateFontString(nil, "ARTWORK", "GameFontDisable")
local font, _, flags = f.status:GetFont()
if font then f.status:SetFont(font, P.status * P.scale, flags) end
f.status:SetHeight(P.status * P.scale)
f.status:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 3, P.height / 3 - 2)
f.status:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -3, P.height / 3 - 2)
f.buffs, f.debuffs, f.dispels = {}, {}, {}
for k = 1, P.buffs do f.buffs[k] = createAura(hovered, P.aura) end
for k = 1, P.debuffs do f.debuffs[k] = createAura(hovered, P.aura, true) end
for k = 1, P.dispels do
local t = hovered:CreateTexture(nil, "OVERLAY")
t:SetWidth(P.dispel)
t:SetHeight(P.dispel)
t:SetPoint("TOPRIGHT", f, "TOPRIGHT", -3 - (k - 1) * P.dispel, -2)
t:Hide()
f.dispels[k] = t
end
-- target outline and ready check: full alpha even out of range
local top = CreateFrame("Frame", nil, f)
top:SetAllPoints(f)
top:SetFrameLevel(f:GetFrameLevel() + 3)
f.target = ForeverUI.CreateNineSlice(top, "raidframe-targetframe", P.targetCorner, { 0, 0, 0, 0 }, "OVERLAY") or {}
showRegion(f.target, false)
f.call = top:CreateTexture(nil, "OVERLAY")
f.call:SetWidth(P.call * P.scale)
f.call:SetHeight(P.call * P.scale)
f.call:SetPoint("BOTTOM", f, "BOTTOM", 0, P.height / 3 - 4)
f.call:Hide()
-- What range fades. 3.3.5 has no ignoreParentAlpha, so regions fade one by one.
f.fadedRegions = { f.background, f.health, f.resourceBackground, f.resource, f.name, f.status, f.role }
end
-- New button setup (initialConfigFunction): clicks, menu, size (initial-width / -height,
-- read by the header), art
local function configure(f)
f:RegisterForClicks("AnyUp")
f:SetAttribute("*type1", "target")
f:SetAttribute("*type2", "menu")
f:SetAttribute("toggleForVehicle", true)
f:SetAttribute("initial-width", P.width)
f:SetAttribute("initial-height", P.height)
f.menu = function(self) R.openMenu(self) end
buildButton(f)
f:HookScript("OnAttributeChanged", function(self, name, value)
if name == "unit" then
self.unit = value
R.updateButton(self)
end
end)
f:HookScript("OnShow", function(self) R.updateButton(self) end)
f:SetScript("OnEnter", function(self)
if not self.display then return end
GameTooltip_SetDefaultAnchor(GameTooltip, self)
GameTooltip:SetUnit(self.display)
GameTooltip:Show()
end)
f:SetScript("OnLeave", function() GameTooltip:FadeOut() end)
table.insert(R.buttons, f)
end
-- ------------------------------------------------------------ updates
local function fraction(v, m)
if not m or m <= 0 then return 0 end
return v / m
end
-- shown unit: the vehicle if any (CompactUnitFrame_UpdateUnitEvents)
local function displayedUnit(unit)
if UnitHasVehicleUI(unit) and UnitTargetsVehicleInRaidUI(unit) then
local v = unit:gsub("^raid(%d+)$", "raidpet%1")
if UnitExists(v) then return v, true end
end
return unit, false
end
local function updateHealth(f)
local u = f.display
local connected = UnitIsConnected(f.unit)
local isFull = P.width - 2
populate(f.health, connected and fraction(UnitHealth(u), UnitHealthMax(u)) or 1, isFull)
-- CompactUnitFrame_UpdateHealthColor
if not connected then
f.health:SetVertexColor(0.5, 0.5, 0.5)
else
local _, className = UnitClass(f.unit)
local c = className and RAID_CLASS_COLORS[className]
if c then f.health:SetVertexColor(c.r, c.g, c.b) else f.health:SetVertexColor(0, 1, 0) end
end
-- CompactUnitFrame_UpdateStatusText
if not connected then
f.status:SetText(PLAYER_OFFLINE)
f.status:Show()
elseif UnitIsDeadOrGhost(u) then
f.status:SetText(DEAD)
f.status:Show()
else
f.status:Hide()
end
end
local function updateResource(f)
local u = f.display
local type, token = UnitPowerType(u)
local c = (token and PowerBarColor[token]) or PowerBarColor[type] or PowerBarColor["MANA"]
if not UnitIsConnected(f.unit) then
f.resource:SetVertexColor(0.5, 0.5, 0.5)
else
f.resource:SetVertexColor(c.r, c.g, c.b)
end
populate(f.resource, fraction(UnitPower(u, type), UnitPowerMax(u, type)), P.width - 2)
end
-- vertical layout: health, then an 8 px resource bar (DefaultCompactUnitFrameSetup)
local function placeBars(f)
f.health:SetHeight(P.height - 2 - P.resourceH)
f.resourceBackground:ClearAllPoints()
f.resourceBackground:SetPoint("TOPLEFT", f, "BOTTOMLEFT", 1, 1 + P.resourceH)
f.resourceBackground:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -1, 1)
f.resource:ClearAllPoints()
f.resource:SetPoint("TOPLEFT", f.resourceBackground, "TOPLEFT", 0, 0)
f.resource:SetHeight(P.resourceH)
end
-- role icon: vehicle, then MT / MA, then the group role
local function updateRole(f)
local atlas, file
if f.vehicle then
atlas = "raidframe-icon-vehicle"
else
local id = f.unit and tonumber(f.unit:match("^raid(%d+)$"))
local role = id and select(10, GetRaidRosterInfo(id))
if role == "MAINTANK" then
atlas = "raidframe-icon-maintank"
elseif role == "MAINASSIST" then
atlas = "raidframe-icon-mainassist"
else
local tank, healer, damage = UnitGroupRolesAssigned(f.unit)
file = (tank and P.roles.TANK) or (healer and P.roles.HEALER) or (damage and P.roles.DAMAGER)
end
end
if atlas then
ForeverUI.SetAtlas(f.role, atlas, true)
elseif file then
f.role:SetTexture(file)
f.role:SetTexCoord(0, 1, 0, 1)
end
if atlas or file then
f.role:SetWidth(P.role)
f.role:Show()
else
-- hidden, it keeps 1 px width so the name moves left
f.role:SetWidth(1)
f.role:Hide()
end
end
local function updateThreat(f)
local status = UnitThreatSituation(f.display)
if status and status > 0 then
applyTint(f.threat, GetThreatStatusColor(status))
showRegion(f.threat, true)
else
showRegion(f.threat, false)
end
end
local function updateTarget(f)
showRegion(f.target, f.display and UnitIsUnit(f.display, "target"))
end
local function applyAura(a, icon, stack, duration, finish)
a.icon:SetTexture(icon)
if stack and stack > 1 then a.stack:SetText(stack >= 100 and "*" or stack) else a.stack:SetText("") end
if duration and duration > 0 and finish then
CooldownFrame_SetTimer(a.cooldown, finish - duration, duration, 1)
else
a.cooldown:Hide()
end
a:Show()
end
local function updateAuras(f)
local u = f.display
-- dispel: the first debuff that has a type
local types, first = {}, nil
for i = 1, 40 do
local name, _, _, _, type = UnitDebuff(u, i)
if not name then break end
if type and P.borders[type] and not types[type] then
types[type] = true
types[#types + 1] = type
first = first or type
end
end
local d = first and P.dispelGap or 0
-- The grids move only when the dispel icons come or go, not on every aura change
if f.auraOffset ~= d then
f.auraOffset = d
placeGrid(f.buffs, "BOTTOMRIGHT", -1, -3 - d, P.auraBottom + P.resourceH + d, P.aura)
placeGrid(f.debuffs, "BOTTOMLEFT", 1, 3 + d, P.auraBottom + P.resourceH + d, P.aura)
end
if first then
local c = DebuffTypeColor[first] or DebuffTypeColor["none"]
applyTint({ f.overlay.background, f.overlay.gradient }, c.r, c.g, c.b)
applyTint(f.overlay.edge, c.r, c.g, c.b)
f.overlay:Show()
else
f.overlay:Hide()
end
for k, t in ipairs(f.dispels) do
local type = types[k]
if type and ForeverUI.SetAtlas(t, "raidframe-icon-debuff" .. P.borders[type], true) then
t:Show()
else
t:Hide()
end
end
-- buffs, from BOTTOMRIGHT leftwards; the PLAYER filter stands in for camelot's
-- player-castable buffs
for k, a in ipairs(f.buffs) do
local name, _, icon, stack, _, duration, finish = UnitBuff(u, k, "PLAYER")
if name then applyAura(a, icon, stack, duration, finish) else a:Hide() end
end
-- debuffs, from BOTTOMLEFT rightwards
for k, a in ipairs(f.debuffs) do
local name, _, icon, stack, type, duration, finish = UnitDebuff(u, k)
if name then
ForeverUI.SetAtlas(a.border, "ui-debuff-border-" .. (P.borders[type or ""] or "default") .. "-noicon", true)
applyAura(a, icon, stack, duration, finish)
else
a:Hide()
end
end
end
local function updateReadyCheck(f, finish)
local state = f.unit and GetReadyCheckStatus(f.unit)
if not state then
if not finish then f.call:Hide() end
return
end
if finish and state == "waiting" then state = "notready" end
f.call:SetTexture(P.markers[state] or P.markers.waiting)
f.call:Show()
end
local function updateRange(f)
local inside = UnitInRange(f.display)
local a = (inside or UnitIsUnit(f.display, "player")) and 1 or P.outOfRangeAlpha
if f.rangeAlpha ~= a then
f.rangeAlpha = a
for _, r in ipairs(f.fadedRegions) do r:SetAlpha(a) end
for _, g in ipairs({ f.buffs, f.debuffs }) do
for _, x in ipairs(g) do x:SetAlpha(a) end
end
for _, t in ipairs(f.threat) do t:SetAlpha(a) end
f.overlay:SetAlpha(a)
end
end
function R.updateButton(f)
if not f.unit or not UnitExists(f.unit) then
f.display = nil
return
end
f.display, f.vehicle = displayedUnit(f.unit)
placeBars(f)
f.name:SetText(GetUnitName(f.unit, false))
updateHealth(f)
updateResource(f)
updateRole(f)
updateThreat(f)
updateTarget(f)
updateAuras(f)
updateReadyCheck(f)
updateRange(f)
end
-- What each frequent unit event changes on a button; the others rebuild it. In a raid these
-- events come by hundreds a second: a full rebuild (bars, role, auras...) for each was the
-- main cost of the frames.
local PARTIAL = {
UNIT_HEALTH = updateHealth, UNIT_MAXHEALTH = updateHealth,
UNIT_AURA = updateAuras, UNIT_THREAT_SITUATION_UPDATE = updateThreat,
}
for _, ev in ipairs({ "UNIT_DISPLAYPOWER", "UNIT_MANA", "UNIT_RAGE", "UNIT_FOCUS", "UNIT_ENERGY",
"UNIT_RUNIC_POWER", "UNIT_MAXMANA", "UNIT_MAXRAGE", "UNIT_MAXFOCUS", "UNIT_MAXENERGY",
"UNIT_MAXRUNIC_POWER" }) do
PARTIAL[ev] = updateResource
end
-- --------------------------------------------------------------------- menu
local menu = CreateFrame("Frame", "ForeverUICompactRaidFrameDropDown", UIParent, "UIDropDownMenuTemplate")
menu:Hide()
menu.displayMode = "MENU"
-- CompactUnitFrame_OpenMenu: SELF, VEHICLE or RAID_PLAYER; the menu fills when opened
-- (ToggleDropDownMenu), never when built
menu.initialize = function(self)
local m = UIDROPDOWNMENU_OPEN_MENU or self
if not m or not m.unit then return end
UnitPopup_ShowMenu(m, m.which, m.unit, m.name, m.id)
end
function R.openMenu(f)
if not f.unit then return end
local id = tonumber(f.unit:match("^raid(%d+)$"))
if UnitIsUnit(f.unit, "player") then
menu.which = "SELF"
elseif f.vehicle then
menu.which = "VEHICLE"
else
menu.which = "RAID_PLAYER"
end
menu.unit = f.vehicle and f.display or f.unit
menu.name = UnitName(f.unit)
menu.id = id
ForeverUI.UnitMenu.open(ToggleDropDownMenu, 1, nil, menu, "cursor")
end
-- ------------------------------------------------------ container and groups
local container = CreateFrame("Frame", "ForeverUICompactRaidFrameContainer", UIParent)
container:SetFrameStrata("LOW")
container:SetWidth(P.width)
container:SetHeight(P.titleH + P.perGroup * P.height)
R.container = container
R.headers, R.titles = {}, {}
for g = 1, P.groups do
local h = CreateFrame("Frame", "ForeverUICompactRaidGroup" .. g, container, "SecureRaidGroupHeaderTemplate")
h:SetAttribute("groupFilter", tostring(g))
h:SetAttribute("point", "TOP")
h:SetAttribute("yOffset", 0)
h:SetAttribute("template", "SecureUnitButtonTemplate")
h:SetAttribute("templateType", "Button")
h:SetAttribute("unitsPerColumn", P.perGroup)
h:SetAttribute("maxColumns", 1)
h:SetAttribute("sortMethod", "INDEX")
h.initialConfigFunction = configure
h:SetWidth(P.width)
h:SetHeight(P.perGroup * P.height)
R.headers[g] = h
-- title (CompactRaidGroup: 14 high, GameFontNormalSmall): a plain frame, so it can show or
-- hide in combat. The client has no GROUP_NUMBER, so it reads GROUP and the number.
local t = CreateFrame("Frame", nil, container)
t:SetWidth(P.width)
t:SetHeight(P.titleH)
t.text = t:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
t.text:SetPoint("TOP", t, "TOP", 0, 0)
t.text:SetText(GROUP .. " " .. g)
t:Hide()
R.titles[g] = t
end
-- Groups that have members; a flag per group number.
local function usedGroups()
local inUse = {}
for i = 1, (GetNumRaidMembers() or 0) do
local _, _, group = GetRaidRosterInfo(i)
if group then inUse[group] = true end
end
return inUse
end
-- Used groups go in adjacent columns, the others follow in order, so a group filling up in
-- combat appears at the end without overlapping. Secure headers move only out of combat,
-- so in combat the layout waits for PLAYER_REGEN_ENABLED.
function R.layout()
if InCombatLockdown() then
R.needsLayout = true
return
end
R.needsLayout = nil
local inUse = usedGroups()
local order = {}
for g = 1, P.groups do if inUse[g] then order[#order + 1] = g end end
local n = #order
for g = 1, P.groups do if not inUse[g] then order[#order + 1] = g end end
for k, g in ipairs(order) do
local x = (k - 1) * P.width
R.titles[g]:ClearAllPoints()
R.titles[g]:SetPoint("TOPLEFT", container, "TOPLEFT", x, 0)
R.headers[g]:ClearAllPoints()
R.headers[g]:SetPoint("TOPLEFT", container, "TOPLEFT", x, -P.titleH)
end
container:SetWidth(P.width * math.max(n, 1))
R.updateTitles()
end
-- one title per group with members (not a protected frame)
function R.updateTitles()
local inUse = usedGroups()
for g = 1, P.groups do
if inUse[g] then R.titles[g]:Show() else R.titles[g]:Hide() end
end
end
-- Creates the five buttons of each group ahead, out of combat: startingIndex at -4 for a
-- moment makes the header build them all, so none is created in combat.
local function createButtons()
container:Show()
for _, h in ipairs(R.headers) do
h:Show()
h:SetAttribute("startingIndex", -(P.perGroup - 1))
h:SetAttribute("startingIndex", 1)
end
end
-- ------------------------------------------------------------------ HD addon
-- "Compact Raid Frames - HD client" (patch-5.mpq): disabled for the next session, its frames
-- hidden for this one
local function disableHDAddon()
if not (IsAddOnLoaded and IsAddOnLoaded("CompactRaidFrame")) then return end
if DisableAddOn then DisableAddOn("CompactRaidFrame") end
if InCombatLockdown() then return end
for _, name in ipairs({ "CompactRaidFrameManager", "CompactRaidFrameContainer" }) do
local f = _G[name]
if f then RegisterStateDriver(f, "visibility", "hide") end
end
end
-- --------------------------------------------------------------- events
local listener = CreateFrame("Frame")
R.listener = listener
for _, ev in ipairs({ "PLAYER_ENTERING_WORLD", "RAID_ROSTER_UPDATE", "PLAYER_REGEN_ENABLED",
"PLAYER_TARGET_CHANGED", "PLAYER_ROLES_ASSIGNED", "UNIT_HEALTH", "UNIT_MAXHEALTH", "UNIT_DISPLAYPOWER",
"UNIT_MANA", "UNIT_RAGE", "UNIT_FOCUS", "UNIT_ENERGY", "UNIT_RUNIC_POWER", "UNIT_MAXMANA",
"UNIT_MAXRAGE", "UNIT_MAXFOCUS", "UNIT_MAXENERGY", "UNIT_MAXRUNIC_POWER", "UNIT_NAME_UPDATE",
"UNIT_AURA", "UNIT_THREAT_SITUATION_UPDATE", "UNIT_ENTERED_VEHICLE", "UNIT_EXITED_VEHICLE",
"PARTY_MEMBER_ENABLE", "PARTY_MEMBER_DISABLE", "READY_CHECK", "READY_CHECK_CONFIRM",
"READY_CHECK_FINISHED" }) do
listener:RegisterEvent(ev)
end
local function all()
for _, f in ipairs(R.buttons) do
if f:IsShown() then R.updateButton(f) end
end
end
listener:SetScript("OnEvent", function(self, ev, unit)
if ev == "PLAYER_ENTERING_WORLD" then
disableHDAddon()
R.layout()
all()
elseif ev == "RAID_ROSTER_UPDATE" then
R.layout()
R.updateTitles()
all()
elseif ev == "PLAYER_REGEN_ENABLED" then
if R.needsLayout then R.layout() end
disableHDAddon()
elseif ev == "PLAYER_TARGET_CHANGED" then
for _, f in ipairs(R.buttons) do if f.display then updateTarget(f) end end
elseif ev == "PLAYER_ROLES_ASSIGNED" or ev == "PARTY_MEMBER_ENABLE" or ev == "PARTY_MEMBER_DISABLE" then
all()
elseif ev == "READY_CHECK" or ev == "READY_CHECK_CONFIRM" then
for _, f in ipairs(R.buttons) do updateReadyCheck(f) end
elseif ev == "READY_CHECK_FINISHED" then
for _, f in ipairs(R.buttons) do updateReadyCheck(f, true) end
R.readyCheckFinish = P.readyCheckFinish
R.timer:Show()
else
local partial = PARTIAL[ev]
for _, f in ipairs(R.buttons) do
if f.unit and (f.unit == unit or f.display == unit) then
if partial and f.display then partial(f) else R.updateButton(f) end
end
end
end
end)
-- range (UnitInRange has no event in 3.3.5) and the end of the ready check
local timer = CreateFrame("Frame")
timer.clock = 0
timer:SetScript("OnUpdate", function(self, elapsed)
self.clock = self.clock + elapsed
if self.clock >= P.rangePeriod then
self.clock = 0
for _, f in ipairs(R.buttons) do
if f.display and f:IsVisible() then updateRange(f) end
end
end
if R.readyCheckFinish then
R.readyCheckFinish = R.readyCheckFinish - elapsed
if R.readyCheckFinish <= 0 then
R.readyCheckFinish = nil
for _, f in ipairs(R.buttons) do f.call:Hide() end
end
end
end)
R.timer = timer
createButtons()
RegisterStateDriver(container, "visibility", "[group:raid] show; hide")
R.layout()
ForeverUI.Layout.Register(container, "raidframe", L.RAIDFRAME_EDIT_LABEL, "TOPLEFT", "TOPLEFT", P.x, P.y)