Files
moon-well-ui/data/addon/ForeverUI/SocialRaid.lua
T
Adrestie d110de1cbe Chat et barres d'auras a la DA de camelot ; plus aucune globale du client ecrite par l'addon
- Chat : cadre, barre et bouton de retour, colonne, bouton d'amis de camelot ;
  au repos fond, onglets, colonne et saisie s'effacent, allumage au survol
- Barres d'ameliorations et d'affaiblissements (Buffs.lua) : grille, bordures
  par type, clignotement, repli, regroupement, enchantements d'arme
- Souillure : StaticPopupDialogs n'est plus reaffectee (BindEnchant bloque) ;
  menus deroulants, suivi des objectifs, choix d'icone, ajout de canal et
  carte du monde n'ecrivent plus les globales du client ; menus d'unite :
  lignes protegees servies par des boutons securises hors combat, grisees en
  combat ; seule MAP_QUEST_DIFFICULTY reste, par decision
- Gestionnaire d'ensembles : plus d'appel GetEquipmentSetIconInfo(nil)
- Banc : garde des globales ecrites (bytecode Lua 5.1), faux client durci

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 18:36:19 +02:00

750 lines
29 KiB
Lua

-- ForeverUI : la page "Raid" de la fenetre Social (onglet 5 du client).
--
-- RELEVE -- RaidFrame.lua et .xml, Blizzard_RaidUI.lua et .xml (chargement a
-- la demande), du client :
-- hors raid RAID_DESCRIPTION (GameFontNormal, 300 de large), puis
-- RAID_BROWSER_DESCRIPTION (GameFontHighlight, centre) et
-- OPEN_RAID_BROWSER (260 x 22) : ShowUIPanel(LFRParentFrame) ;
-- CONVERT_TO_RAID (115) : ConvertToRaid, actif si GetPartyMember(1)
-- et IsPartyLeader et niveau >= 10 et pas HasLFGRestrictions ;
-- RAID_INFO (90) : les instances sauvegardees
-- en raid LOOKING_FOR_RAID (le navigateur de raid), READY_CHECK (chef ou
-- officier : DoReadyCheck), RAID_INFO ; huit groupes de cinq,
-- "Group N" au-dessus, "Empty" pour une place libre ; nom teinte
-- de sa classe (rouge mort, gris hors ligne), niveau, classe ;
-- icone de chef / d'assistant ; clic droit : UnitPopup "RAID" ;
-- glisser (chef ou officier) : SwapRaidSubgroup vers une place
-- occupee, SetRaidSubgroup vers une place vide
-- instances RequestRaidInfo a l'ouverture ; instanceName, instanceID,
-- instanceReset, instanceDifficulty, locked, extended,
-- instanceIDMostSig, isRaid, maxPlayers, difficultyName =
-- GetSavedInstanceInfo(i) ; INSTANCE et LOCK_EXPIRE ; reset par
-- SecondsToTime(reset, true, nil, 3), ou "Expired" en gris ;
-- EXTENDED ; le bouton EXTEND_RAID_LOCK / UNEXTEND_RAID_LOCK /
-- REACTIVATE_RAID_LOCK : SetSavedInstanceExtend(i, prolonger)
--
-- ECARTS, ASSUMES :
-- * LES PLACES NE SONT PAS SECURISEES. Celles de Blizzard_RaidUI heritent de
-- SecureUnitButtonTemplate : un clic gauche cible le joueur. Des cadres
-- securises rendraient notre fenetre -- et FriendsFrame -- proteges en
-- combat : plus moyen de l'ouvrir ou de la fermer. Le clic gauche ne
-- cible donc pas ; le clic droit ouvre le menu, le glisser deplace.
-- * LE TANK ET L'ASSISTANT PRINCIPAUX, DANS LE CLIC DROIT (2026-09-26).
-- Le menu du client ne propose RAID_MAINTANK / RAID_MAINASSIST que si
-- issecure() -- un menu ouvert par un addon ne l'est jamais -- et sa
-- ligne RAID_DEMOTE, qui retire ces roles, appelle ClearPartyAssignment,
-- protege : bloquee aussi. On ajoute donc nos deux lignes
-- "Promote to Main Tank" / "Promote to Main Assist" (SET_MAIN_TANK,
-- SET_MAIN_ASSIST) avant Cancel, et sur elles -- comme sur la ligne
-- Demote d'un tank ou assistant principal -- un bouton SECURISE du
-- client pose par-dessus (SecureActionButtonTemplate, actions
-- "maintank" / "mainassist" de SecureTemplates.lua : set, clear). Ces
-- boutons sont fils d'UIParent (un enfant securise protegerait la
-- fenetre en combat), poses hors combat seulement, caches a la
-- fermeture du menu et a l'entree en combat : en combat, les lignes
-- ne sont pas proposees.
-- * AJOUTES LE 2026-09-26 (suite du chantier) : l'etat de l'appel sur les
-- places (GetReadyCheckStatus : pret, pas pret, en attente, puis absent
-- a la fin de l'appel), les icones de tank et d'assistant principal et
-- de maitre du butin (10e et 11e valeurs de GetRaidRosterInfo), la
-- rangee des classes -- en BAS de la page, la colonne de WotLK n'ayant
-- pas la place a droite -- avec son compte et son infobulle, et les
-- fenetres detachees : on les demande aux fonctions de Blizzard_RaidUI
-- (RaidPullout_GeneratePulloutFrame, RaidPulloutButton_OnDragStart,
-- RaidPulloutStopMoving), comme ses propres boutons. Glisser un groupe
-- (son intitule), une classe, ou un joueur (Maj + glisser pour le chef
-- et les officiers, glisser simple pour les autres) les detache.
local ForeverUI = ForeverUI or {}
_G.ForeverUI = ForeverUI
local S = ForeverUI.Social
if not S or not S.inscrirePage then
return
end
local R = {}
S.Raid = R
local SEP = string.char(92)
local txt = S.txt
local P = {
encadreY1 = -60, encadreBas = 30,
icone = 10, iconePas = 11, appel = 12,
classeCote = 18, classePas = 26, classeX = 22, classeBas = 10,
tank = "Interface" .. SEP .. "GroupFrame" .. SEP .. "UI-Group-MainTankIcon",
assistantPrincipal = "Interface" .. SEP .. "GroupFrame" .. SEP .. "UI-Group-MainAssistIcon",
butin = "Interface" .. SEP .. "GroupFrame" .. SEP .. "UI-Group-MasterLooter",
classes = "Interface" .. SEP .. "Glues" .. SEP .. "CharacterCreate" .. SEP .. "UI-CharacterCreate-Classes",
pets = "Interface" .. SEP .. "RaidFrame" .. SEP .. "UI-RaidFrame-Pets",
mt = "Interface" .. SEP .. "RaidFrame" .. SEP .. "UI-RaidFrame-MainTank",
ma = "Interface" .. SEP .. "RaidFrame" .. SEP .. "UI-RaidFrame-MainAssist",
surbrillanceCarre = "Interface" .. SEP .. "Buttons" .. SEP .. "ButtonHilight-Square",
pret = "Interface" .. SEP .. "RaidFrame" .. SEP .. "ReadyCheck-Ready",
pasPret = "Interface" .. SEP .. "RaidFrame" .. SEP .. "ReadyCheck-NotReady",
attente = "Interface" .. SEP .. "RaidFrame" .. SEP .. "ReadyCheck-Waiting",
finAppel = 10,
groupesX = 8, groupesY = -64, groupeL = 180, groupeEcartX = 4, groupeH = 66, groupeEcartY = 16,
placeH = 12, etiquetteH = 12,
chef = "Interface" .. SEP .. "GroupFrame" .. SEP .. "UI-Group-LeaderIcon",
assistant = "Interface" .. SEP .. "GroupFrame" .. SEP .. "UI-Group-AssistantIcon",
surbrillance = "Interface" .. SEP .. "QuestFrame" .. SEP .. "UI-QuestTitleHighlight",
boutonY = -34,
}
local function actif(b, oui)
if b.Activer then b:Activer(oui) elseif oui then b:Enable() else b:Disable() end
end
local function enRaid()
return (GetNumRaidMembers() or 0) > 0
end
local function chefOuOfficier()
return (IsRaidLeader and IsRaidLeader()) or (IsRaidOfficer and IsRaidOfficer())
end
-- LES FENETRES DETACHEES : celles de Blizzard_RaidUI, chargees avec le raid.
local function detacher(filtre, classe)
if not (RaidPullout_GeneratePulloutFrame and RaidPulloutButton_OnDragStart) then return false end
local f = RaidPullout_GeneratePulloutFrame(filtre, classe)
if not f then return false end
RaidPulloutButton_OnDragStart(f)
-- LE CLIENT LA POSE LOIN DE LA SOURIS sur un ecran large : il convertit
-- le curseur par GetScreenWidthScale (= largeur / 1024), qui ne vaut
-- que pour le 4:3. La conversion juste est l'echelle effective de la
-- fenetre ; on la repose, en gardant le glisser deja commence.
local x, y = GetCursorPosition()
local e = f:GetEffectiveScale()
if e and e > 0 then
f:ClearAllPoints()
f:SetPoint("TOP", UIParent, "BOTTOMLEFT", x / e, y / e)
end
return true
end
local function lacher()
if RaidPulloutStopMoving then RaidPulloutStopMoving() end
end
-- ------------------------------------------------------------ les groupes
-- place[g][n] : la n-ieme place du groupe g ; son .membre est l'index de raid
local function creerPlace(parent, g, n)
local b = CreateFrame("Button", "ForeverUIRaidSlot" .. g .. "_" .. n, parent)
b:SetHeight(P.placeH)
b:RegisterForClicks("LeftButtonUp", "RightButtonUp")
b:RegisterForDrag("LeftButton")
b.groupe = g
-- rang, role, butin : trois icones qui s'enchainent depuis la gauche
b.icones = {}
for k = 1, 3 do
local t = b:CreateTexture(nil, "ARTWORK")
t:SetWidth(P.icone)
t:SetHeight(P.icone)
t:SetPoint("LEFT", b, "LEFT", 2 + (k - 1) * P.iconePas, 0)
t:Hide()
b.icones[k] = t
end
b.rang = b.icones[1]
b.appel = b:CreateTexture(nil, "OVERLAY")
b.appel:SetWidth(P.appel)
b.appel:SetHeight(P.appel)
b.appel:SetPoint("RIGHT", b, "RIGHT", -2, 0)
b.appel:Hide()
b.nom = b:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
b.nom:SetPoint("LEFT", b, "LEFT", 14, 0)
b.nom:SetWidth(90)
b.nom:SetJustifyH("LEFT")
b.niveau = b:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
b.niveau:SetPoint("LEFT", b.nom, "RIGHT", 2, 0)
b.niveau:SetWidth(20)
b.classe = b:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
b.classe:SetPoint("LEFT", b.niveau, "RIGHT", 2, 0)
b.classe:SetPoint("RIGHT", b, "RIGHT", -2, 0)
b.classe:SetJustifyH("LEFT")
b:SetHighlightTexture(P.surbrillance)
local s = b:GetHighlightTexture()
if s then s:SetBlendMode("ADD") end
b:SetScript("OnClick", function(self, bouton)
if bouton == "RightButton" and self.membre then
-- RaidGroupButton_ShowMenu, sur notre menu
local d = R.menu
d.name = GetRaidRosterInfo(self.membre)
d.id = self.membre
d.unit = "raid" .. self.membre
ForeverUI.MenuUnite.ouvrir(ToggleDropDownMenu, 1, nil, d, "cursor")
end
end)
b:SetScript("OnDragStart", function(self)
if not self.membre then return end
-- le glisser de WotLK : Maj (chef, officier) ou toujours (les autres)
-- detache la fenetre du joueur ; sinon on deplace
if not chefOuOfficier() or IsShiftKeyDown() then
self.detache = detacher(GetRaidRosterInfo(self.membre), nil)
return
end
R.glisse = self
R.fantome.texte:SetText(self.nom:GetText())
R.fantome:Show()
end)
b:SetScript("OnDragStop", function(self)
if self.detache then
self.detache = nil
lacher()
return
end
R.fantome:Hide()
local source = R.glisse
R.glisse = nil
if not source or not source.membre then return end
-- RaidGroupButton_OnDragStop : la place sous la souris
for gg = 1, 8 do
for nn = 1, 5 do
local cible = R.places[gg][nn]
if cible ~= source and MouseIsOver(cible) then
if cible.membre then
SwapRaidSubgroup(source.membre, cible.membre)
else
SetRaidSubgroup(source.membre, gg)
end
return
end
end
end
end)
return b
end
local function viderPlace(b)
b.membre = nil
for _, t in ipairs(b.icones) do t:Hide() end
b.appel:Hide()
b.nom:ClearAllPoints()
b.nom:SetPoint("LEFT", b, "LEFT", 14, 0)
b.nom:SetText(txt("EMPTY"))
b.nom:SetTextColor(0.35, 0.35, 0.35)
b.niveau:SetText("")
b.classe:SetText("")
end
-- RaidGroupFrame_Update, pour nos places
local function majGroupes()
local rempli = {}
for g = 1, 8 do
rempli[g] = 0
for n = 1, 5 do viderPlace(R.places[g][n]) end
end
for i = 1, (GetNumRaidMembers() or 0) do
local nom, rang, groupe, niveau, classe, fichier, _, enLigne, mort = GetRaidRosterInfo(i)
if nom and groupe and groupe >= 1 and groupe <= 8 and rempli[groupe] < 5 then
rempli[groupe] = rempli[groupe] + 1
local b = R.places[groupe][rempli[groupe]]
b.membre = i
b.nom:SetText(nom)
b.niveau:SetText(niveau)
b.classe:SetText(classe)
local c = fichier and RAID_CLASS_COLORS and RAID_CLASS_COLORS[fichier] or NORMAL_FONT_COLOR
if mort then
b.nom:SetTextColor(1, 0, 0)
elseif not enLigne then
b.nom:SetTextColor(0.5, 0.5, 0.5)
else
b.nom:SetTextColor(c.r, c.g, c.b)
end
local gris = enLigne and 1 or 0.5
b.niveau:SetTextColor(gris, gris, gris)
b.classe:SetTextColor(gris, gris, gris)
-- les icones : rang, role, butin -- dans cet ordre, sans trou
local role, butin = select(10, GetRaidRosterInfo(i))
local liste = {}
if rang == 2 then liste[#liste + 1] = P.chef elseif rang == 1 then liste[#liste + 1] = P.assistant end
if role == "MAINTANK" then liste[#liste + 1] = P.tank elseif role == "MAINASSIST" then liste[#liste + 1] = P.assistantPrincipal end
if butin then liste[#liste + 1] = P.butin end
for k, t in ipairs(b.icones) do
if liste[k] then t:SetTexture(liste[k]) t:Show() else t:Hide() end
end
b.nom:ClearAllPoints()
b.nom:SetPoint("LEFT", b, "LEFT", math.max(14, 3 + #liste * P.iconePas), 0)
-- l'appel : pret, pas pret, en attente -- absent une fois fini
local etat = GetReadyCheckStatus and GetReadyCheckStatus("raid" .. i)
if etat == "ready" then
b.appel:SetTexture(READY_CHECK_READY_TEXTURE or P.pret)
b.appel:Show()
elseif etat == "notready" then
b.appel:SetTexture(READY_CHECK_NOT_READY_TEXTURE or P.pasPret)
b.appel:Show()
elseif etat == "waiting" then
b.appel:SetTexture((R.appelFini and READY_CHECK_AFK_TEXTURE) or READY_CHECK_WAITING_TEXTURE or P.attente)
b.appel:Show()
end
end
end
end
-- -------------------------------------------------------------- les classes
-- RAID_CLASS_BUTTONS : les dix classes dans l'ordre du client, puis les
-- familiers, le tank et l'assistant principaux
local function classes()
local l = {}
for _, fichier in ipairs(CLASS_SORT_ORDER or {}) do
local c = CLASS_ICON_TCOORDS and CLASS_ICON_TCOORDS[fichier]
l[#l + 1] = { fichier = fichier, icone = P.classes, coords = c,
nom = (LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE[fichier]) or fichier }
end
l[#l + 1] = { fichier = "PETS", icone = P.pets, nom = txt("PETS") }
l[#l + 1] = { fichier = "MAINTANK", icone = P.mt, nom = txt("MAINTANK") }
l[#l + 1] = { fichier = "MAINASSIST", icone = P.ma, nom = txt("MAINASSIST") }
return l
end
-- qui est dans quelle case : les membres par classe, les familiers, les
-- roles
local function membresDe(fichier)
local noms = {}
for i = 1, (GetNumRaidMembers() or 0) do
local nom, _, _, _, _, f, _, _, _, role = GetRaidRosterInfo(i)
if fichier == "PETS" then
if UnitExists and UnitExists("raidpet" .. i) then noms[#noms + 1] = UnitName("raidpet" .. i) end
elseif fichier == "MAINTANK" or fichier == "MAINASSIST" then
if role == fichier then noms[#noms + 1] = nom end
elseif f == fichier then
noms[#noms + 1] = nom
end
end
return noms
end
local function creerClasses(parent)
R.classes = {}
for k, def in ipairs(classes()) do
local b = CreateFrame("Button", "ForeverUIRaidClassButton" .. k, parent)
b:SetWidth(P.classeCote)
b:SetHeight(P.classeCote)
b:SetPoint("BOTTOMLEFT", S.cadre, "BOTTOMLEFT", P.classeX + (k - 1) * P.classePas, P.classeBas)
b.icone = b:CreateTexture(nil, "ARTWORK")
b.icone:SetAllPoints(b)
b.icone:SetTexture(def.icone)
if def.coords then b.icone:SetTexCoord(def.coords[1], def.coords[2], def.coords[3], def.coords[4]) end
b.compte = b:CreateFontString(nil, "OVERLAY", "NumberFontNormalSmall")
b.compte:SetPoint("BOTTOMRIGHT", b, "BOTTOMRIGHT", 3, -2)
b:SetHighlightTexture(P.surbrillanceCarre)
local h = b:GetHighlightTexture()
if h then h:SetBlendMode("ADD") end
b:RegisterForDrag("LeftButton")
b.def = def
-- RaidClassButton_OnEnter
b:SetScript("OnEnter", function(self)
local noms = membresDe(self.def.fichier)
GameTooltip_SetDefaultAnchor(GameTooltip, UIParent)
GameTooltip:SetText(string.format("%s%s (%d)|r", self.def.nom, NORMAL_FONT_COLOR_CODE or "|cffffd200", #noms), 1, 1, 1)
if #noms > 0 then GameTooltip:AddLine(table.concat(noms, ", "), 1, 1, 1, 1) end
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
b:SetScript("OnDragStart", function(self)
if (self.nombre or 0) > 0 then detacher(self.def.fichier, self.def.nom) end
end)
b:SetScript("OnDragStop", lacher)
R.classes[k] = b
end
end
-- RaidClassButton_Update
local function majClasses()
for _, b in ipairs(R.classes) do
local n = #membresDe(b.def.fichier)
b.nombre = n
if n > 0 then
b.icone:SetDesaturated(false)
b.icone:SetAlpha(1)
local seul = b.def.fichier == "MAINTANK" or b.def.fichier == "MAINASSIST"
b.compte:SetText(seul and "" or n)
else
b.icone:SetDesaturated(true)
b.icone:SetAlpha(0.5)
b.compte:SetText("")
end
end
end
-- ----------------------------------------------------------- les instances
local N = {}
local function creerInstances()
local a = S.creerAnnexe("ForeverUIRaidInfoFrame", 345, 270)
a.titre:SetText(txt("RAID_INFORMATION"))
N.cadre = a
local e = ForeverUI.CreateInset(a)
e:SetPoint("TOPLEFT", a, "TOPLEFT", 12, -30)
e:SetPoint("BOTTOMRIGHT", a, "BOTTOMRIGHT", -12, 42)
local h1 = S.creerEntete(e, "ForeverUIRaidInfoColumn1", 173, txt("INSTANCE"))
h1:SetPoint("TOPLEFT", e, "TOPLEFT", 4, -4)
local h2 = S.creerEntete(e, "ForeverUIRaidInfoColumn2", 140, txt("LOCK_EXPIRE"))
h2:SetPoint("LEFT", h1, "RIGHT", -1, 0)
N.liste = S.creerListe(e, "ForeverUIRaidInfoList", 30, function(l)
l.nom = l:CreateFontString(nil, "ARTWORK", "GameFontNormal")
l.nom:SetPoint("TOPLEFT", l, "TOPLEFT", 5, -3)
l.nom:SetWidth(150)
l.nom:SetJustifyH("LEFT")
l.difficulte = l:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
l.difficulte:SetPoint("TOPLEFT", l.nom, "BOTTOMLEFT", 10, -2)
l.reset = l:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
l.reset:SetPoint("TOPRIGHT", l, "TOPRIGHT", -2, -4)
l.reset:SetJustifyH("RIGHT")
l.prolonge = l:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
l.prolonge:SetPoint("TOPRIGHT", l.reset, "BOTTOMRIGHT", 0, -2)
l.prolonge:SetText(txt("EXTENDED"))
l:SetHighlightTexture(P.surbrillance)
local s = l:GetHighlightTexture()
if s then s:SetBlendMode("ADD") end
l:SetScript("OnClick", function(self)
N.choisi = self.longID
N.maj()
end)
l:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(self.nom:GetText())
GameTooltip:AddLine(self.difficulte:GetText(), 1, 1, 1)
GameTooltip:AddLine(string.format(txt("INSTANCE_ID"), self.instanceID or 0), 1, 1, 1)
GameTooltip:Show()
end)
l:SetScript("OnLeave", function() GameTooltip:Hide() end)
end, function(l, i)
local nom, id, reset, _, verrouille, prolonge, idHaut, _, _, difficulte = GetSavedInstanceInfo(i)
l.instanceID = id
l.longID = string.format("%x%x", idHaut or 0, id or 0)
l.difficulte:SetText(difficulte)
if prolonge or verrouille then
l.reset:SetText(SecondsToTime and SecondsToTime(reset or 0, true, nil, 3) or tostring(reset))
l.nom:SetText(nom)
else
l.reset:SetText("|cff808080" .. txt("RAID_INSTANCE_EXPIRES_EXPIRED") .. "|r")
l.nom:SetText("|cff808080" .. (nom or "") .. "|r")
end
if prolonge then l.prolonge:Show() else l.prolonge:Hide() end
if N.choisi == l.longID then l:LockHighlight() else l:UnlockHighlight() end
end)
N.liste:SetPoint("TOPLEFT", h1, "BOTTOMLEFT", 0, -2)
N.liste:SetPoint("BOTTOMRIGHT", e, "BOTTOMRIGHT", -18, 4)
N.prolonger = S.bouton(a, txt("EXTEND_RAID_LOCK"), 200)
N.prolonger:SetPoint("BOTTOMLEFT", a, "BOTTOMLEFT", 14, 13)
N.prolonger:SetScript("OnClick", function(self)
if N.index then
SetSavedInstanceExtend(N.index, self.prolonger)
RequestRaidInfo()
N.maj()
end
end)
local fermer = S.bouton(a, txt("CLOSE"), 90)
fermer:SetPoint("BOTTOMRIGHT", a, "BOTTOMRIGHT", -14, 13)
fermer:SetScript("OnClick", function() a:Hide() end)
a:HookScript("OnShow", function() N.maj() end)
end
-- RaidInfoFrame_Update et RaidInfoFrame_UpdateSelectedIndex
function N.maj()
if not N.cadre or not N.cadre:IsShown() then return end
local total = GetNumSavedInstances() or 0
N.liste:Maj(total)
N.index = nil
for i = 1, total do
local _, id, _, _, verrouille, prolonge, idHaut = GetSavedInstanceInfo(i)
if N.choisi and string.format("%x%x", idHaut or 0, id or 0) == N.choisi then
N.index = i
if prolonge then
N.prolonger.prolonger = false
N.prolonger:SetText(txt("UNEXTEND_RAID_LOCK"))
elseif verrouille then
N.prolonger.prolonger = true
N.prolonger:SetText(txt("EXTEND_RAID_LOCK"))
else
N.prolonger.prolonger = true
N.prolonger:SetText(txt("REACTIVATE_RAID_LOCK"))
end
end
end
actif(N.prolonger, N.index ~= nil)
end
-- ------------------------------------------------------------ la mise a jour
-- LES SURCOUCHES SECURISEES DU MENU. Elles s'effacent hors combat seulement ;
-- a l'entree en combat, l'evenement les cache avant le verrou.
function R.cacherSurcouches()
if InCombatLockdown and InCombatLockdown() then return end
for _, o in ipairs(R.surcouches or {}) do o:Hide() end
end
local function surcouche(k)
R.surcouches = R.surcouches or {}
local o = R.surcouches[k]
if o then return o end
o = CreateFrame("Button", "ForeverUIRaidMenuSecure" .. k, UIParent, "SecureActionButtonTemplate")
o:RegisterForClicks("LeftButtonUp")
o:SetHighlightTexture(P.surbrillance)
local h = o:GetHighlightTexture()
if h then h:SetBlendMode("ADD") end
o:SetScript("PostClick", function(self)
if self.apres then self.apres() end
CloseDropDownMenus()
end)
o:Hide()
R.surcouches[k] = o
return o
end
-- Le menu du clic droit : celui du client (UnitPopup "RAID"), puis nos
-- lignes securisees pour le chef et les assistants.
function R.initialiserMenu(self)
local menu = UIDROPDOWNMENU_OPEN_MENU or self
if not menu or not R.menu.name then return end
UnitPopup_ShowMenu(menu, "RAID", R.menu.unit, R.menu.name, R.menu.id)
R.cacherSurcouches()
if (UIDROPDOWNMENU_MENU_LEVEL or 1) ~= 1 or (InCombatLockdown and InCombatLockdown()) then return end
local chef = IsRaidLeader and IsRaidLeader()
local officier = IsRaidOfficer and IsRaidOfficer()
if not (chef or officier) then return end
local liste = DropDownList1
local nom, role = R.menu.name, select(10, GetRaidRosterInfo(R.menu.id))
local unite = "raid" .. R.menu.id
local k = 0
local function poser(bouton, type, action, apres)
k = k + 1
local o = surcouche(k)
o:SetAttribute("type", type)
o:SetAttribute("action", action)
o:SetAttribute("unit", unite)
o.apres = apres
o:ClearAllPoints()
o:SetAllPoints(bouton)
-- AU-DESSUS DE LA LISTE, POUR DE BON. ToggleDropDownMenu remplit la
-- liste PUIS l'affiche, et DropDownList1, toplevel, remonte alors
-- au premier plan de sa strate : un niveau pose avant ne tient pas,
-- et le clic tombait sur la ligne ordinaire (retour du 2026-09-26).
-- La strate des infobulles est au-dessus de FULLSCREEN_DIALOG.
o:SetFrameStrata("TOOLTIP")
o:Show()
end
-- la ligne Demote d'un tank ou assistant principal : le retrait du role
-- est protege ; la retrogradation d'un assistant, elle, ne l'est pas
if role == "MAINTANK" or role == "MAINASSIST" then
for i = 1, liste.numButtons do
local b = _G["DropDownList1Button" .. i]
if b and b.value == "RAID_DEMOTE" then
poser(b, string.lower(role), "clear", function()
if chef and UnitIsRaidOfficer and UnitIsRaidOfficer(unite) then DemoteAssistant(nom, 1) end
end)
end
end
end
-- Cancel s'en va le temps d'ajouter nos lignes, puis revient en dernier
local dernier = _G["DropDownList1Button" .. liste.numButtons]
local annuler = dernier and dernier.value == "CANCEL"
if annuler then liste.numButtons = liste.numButtons - 1 end
local function ligne(texte, type)
local info = UIDropDownMenu_CreateInfo()
info.text = texte
info.notCheckable = 1
info.func = function() end
UIDropDownMenu_AddButton(info, 1)
poser(_G["DropDownList1Button" .. liste.numButtons], type, "set")
end
if role ~= "MAINTANK" then ligne(txt("SET_MAIN_TANK"), "maintank") end
if role ~= "MAINASSIST" then ligne(txt("SET_MAIN_ASSIST"), "mainassist") end
if annuler then
local info = UIDropDownMenu_CreateInfo()
info.text = txt("CANCEL")
info.value = "CANCEL"
info.owner = "RAID"
info.notCheckable = 1
info.func = UnitPopup_OnClick
UIDropDownMenu_AddButton(info, 1)
end
end
function R.maj()
if not R.hors then return end
local raid = enRaid()
if raid then
R.hors:Hide()
R.groupes:Show()
majGroupes()
majClasses()
R.convertir:Hide()
R.navigateur:Show()
if chefOuOfficier() then R.appel:Show() else R.appel:Hide() end
else
R.hors:Show()
R.groupes:Hide()
R.convertir:Show()
R.navigateur:Hide()
R.appel:Hide()
local ok = GetPartyMember and GetPartyMember(1) and IsPartyLeader() and UnitLevel("player") >= 10
and not (HasLFGRestrictions and HasLFGRestrictions())
actif(R.convertir, ok and true or false)
end
-- le bouton des instances suit la liste (UPDATE_INSTANCE_INFO)
actif(R.info, (GetNumSavedInstances() or 0) > 0)
N.maj()
end
local function construire(cadre)
-- hors raid
local hors = ForeverUI.CreateInset(cadre, "ForeverUIRaidNotInRaid")
hors:SetPoint("TOPLEFT", S.cadre, "TOPLEFT", 4, P.encadreY1)
hors:SetPoint("BOTTOMRIGHT", S.cadre, "BOTTOMRIGHT", -6, P.encadreBas)
local desc = hors:CreateFontString(nil, "ARTWORK", "GameFontNormal")
desc:SetPoint("TOPLEFT", hors, "TOPLEFT", 18, -16)
desc:SetWidth(335)
desc:SetJustifyH("LEFT")
desc:SetText(txt("RAID_DESCRIPTION"))
local nav = hors:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
nav:SetPoint("TOP", desc, "BOTTOM", 0, -30)
nav:SetWidth(335)
nav:SetText(txt("RAID_BROWSER_DESCRIPTION"))
local ouvrir = S.bouton(hors, txt("OPEN_RAID_BROWSER"), 260)
ouvrir:SetPoint("TOP", nav, "BOTTOM", 0, -10)
ouvrir:SetScript("OnClick", function()
if LFRParentFrame then ShowUIPanel(LFRParentFrame) end
end)
R.hors = hors
-- en raid : huit groupes, deux colonnes de quatre
local groupes = CreateFrame("Frame", "ForeverUIRaidGroups", cadre)
groupes:SetAllPoints(S.cadre)
R.groupes = groupes
R.places = {}
for g = 1, 8 do
local col, rangee = (g - 1) % 2, math.floor((g - 1) / 2)
local boite = ForeverUI.CreateInset(groupes, "ForeverUIRaidGroup" .. g)
boite:SetWidth(P.groupeL)
boite:SetHeight(P.groupeH)
boite:SetPoint("TOPLEFT", S.cadre, "TOPLEFT", P.groupesX + col * (P.groupeL + P.groupeEcartX),
P.groupesY - P.etiquetteH - rangee * (P.groupeH + P.groupeEcartY))
-- l'intitule : glisse, il detache la fenetre du groupe
local etiquette = CreateFrame("Button", "ForeverUIRaidGroupLabel" .. g, groupes)
etiquette:SetHeight(P.etiquetteH)
etiquette:SetWidth(80)
etiquette:SetPoint("BOTTOMLEFT", boite, "TOPLEFT", 4, 1)
local et = etiquette:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
et:SetPoint("LEFT", etiquette, "LEFT", 0, 0)
et:SetText(txt("GROUP") .. " " .. g)
etiquette.texte = et
etiquette:RegisterForDrag("LeftButton")
etiquette:SetScript("OnDragStart", function() detacher(g) end)
etiquette:SetScript("OnDragStop", lacher)
R.places[g] = {}
for n = 1, 5 do
local b = creerPlace(boite, g, n)
b:SetPoint("TOPLEFT", boite, "TOPLEFT", 3, -3 - (n - 1) * P.placeH)
b:SetPoint("TOPRIGHT", boite, "TOPRIGHT", -3, -3 - (n - 1) * P.placeH)
R.places[g][n] = b
end
end
-- le fantome qui suit la souris pendant un glisser
local f = CreateFrame("Frame", "ForeverUIRaidDragGhost", UIParent)
f:SetFrameStrata("TOOLTIP")
f:SetWidth(120)
f:SetHeight(14)
f.texte = f:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
f.texte:SetAllPoints(f)
f:Hide()
f:SetScript("OnUpdate", function(self)
local x, y = GetCursorPosition()
local e = UIParent:GetEffectiveScale()
self:ClearAllPoints()
self:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x / e, y / e)
end)
R.fantome = f
creerClasses(groupes)
-- LE MENU DU CLIC DROIT : RaidGroupButton_ShowMenu pose .initialize et
-- .displayMode, et ToggleDropDownMenu le remplit a l'ouverture. Ne pas
-- passer par UIDropDownMenu_Initialize ici : il appelle la fonction
-- aussitot, sans menu ouvert -- UnitPopup_ShowMenu(nil) a la connexion.
R.menu = CreateFrame("Frame", "ForeverUIRaidDropDown", cadre, "UIDropDownMenuTemplate")
R.menu:Hide()
R.menu.displayMode = "MENU"
R.menu.initialize = R.initialiserMenu
-- la liste se ferme : nos surcouches aussi
if DropDownList1 then DropDownList1:HookScript("OnHide", R.cacherSurcouches) end
-- les boutons du haut
R.info = S.bouton(cadre, txt("RAID_INFO"), 90)
R.info:SetPoint("TOPRIGHT", S.cadre, "TOPRIGHT", -10, P.boutonY)
R.info:SetScript("OnClick", function()
if N.cadre:IsShown() then N.cadre:Hide() else N.cadre:Show() end
end)
R.convertir = S.bouton(cadre, txt("CONVERT_TO_RAID"), 115)
R.convertir:SetPoint("RIGHT", R.info, "LEFT", -4, 0)
R.convertir:SetScript("OnClick", function() ConvertToRaid() end)
R.appel = S.bouton(cadre, txt("READY_CHECK"), 90)
R.appel:SetPoint("RIGHT", R.info, "LEFT", -2, 0)
R.appel:SetScript("OnClick", function()
DoReadyCheck()
PlaySound("UChatScrollButton")
end)
R.navigateur = S.bouton(cadre, txt("LOOKING_FOR_RAID"), 90)
R.navigateur:SetPoint("RIGHT", R.appel, "LEFT", -2, 0)
R.navigateur:SetScript("OnClick", function()
if LFRParentFrame then ShowUIPanel(LFRParentFrame) end
end)
creerInstances()
end
S.inscrirePage(5, {
construire = construire,
titre = function() return txt("RAID") end,
maj = R.maj,
-- l'OnShow de RaidFrame
montrer = function() RequestRaidInfo() end,
cacher = function()
if N.cadre then N.cadre:Hide() end
if R.fantome then R.fantome:Hide() end
end,
})
local veilleur = CreateFrame("Frame")
for _, ev in ipairs({ "RAID_ROSTER_UPDATE", "PARTY_MEMBERS_CHANGED", "PARTY_LEADER_CHANGED",
"UPDATE_INSTANCE_INFO", "UNIT_LEVEL", "UNIT_NAME_UPDATE", "UNIT_PET", "PARTY_LFG_RESTRICTED",
"READY_CHECK", "READY_CHECK_CONFIRM", "READY_CHECK_FINISHED", "PLAYER_REGEN_DISABLED",
"PLAYER_REGEN_ENABLED" }) do
veilleur:RegisterEvent(ev)
end
-- LA FIN DE L'APPEL : RaidGroupFrame_ReadyCheckFinished passe les absents en
-- "AFK" puis efface les icones ; ici apres P.finAppel secondes.
local minuterie = CreateFrame("Frame")
minuterie:Hide()
minuterie:SetScript("OnUpdate", function(self, ecoule)
self.reste = (self.reste or 0) - (ecoule or 0)
if self.reste <= 0 then
self:Hide()
R.appelFini = nil
if R.places then
for g = 1, 8 do for n = 1, 5 do R.places[g][n].appel:Hide() end end
end
end
end)
R.minuterie = minuterie
veilleur:SetScript("OnEvent", function(self, ev)
-- a l'entree en combat, avant le verrou : les boutons securises s'en vont
if ev == "PLAYER_REGEN_DISABLED" then
for _, o in ipairs(R.surcouches or {}) do o:Hide() end
return
elseif ev == "PLAYER_REGEN_ENABLED" then
return
end
if ev == "READY_CHECK" then
R.appelFini = nil
minuterie:Hide()
elseif ev == "READY_CHECK_FINISHED" then
R.appelFini = true
minuterie.reste = P.finAppel
minuterie:Show()
end
if R.hors and R.hors:GetParent():IsVisible() then R.maj() end
end)