7e742d094e
Les animations de camelot, rejouées image par image comme dans le suivi des quêtes (les groupes de 3.3.5 n'ont ni fromAlpha / toAlpha ni setToFinalAlpha), avec leurs durées et leur lissage IN : ouverture (monte de 10 et apparaît en 0,1 s), fermeture (descend de 10 et s'efface en 0,1 s), apparition des cartes (0,1 s), et glissement de 100 vers la droite en 0,3 s avec fondu sur les 0,2 dernières à chaque ramassage, clic compris (camelot : butin automatique seulement). À la fermeture, toute animation en cours s'arrête et chaque carte reprend sa place : rien ne reste à l'écran quand le dernier objet ferme le butin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
535 lines
19 KiB
Lua
535 lines
19 KiB
Lua
-- ForeverUI: the loot window with camelot's LootFrame (blizzard_uipanels_game/mainline/
|
|
-- lootframe.xml / .lua, scrollingflatpanel.xml / .lua): a flat panel titled ITEMS, 220 wide and
|
|
-- 290 high at most, listing one card per loot slot (looting_itemcard_*: background tinted by the
|
|
-- quality, stroke, rarity tag with the quality name, 37 x 37 item button, name).
|
|
-- The client's LootFrame stays the host: its events, the master loot list (anchored to the
|
|
-- button named in LootFrame.selectedLootButton) and CloseLoot when it hides. It stays shown while
|
|
-- the loot is open, invisible and mouse-less; our panel lives on UIParent and follows it.
|
|
-- 3.3.5 does not tell quest items (no quest border) nor currencies.
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
|
|
local Tpl = ForeverUI.Templates
|
|
|
|
local P = {}
|
|
ForeverUI.Loot = P
|
|
|
|
local N = {
|
|
width = 220, maxHeight = 290, barWidth = 16, -- panelWidth, panelMaxHeight, ScrollBarWidth
|
|
anchors = 26, extra = 20, -- ScrollingFlatPanelMixin:Resize
|
|
list = { 4, -22, 4 }, -- ScrollBox: TOPLEFT (4, -22), BOTTOM (0, 4)
|
|
pad = 6, spacing = 2, card = 46, -- ScrollBoxPad, ScrollBoxSpacing, element height
|
|
bar = { -16, -28, 6 }, -- ScrollBar at the panel's TOPRIGHT / BOTTOMRIGHT
|
|
background = { 6, -20, -2, 2 }, -- DefaultPanelFlatTemplate Bg
|
|
title = { 30, -1, -24, 20, -5 }, -- TitleContainer, its text at TOP (0, -5)
|
|
item = { 5, -4, 37, -157 }, -- ItemButton TOPLEFT (5, -4), hit rect to the right
|
|
stripe = { 100, 13 }, -- QualityStripe, TOPRIGHT
|
|
quality = { -4, -2 }, -- QualityText, TOPRIGHT
|
|
text = { 8, -8, 150, 30 }, -- Text at the item's TOPRIGHT
|
|
money = { 8, 0, 93, 38 }, -- money Text at the item's RIGHT
|
|
highlight = 0.7, -- HighlightNameFrame alpha
|
|
shadow = { 30, 0.2 }, -- ScrollBox shadows: 30 in from each side, scale 0.2
|
|
cursor = { -30, 50, 350 }, -- lootUnderMouse: x - 30, max(y + 50, 350)
|
|
default = { 16, -116 }, -- EditModePresetLayouts: TOPLEFT of UIParent
|
|
}
|
|
|
|
-- GameFontWhiteTiny2 (SystemFont_Tiny2: FRIZQT 8, white), missing in 3.3.5
|
|
local TINY = CreateFont("ForeverUIFontWhiteTiny2")
|
|
TINY:SetFont("Fonts" .. string.char(92) .. "FRIZQT__.TTF", 8, "")
|
|
TINY:SetTextColor(1, 1, 1)
|
|
|
|
local COMMON = 1
|
|
|
|
local function atlas(t, name)
|
|
return ForeverUI.SetAtlas(t, name, true)
|
|
end
|
|
|
|
-- ------------------------------------------------------------ animations
|
|
|
|
-- camelot's animation groups, replayed by hand as in ObjectiveTracker.lua: 3.3.5 groups have no
|
|
-- fromAlpha / toAlpha nor setToFinalAlpha. One step per frame (the panel, a card): a new step
|
|
-- replaces the running one, stop drops it. fn(progress) runs each frame; finish once done,
|
|
-- after the loop.
|
|
local A = {
|
|
open = 0.1, close = 0.1, drop = 10, -- ScrollingFlatPanel ShowAnim / HideAnim
|
|
card = 0.1, -- element ShowAnim
|
|
slide = 0.3, slideX = 100, fade = 0.2, fadeDelay = 0.1, -- SlideOutRightAnim
|
|
}
|
|
local steps = {}
|
|
local runner = CreateFrame("Frame")
|
|
runner:Hide()
|
|
runner:SetScript("OnUpdate", function(self, elapsed)
|
|
local finished
|
|
for key, e in pairs(steps) do
|
|
e.t = e.t + elapsed
|
|
if e.t >= e.delay then
|
|
local p = (e.duration > 0) and math.min(1, (e.t - e.delay) / e.duration) or 1
|
|
e.fn(p)
|
|
if p >= 1 then
|
|
steps[key] = nil
|
|
if e.finish then
|
|
finished = finished or {}
|
|
table.insert(finished, e.finish)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if finished then
|
|
for _, finish in ipairs(finished) do finish() end
|
|
end
|
|
if not next(steps) then self:Hide() end
|
|
end)
|
|
P.steps, P.runner = steps, runner
|
|
|
|
local function play(key, duration, fn, finish)
|
|
steps[key] = { t = 0, delay = 0, duration = duration, fn = fn, finish = finish }
|
|
runner:Show()
|
|
end
|
|
|
|
local function stop(key)
|
|
steps[key] = nil
|
|
end
|
|
|
|
-- smoothing="IN"
|
|
local function easeIn(p) return p * p end
|
|
|
|
-- ------------------------------------------------------------ panel
|
|
|
|
local panel = CreateFrame("Frame", "ForeverUILootFrame", UIParent)
|
|
panel:SetWidth(N.width)
|
|
panel:SetHeight(N.maxHeight)
|
|
panel:SetFrameStrata("HIGH")
|
|
panel:SetToplevel(true)
|
|
panel:SetClampedToScreen(true)
|
|
panel:EnableMouse(true)
|
|
panel:Hide()
|
|
P.panel = panel
|
|
|
|
-- FlatPanelBackgroundTemplate: two 16 x 16 rounded bottom corners, the edge between them and the
|
|
-- rest flat, tinted by PANEL_BACKGROUND_COLOR
|
|
do
|
|
local B = N.background
|
|
local c = ForeverUI.PanelBackground
|
|
local bottomLeft = panel:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(bottomLeft, "uiframebackground-nineslice-cornerbottomleft")
|
|
bottomLeft:SetPoint("BOTTOMLEFT", panel, "BOTTOMLEFT", B[1], B[4])
|
|
local bottomRight = panel:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(bottomRight, "uiframebackground-nineslice-cornerbottomright")
|
|
bottomRight:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", B[3], B[4])
|
|
local edge = panel:CreateTexture(nil, "BACKGROUND")
|
|
edge:SetTexture(c[1], c[2], c[3], c[4])
|
|
edge:SetPoint("TOPLEFT", bottomLeft, "TOPRIGHT")
|
|
edge:SetPoint("BOTTOMRIGHT", bottomRight, "BOTTOMLEFT")
|
|
local body = panel:CreateTexture(nil, "BACKGROUND")
|
|
body:SetTexture(c[1], c[2], c[3], c[4])
|
|
body:SetPoint("TOPLEFT", panel, "TOPLEFT", B[1], B[2])
|
|
body:SetPoint("BOTTOMRIGHT", bottomRight, "TOPRIGHT")
|
|
for _, t in ipairs({ bottomLeft, bottomRight }) do t:SetVertexColor(c[1], c[2], c[3], c[4]) end
|
|
end
|
|
|
|
-- Bronze metal without portrait (DefaultPanelBaseTemplate's NineSlice), above the cards
|
|
local metal = CreateFrame("Frame", nil, panel)
|
|
metal:SetAllPoints(panel)
|
|
metal:SetFrameLevel(panel:GetFrameLevel() + 10)
|
|
Tpl.NineSlice(metal, "ButtonFrameTemplateNoPortrait", panel)
|
|
|
|
-- Title (TitleContainer, frame level 510 in the source: above the metal)
|
|
do
|
|
local T = N.title
|
|
local container = CreateFrame("Frame", nil, panel)
|
|
container:SetHeight(T[4])
|
|
container:SetPoint("TOPLEFT", panel, "TOPLEFT", T[1], T[2])
|
|
container:SetPoint("TOPRIGHT", panel, "TOPRIGHT", T[3], T[2])
|
|
container:SetFrameLevel(panel:GetFrameLevel() + 12)
|
|
local title = container:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
title:SetPoint("TOP", container, "TOP", 0, T[5])
|
|
title:SetPoint("LEFT", container, "LEFT")
|
|
title:SetPoint("RIGHT", container, "RIGHT")
|
|
title:SetText(ITEMS)
|
|
P.title = title
|
|
end
|
|
|
|
-- ClosePanelButton: closes the host, as 3.3.5's LootCloseButton does
|
|
local close = CreateFrame("Button", "ForeverUILootFrameCloseButton", panel)
|
|
Tpl.CloseButton(close, panel)
|
|
close:SetFrameLevel(panel:GetFrameLevel() + 12)
|
|
close:SetScript("OnClick", function() HideUIPanel(LootFrame) end)
|
|
|
|
-- ScrollBox: a clipping scroll frame; the cards are laid out in its child
|
|
local zone = CreateFrame("ScrollFrame", "ForeverUILootList", panel)
|
|
zone:SetPoint("TOPLEFT", panel, "TOPLEFT", N.list[1], N.list[2])
|
|
zone:SetPoint("BOTTOMLEFT", panel, "BOTTOMLEFT", N.list[1], N.list[3])
|
|
zone:SetWidth(N.width - N.pad)
|
|
local child = CreateFrame("Frame", nil, zone)
|
|
child:SetWidth(N.width - N.pad)
|
|
child:SetHeight(1)
|
|
zone:SetScrollChild(child)
|
|
P.zone, P.child = zone, child
|
|
|
|
local step = N.card + N.spacing
|
|
|
|
-- Upper and lower shadows, shown when there are cards beyond the view. As the ScrollBox's Shadows
|
|
-- frame: 15 levels above the cards, at scale 0.2; _looting_itemcard_shadow-center at its size,
|
|
-- 30 in from each side in that scale, the upper one flipped.
|
|
local shadows = CreateFrame("Frame", nil, zone)
|
|
shadows:SetAllPoints(zone)
|
|
shadows:SetScale(N.shadow[2])
|
|
shadows:SetFrameLevel(child:GetFrameLevel() + 15)
|
|
P.shadows = shadows
|
|
local function shadow(top)
|
|
local t = shadows:CreateTexture(nil, "OVERLAY")
|
|
local e = ForeverUI.AtlasEntry("_looting_itemcard_shadow-center")
|
|
atlas(t, "_looting_itemcard_shadow-center")
|
|
t:SetHeight(e and e[7] or 150)
|
|
if top then
|
|
if e then t:SetTexCoord(e[2], e[3], e[5], e[4]) end
|
|
t:SetPoint("TOPLEFT", shadows, "TOPLEFT", N.shadow[1], 0)
|
|
t:SetPoint("TOPRIGHT", shadows, "TOPRIGHT", -N.shadow[1], 0)
|
|
else
|
|
t:SetPoint("BOTTOMLEFT", shadows, "BOTTOMLEFT", N.shadow[1], 0)
|
|
t:SetPoint("BOTTOMRIGHT", shadows, "BOTTOMRIGHT", -N.shadow[1], 0)
|
|
end
|
|
t:Hide()
|
|
return t
|
|
end
|
|
local upperShadow, lowerShadow = shadow(true), shadow(false)
|
|
P.upperShadow, P.lowerShadow = upperShadow, lowerShadow
|
|
|
|
local bar = ForeverUI.CreateScrollBar("ForeverUILootScrollBar", panel, zone)
|
|
bar:ClearAllPoints()
|
|
bar:SetPoint("TOPLEFT", panel, "TOPRIGHT", N.bar[1], N.bar[2])
|
|
bar:SetPoint("BOTTOMLEFT", panel, "BOTTOMRIGHT", N.bar[1], N.bar[3])
|
|
bar:SetFrameLevel(panel:GetFrameLevel() + 12)
|
|
bar:Hide()
|
|
P.bar = bar
|
|
|
|
local function scrollTo(offset)
|
|
local range = math.max(0, child:GetHeight() - zone:GetHeight())
|
|
local y = math.min(offset * step, range)
|
|
zone:SetVerticalScroll(y)
|
|
Tpl.SetShown(upperShadow, y > 0)
|
|
Tpl.SetShown(lowerShadow, y < range)
|
|
end
|
|
bar.onScroll = scrollTo
|
|
|
|
zone:EnableMouseWheel(true)
|
|
zone:SetScript("OnMouseWheel", function(_, delta)
|
|
if bar:IsShown() then bar:MoveTo((bar.offset or 0) - delta) end
|
|
end)
|
|
|
|
-- ------------------------------------------------------------ cards
|
|
|
|
local cards = {}
|
|
P.cards = cards
|
|
|
|
local function quality(slot)
|
|
if LootSlotIsCoin(slot) then return COMMON end
|
|
local _, _, _, q = GetLootSlotInfo(slot)
|
|
return q or COMMON
|
|
end
|
|
|
|
local function onEnter(card)
|
|
local enabled = card.item:IsEnabled()
|
|
if not enabled or enabled == 0 then return end
|
|
card.highlight:Show()
|
|
if LootSlotIsItem(card.slot) then
|
|
GameTooltip:SetOwner(card, "ANCHOR_NONE")
|
|
GameTooltip:ClearAllPoints()
|
|
GameTooltip:SetPoint("LEFT", card, "RIGHT")
|
|
GameTooltip:SetLootItem(card.slot)
|
|
CursorUpdate(card)
|
|
end
|
|
end
|
|
|
|
local function onLeave(card)
|
|
if GameTooltip:IsOwned(card) then GameTooltip:Hide() end
|
|
ResetCursor()
|
|
card.highlight:Hide()
|
|
end
|
|
|
|
-- LootButton_OnClick: the host keeps what GroupLootDropDown and the master loot confirmation
|
|
-- read (the button name anchors the master loot list)
|
|
local function onClick(button)
|
|
local card = button.card
|
|
if IsModifiedClick() then
|
|
HandleModifiedItemClick(GetLootSlotLink(card.slot))
|
|
return
|
|
end
|
|
StaticPopup_Hide("CONFIRM_LOOT_DISTRIBUTION")
|
|
LootFrame.selectedLootButton = button:GetName()
|
|
LootFrame.selectedSlot = card.slot
|
|
LootFrame.selectedQuality = card.quality
|
|
LootFrame.selectedItemName = card.text:GetText()
|
|
LootSlot(card.slot)
|
|
end
|
|
|
|
-- the card at its place in the list, moved right by dx (slide out)
|
|
local function placeCard(card, dx)
|
|
card:ClearAllPoints()
|
|
card:SetPoint("TOPLEFT", child, "TOPLEFT", N.pad + dx, card.top)
|
|
card:SetPoint("TOPRIGHT", child, "TOPRIGHT", -N.pad + dx, card.top)
|
|
end
|
|
|
|
-- LootFrameElementTemplate (+ LootFrameItemElementTemplate's stripe and quality name)
|
|
local function createCard(i)
|
|
local card = CreateFrame("Frame", nil, child)
|
|
card:SetHeight(N.card)
|
|
card.top = -(N.pad + (i - 1) * step)
|
|
placeCard(card, 0)
|
|
local nameFrame = card:CreateTexture(nil, "BACKGROUND")
|
|
atlas(nameFrame, "looting_itemcard_bg")
|
|
nameFrame:SetAllPoints(card)
|
|
local stripe = card:CreateTexture(nil, "BACKGROUND")
|
|
atlas(stripe, "looting_raritytag_frame")
|
|
stripe:SetWidth(N.stripe[1])
|
|
stripe:SetHeight(N.stripe[2])
|
|
stripe:SetPoint("TOPRIGHT", card, "TOPRIGHT", 0, 0)
|
|
local border = card:CreateTexture(nil, "BORDER")
|
|
atlas(border, "looting_itemcard_stroke_normal")
|
|
border:SetAllPoints(card)
|
|
local highlight = card:CreateTexture(nil, "OVERLAY")
|
|
atlas(highlight, "looting_itemcard_stroke_clickstate")
|
|
highlight:SetBlendMode("ADD")
|
|
highlight:SetAlpha(N.highlight)
|
|
highlight:SetAllPoints(card)
|
|
highlight:Hide()
|
|
local pushed = card:CreateTexture(nil, "OVERLAY")
|
|
atlas(pushed, "looting_itemcard_stroke_clickstate")
|
|
pushed:SetBlendMode("ADD")
|
|
pushed:SetAllPoints(card)
|
|
pushed:Hide()
|
|
local qualityText = card:CreateFontString(nil, "ARTWORK")
|
|
qualityText:SetFontObject(TINY)
|
|
qualityText:SetJustifyH("RIGHT")
|
|
qualityText:SetPoint("TOPRIGHT", card, "TOPRIGHT", N.quality[1], N.quality[2])
|
|
local I = N.item
|
|
local item = CreateFrame("Button", "ForeverUILootItem" .. i, card, "ItemButtonTemplate")
|
|
item:SetWidth(I[3])
|
|
item:SetHeight(I[3])
|
|
item:SetPoint("TOPLEFT", card, "TOPLEFT", I[1], I[2])
|
|
item:SetHitRectInsets(0, I[4], 0, 0)
|
|
item:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
|
item.card = card
|
|
local text = card:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
text:SetJustifyH("LEFT")
|
|
text:SetJustifyV("TOP")
|
|
card.item, card.text, card.qualityText, card.stripe = item, text, qualityText, stripe
|
|
card.nameFrame, card.highlight, card.pushed = nameFrame, highlight, pushed
|
|
item:SetScript("OnEnter", function() onEnter(card) end)
|
|
item:SetScript("OnLeave", function() onLeave(card) end)
|
|
item:SetScript("OnMouseDown", function() pushed:Show() end)
|
|
item:SetScript("OnMouseUp", function() pushed:Hide() end)
|
|
item:SetScript("OnUpdate", function()
|
|
if GameTooltip:IsOwned(card) then onEnter(card) end
|
|
CursorOnUpdate(card)
|
|
end)
|
|
item:SetScript("OnClick", onClick)
|
|
return card
|
|
end
|
|
|
|
-- LootFrameElementMixin:Init on the card of slot; money and empty slots as camelot's element
|
|
-- factory sorts them
|
|
local function fill(card, slot)
|
|
card.slot = slot
|
|
stop(card)
|
|
placeCard(card, 0)
|
|
card:SetAlpha(1)
|
|
local isCoin, isItem = LootSlotIsCoin(slot), LootSlotIsItem(slot)
|
|
if not (isCoin or isItem) then
|
|
card:Hide()
|
|
return
|
|
end
|
|
local texture, name, quantity, _, locked = GetLootSlotInfo(slot)
|
|
local q = quality(slot)
|
|
card.quality = q
|
|
local c = ITEM_QUALITY_COLORS[q] or ITEM_QUALITY_COLORS[COMMON]
|
|
card.text:SetText(name)
|
|
card.text:SetVertexColor(c.r, c.g, c.b)
|
|
card.nameFrame:SetVertexColor(c.r, c.g, c.b)
|
|
card.text:ClearAllPoints()
|
|
if isCoin then
|
|
local M = N.money
|
|
card.text:SetWidth(M[3])
|
|
card.text:SetHeight(M[4])
|
|
card.text:SetJustifyV("MIDDLE")
|
|
card.text:SetPoint("LEFT", card.item, "RIGHT", M[1], M[2])
|
|
card.stripe:Hide()
|
|
card.qualityText:Hide()
|
|
Tpl.QualityOutline(card.item, nil)
|
|
else
|
|
local T = N.text
|
|
card.text:SetWidth(T[3])
|
|
card.text:SetHeight(T[4])
|
|
card.text:SetJustifyV("TOP")
|
|
card.text:SetPoint("TOPLEFT", card.item, "TOPRIGHT", T[1], T[2])
|
|
card.stripe:Show()
|
|
card.qualityText:SetText(_G["ITEM_QUALITY" .. q .. "_DESC"])
|
|
card.qualityText:Show()
|
|
Tpl.QualityOutline(card.item, q)
|
|
end
|
|
local icon = _G[card.item:GetName() .. "IconTexture"]
|
|
icon:SetTexture(texture)
|
|
local r, g, b = 1, 1, 1
|
|
if locked then r, g, b = 0.9, 0, 0 end
|
|
SetItemButtonTextureVertexColor(card.item, r, g, b)
|
|
SetItemButtonNormalTextureVertexColor(card.item, r, g, b)
|
|
local count = _G[card.item:GetName() .. "Count"]
|
|
if (quantity or 0) > 1 then
|
|
count:SetText(quantity)
|
|
count:Show()
|
|
else
|
|
count:Hide()
|
|
end
|
|
card.item:Enable()
|
|
-- ShowAnim: fades in over 0.1 s
|
|
card:SetAlpha(0)
|
|
card:Show()
|
|
play(card, A.card, function(p) card:SetAlpha(easeIn(p)) end)
|
|
end
|
|
|
|
-- SlideOutRightAnim, on every looted slot: 100 right over 0.3 s, fading out over the last 0.2 s,
|
|
-- then the card hides
|
|
local function slideOut(card)
|
|
if GameTooltip:IsOwned(card) then GameTooltip:Hide() end
|
|
card.highlight:Hide()
|
|
card.pushed:Hide()
|
|
card.item:Disable()
|
|
card:SetAlpha(1)
|
|
play(card, A.slide, function(p)
|
|
placeCard(card, A.slideX * easeIn(p))
|
|
local f = math.min(1, math.max(0, (p * A.slide - A.fadeDelay) / A.fade))
|
|
card:SetAlpha(1 - easeIn(f))
|
|
end, function()
|
|
card:Hide()
|
|
placeCard(card, 0)
|
|
card:SetAlpha(1)
|
|
end)
|
|
end
|
|
|
|
-- ScrollingFlatPanelMixin:Resize: the panel fits its cards up to 290, wider by the bar when
|
|
-- they do not fit
|
|
local function resize(n)
|
|
local cardsHeight = math.max(0, n * N.card + (n - 1) * N.spacing)
|
|
local height = math.min(cardsHeight + N.anchors + N.extra, N.maxHeight)
|
|
panel:SetHeight(height)
|
|
child:SetHeight(math.max(1, cardsHeight + 2 * N.pad))
|
|
local view = height - N.anchors
|
|
local hasBar = cardsHeight + 2 * N.pad > view
|
|
panel:SetWidth(N.width + (hasBar and N.barWidth or 0))
|
|
Tpl.SetShown(bar, hasBar)
|
|
local visibleCount = math.floor((view - N.pad) / step)
|
|
bar:Configure(n, visibleCount, 0)
|
|
scrollTo(0)
|
|
end
|
|
|
|
-- the panel at its place (P.anchor), moved down by dy
|
|
local function placePanel(dy)
|
|
local a = P.anchor
|
|
panel:ClearAllPoints()
|
|
panel:SetPoint(a[1], a[2], a[3], a[4], a[5] + dy)
|
|
end
|
|
|
|
function P.Open()
|
|
local n = GetNumLootItems() or 0
|
|
for i = 1, n do
|
|
cards[i] = cards[i] or createCard(i)
|
|
fill(cards[i], i)
|
|
end
|
|
for i = n + 1, #cards do cards[i]:Hide() end
|
|
resize(n)
|
|
if GetCVar("lootUnderMouse") == "1" then
|
|
local C = N.cursor
|
|
local x, y = GetCursorPosition()
|
|
local scale = panel:GetEffectiveScale()
|
|
P.anchor = { "TOPLEFT", nil, "BOTTOMLEFT", x / scale + C[1], math.max(y / scale + C[2], C[3]) }
|
|
else
|
|
P.anchor = { "TOPLEFT", UIParent, "TOPLEFT", N.default[1], N.default[2] }
|
|
end
|
|
-- ShowAnim played backwards: comes up 10 and fades in over 0.1 s
|
|
stop(panel)
|
|
placePanel(-A.drop)
|
|
panel:SetAlpha(0)
|
|
panel:Show()
|
|
panel:Raise()
|
|
play(panel, A.open, function(p)
|
|
local back = easeIn(1 - p)
|
|
placePanel(-A.drop * back)
|
|
panel:SetAlpha(1 - back)
|
|
end)
|
|
end
|
|
|
|
-- HideAnim: goes down 10 and fades out over 0.1 s, then the panel hides (and its OnHide stops
|
|
-- every animation, so nothing stays on screen when the last item closes the loot)
|
|
function P.Close()
|
|
if GameTooltip:GetOwner() and GameTooltip:GetOwner():GetParent() == child then GameTooltip:Hide() end
|
|
if not panel:IsShown() then return end
|
|
play(panel, A.close, function(p)
|
|
local e = easeIn(p)
|
|
placePanel(-A.drop * e)
|
|
panel:SetAlpha(1 - e)
|
|
end, function() panel:Hide() end)
|
|
end
|
|
|
|
panel:SetScript("OnHide", function()
|
|
stop(panel)
|
|
panel:SetAlpha(1)
|
|
if P.anchor then placePanel(0) end
|
|
for _, card in ipairs(cards) do
|
|
stop(card)
|
|
placeCard(card, 0)
|
|
card:SetAlpha(1)
|
|
end
|
|
end)
|
|
|
|
-- ------------------------------------------------------------ host
|
|
|
|
-- The client's LootFrame: invisible and mouse-less, shown while the loot is open
|
|
local function suppress()
|
|
LootFrame:SetAlpha(0)
|
|
LootFrame:EnableMouse(false)
|
|
for _, name in ipairs({ "LootCloseButton", "LootFrameUpButton", "LootFrameDownButton" }) do
|
|
local b = _G[name]
|
|
if b then b:EnableMouse(false) end
|
|
end
|
|
for i = 1, LOOTFRAME_NUMBUTTONS or 4 do
|
|
local b = _G["LootButton" .. i]
|
|
if b then b:EnableMouse(false) end
|
|
end
|
|
end
|
|
|
|
if LootFrame then
|
|
suppress()
|
|
hooksecurefunc("LootFrame_Show", P.Open)
|
|
LootFrame:HookScript("OnHide", P.Close)
|
|
end
|
|
|
|
-- The master loot list: GetMasterLootCandidate gives no name until the name cache has it (often
|
|
-- the master looter's own); the client then queries it and sends UPDATE_MASTER_LOOT_LIST, which
|
|
-- 3.3.5's LootFrame answers with UIDropDownMenu_Refresh, which does not rebuild the list. Opened,
|
|
-- the list is opened again, so it lists the candidates whose names came.
|
|
local function reopenMasterLootList()
|
|
if GroupLootDropDown and UIDROPDOWNMENU_OPEN_MENU == GroupLootDropDown and DropDownList1:IsShown() then
|
|
CloseDropDownMenus()
|
|
ToggleDropDownMenu(1, nil, GroupLootDropDown, LootFrame.selectedLootButton, 0, 0)
|
|
end
|
|
end
|
|
|
|
local events = CreateFrame("Frame")
|
|
events:RegisterEvent("LOOT_SLOT_CLEARED")
|
|
events:RegisterEvent("LOOT_SLOT_CHANGED")
|
|
events:RegisterEvent("UPDATE_MASTER_LOOT_LIST")
|
|
events:SetScript("OnEvent", function(_, event, slot)
|
|
if event == "UPDATE_MASTER_LOOT_LIST" then
|
|
reopenMasterLootList()
|
|
return
|
|
end
|
|
local card = slot and cards[slot]
|
|
if not (card and panel:IsShown()) then return end
|
|
if event == "LOOT_SLOT_CLEARED" then
|
|
slideOut(card)
|
|
else
|
|
fill(card, slot)
|
|
end
|
|
end)
|