diff --git a/modules/raid.lua b/modules/raid.lua new file mode 100644 index 0000000..ed503a8 --- /dev/null +++ b/modules/raid.lua @@ -0,0 +1,179 @@ +pfUI:RegisterModule("raid", "vanilla:tbc", function () + -- do not go further on disabled UFs + if C.unitframes.disable == "1" then return end + + -- tell RaidFrame.lua pfUI replaces party frames + HookAddonOrVariable("Blizzard_RaidUI", function() + GROUP_REPLACE_PARTY = "1" + end) + + pfUI.uf.raid = CreateFrame("Frame", "pfRaidUpdater", UIParent) + + local maxraid = tonumber(C.unitframes.maxraid) + local rawborder, default_border = GetBorderSize("chat") + local cluster = CreateFrame("Frame", "pfRaidCluster", UIParent) + cluster:SetFrameLevel(20) + cluster:SetWidth(120) + cluster:SetHeight(10) + cluster:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", default_border*2, C.chat.left.height + default_border*5) + UpdateMovable(cluster) + + pfUI.uf.raid.tanksfirst = { + ["PF_TANK_TOGGLE"] = { T["Toggle as Tank"], "toggleTank" } + } + + -- no tank order for now, just "all tanks first" + pfUI.uf.raid.tankrole = { } + + function pfUI.uf.raid:UpdateConfig() + local rawborder, default_border = GetBorderSize("unitframes") + maxraid = tonumber(C.unitframes.maxraid) + + for i=1,maxraid do + pfUI.uf.raid[i] = pfUI.uf.raid[i] or pfUI.uf:CreateUnitFrame("Raid", i, C.unitframes.raid) + pfUI.uf.raid[i]:SetParent(cluster) + pfUI.uf.raid[i]:SetFrameLevel(5) + + pfUI.uf.raid[i]:UpdateConfig() + pfUI.uf.raid[i]:UpdateFrameSize() + end + + local i = 1 + local width = pfUI.uf.raid[1]:GetWidth()+2*default_border + local height = pfUI.uf.raid[1]:GetHeight()+2*default_border + local layout = pfUI.uf.raid[1].config.raidlayout + local padding = tonumber(pfUI.uf.raid[1].config.raidpadding)*GetPerfectPixel() + local fill = pfUI.uf.raid[1].config.raidfill + local _, _, x, y = string.find(layout,"(.+)x(.+)") + x, y = tonumber(x), tonumber(y) + + if fill == "VERTICAL" then + for r=1, x do for g=1, y do + if pfUI.uf.raid[i] then + 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 + else + for g=1, y do for r=1, x do + if pfUI.uf.raid[i] then + 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 + + pfUI.uf.raid:UpdateConfig() + + local function SetRaidIndex(frame, id) + frame.id = id + frame.label = "raid" + frame:UpdateVisibility() + end + + -- add units to the beginning of their groups + function pfUI.uf.raid:AddUnitToGroup(index, group) + for subindex = 1, 5 do + local ids = subindex + 5*(group-1) + if pfUI.uf.raid[ids] and pfUI.uf.raid[ids].id == 0 and pfUI.uf.raid[ids].config.visible == "1" then + SetRaidIndex(pfUI.uf.raid[ids], index) + return + end + end + end + + pfUI.uf.raid:Hide() + pfUI.uf.raid:RegisterEvent("RAID_ROSTER_UPDATE") + pfUI.uf.raid:RegisterEvent("PARTY_MEMBERS_CHANGED") + pfUI.uf.raid:RegisterEvent("PARTY_LEADER_CHANGED") + pfUI.uf.raid:RegisterEvent("VARIABLES_LOADED") + pfUI.uf.raid:SetScript("OnEvent", function() + this:Show() + -- Debounce: delay update by 0.5s to batch rapid roster changes (mass swaps) + this.pendingUpdate = GetTime() + 0.5 + end) + pfUI.uf.raid:SetScript("OnUpdate", function() + -- Wait for debounce: don't update until 0.5s after last event + if this.pendingUpdate and GetTime() < this.pendingUpdate then return end + -- Throttle raid roster updates to 1 FPS max + if (this.tick or 0) > GetTime() then return end + this.tick = GetTime() + 1.0 + this.pendingUpdate = nil + + -- don't proceed without raid or during combat + if not UnitInRaid("player") or (InCombatLockdown and InCombatLockdown()) then return end + + -- clear all existing frames + for i=1, maxraid do SetRaidIndex(pfUI.uf.raid[i], 0) end + + -- sort tanks into their groups + for i=1, GetNumRaidMembers() do + local name, _, subgroup = GetRaidRosterInfo(i) + if name and pfUI.uf.raid.tankrole[name] then + pfUI.uf.raid:AddUnitToGroup(i, subgroup) + end + end + + -- sort players into roster + for i=1, GetNumRaidMembers() do + local name, _, subgroup = GetRaidRosterInfo(i) + if name and not pfUI.uf.raid.tankrole[name] then + pfUI.uf.raid:AddUnitToGroup(i, subgroup) + end + end + + -- Smart GUID-based updates: only refresh frames where unit changed + if pfUI.uf.guidTracker then + local tracker = pfUI.uf.guidTracker + + for i = 1, maxraid do + local frame = pfUI.uf.raid[i] + if frame and frame.id and frame.id > 0 then + local unit = "raid" .. frame.id + local newGuid = GetUnitGUID(unit) + local oldGuid = tracker.frameToGuid[frame] + + if newGuid ~= oldGuid then + -- GUID changed = different player = need full update + tracker.frameToGuid[frame] = newGuid + frame.update_full = true + frame.update_aura = true -- Force aura refresh! + end + end + end + end + + -- rebuild unitmap after frame IDs are assigned + if pfUI.uf.RebuildUnitmap then + pfUI.uf.RebuildUnitmap() + end + + this:Hide() + end) + + -- raid popup option to toggle tank role + for _, menu in pairs({"RAID", "PARTY"}) do + for label, data in pairs(pfUI.uf.raid.tanksfirst) do + UnitPopupButtons[label] = { text = TEXT(data[1]), dist = 0 } + table.insert(UnitPopupMenus[menu], 3, label) + end + end + + hooksecurefunc("UnitPopup_OnClick", function() + local dropdownFrame = UIDROPDOWNMENU_INIT_MENU and _G[UIDROPDOWNMENU_INIT_MENU] + if not dropdownFrame then return end + local button = this.value + local unit = dropdownFrame.unit + local name = dropdownFrame.name + + if button and pfUI.uf.raid.tanksfirst[button] and name then + pfUI.uf.raid.tankrole[name] = not pfUI.uf.raid.tankrole[name] + pfUI.uf.raid:Show() + end + end) +end) \ No newline at end of file