d110de1cbe
- 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>
750 lines
29 KiB
Lua
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)
|