跟随版本 0.8.19

This commit is contained in:
rucky
2026-03-24 15:56:28 +08:00
parent 40d37dc8c4
commit c0f1ecc713
19 changed files with 2227 additions and 259 deletions

View File

@@ -65,11 +65,18 @@ local ICON_BACKDROP = {
insets = { left = 2, right = 2, top = 2, bottom = 2 },
}
local ITEMS_PER_PAGE = 4
local PAGE_BAR_H = 20
local lootRows = {}
local activeAlerts = {}
local alertAnchor = nil
local alertPool = {}
local origLootFrameUpdate = nil
local ShowLootPage
local HideBagFullWarning
--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
@@ -224,6 +231,97 @@ local function CreateLootFrame()
closeFS:SetTextColor(0.9, 0.65, 0.65, 1)
end)
-- Page controls (visible only when > ITEMS_PER_PAGE items)
local pageBar = CreateFrame("Frame", nil, lootFrame)
pageBar:SetHeight(PAGE_BAR_H)
pageBar:SetPoint("BOTTOMLEFT", lootFrame, "BOTTOMLEFT", 7, 4)
pageBar:SetPoint("BOTTOMRIGHT", lootFrame, "BOTTOMRIGHT", -7, 4)
pageBar:SetFrameLevel(lootFrame:GetFrameLevel() + 3)
pageBar:EnableMouse(false)
pageBar:Hide()
lootFrame._pageBar = pageBar
local pageFS = pageBar:CreateFontString(nil, "OVERLAY")
pageFS:SetFont(Font(), 9, "OUTLINE")
pageFS:SetPoint("CENTER", pageBar, "CENTER", 0, 0)
pageFS:SetTextColor(0.75, 0.75, 0.80, 0.95)
lootFrame._pageText = pageFS
local dim = th.dimText or { 0.55, 0.55, 0.60 }
local prevBtn = CreateFrame("Button", nil, pageBar)
prevBtn:SetWidth(22)
prevBtn:SetHeight(16)
prevBtn:SetPoint("RIGHT", pageFS, "LEFT", -8, 0)
prevBtn:SetFrameLevel(pageBar:GetFrameLevel() + 1)
prevBtn:RegisterForClicks("LeftButtonUp")
prevBtn:SetBackdrop(ROUND_BACKDROP_SMALL)
prevBtn:SetBackdropColor(0.10, 0.09, 0.14, 0.80)
prevBtn:SetBackdropBorderColor(0.25, 0.22, 0.35, 0.60)
local prevFS2 = prevBtn:CreateFontString(nil, "OVERLAY")
prevFS2:SetFont(Font(), 10, "OUTLINE")
prevFS2:SetPoint("CENTER", 0, 0)
prevFS2:SetText("<")
prevFS2:SetTextColor(dim[1], dim[2], dim[3], 0.90)
prevBtn:SetScript("OnClick", function()
if lootFrame._page and lootFrame._page > 1 then
lootFrame._page = lootFrame._page - 1
ShowLootPage()
end
end)
prevBtn:SetScript("OnEnter", function()
this:SetBackdropBorderColor(acc[1], acc[2], acc[3], 0.70)
end)
prevBtn:SetScript("OnLeave", function()
this:SetBackdropBorderColor(0.25, 0.22, 0.35, 0.60)
end)
lootFrame._prevBtn = prevBtn
local nextBtn = CreateFrame("Button", nil, pageBar)
nextBtn:SetWidth(22)
nextBtn:SetHeight(16)
nextBtn:SetPoint("LEFT", pageFS, "RIGHT", 8, 0)
nextBtn:SetFrameLevel(pageBar:GetFrameLevel() + 1)
nextBtn:RegisterForClicks("LeftButtonUp")
nextBtn:SetBackdrop(ROUND_BACKDROP_SMALL)
nextBtn:SetBackdropColor(0.10, 0.09, 0.14, 0.80)
nextBtn:SetBackdropBorderColor(0.25, 0.22, 0.35, 0.60)
local nextFS2 = nextBtn:CreateFontString(nil, "OVERLAY")
nextFS2:SetFont(Font(), 10, "OUTLINE")
nextFS2:SetPoint("CENTER", 0, 0)
nextFS2:SetText(">")
nextFS2:SetTextColor(dim[1], dim[2], dim[3], 0.90)
nextBtn:SetScript("OnClick", function()
if lootFrame._page and lootFrame._totalPages and lootFrame._page < lootFrame._totalPages then
lootFrame._page = lootFrame._page + 1
ShowLootPage()
end
end)
nextBtn:SetScript("OnEnter", function()
this:SetBackdropBorderColor(acc[1], acc[2], acc[3], 0.70)
end)
nextBtn:SetScript("OnLeave", function()
this:SetBackdropBorderColor(0.25, 0.22, 0.35, 0.60)
end)
lootFrame._nextBtn = nextBtn
-- Bag-full warning (hidden by default)
local bagFullFS = lootFrame:CreateFontString(nil, "OVERLAY")
bagFullFS:SetFont(Font(), 9, "OUTLINE")
bagFullFS:SetPoint("LEFT", titleFS, "RIGHT", 6, 0)
bagFullFS:SetTextColor(1.0, 0.30, 0.30, 1.0)
bagFullFS:Hide()
lootFrame._bagFullText = bagFullFS
-- Escape key closes our loot frame
table.insert(UISpecialFrames, "NanamiLootFrame")
lootFrame:SetScript("OnHide", function()
if not this._closingLoot then
CloseLoot()
end
end)
lootFrame:Hide()
return lootFrame
end
@@ -302,7 +400,166 @@ local function CreateLootRow(parent, index)
end
--------------------------------------------------------------------------------
-- Update loot frame
-- Bag-full warning helpers
--------------------------------------------------------------------------------
local function ShowBagFullWarning()
if not lootFrame or not lootFrame:IsShown() then return end
if lootFrame._bagFullText then
lootFrame._bagFullText:SetText("背包已满")
lootFrame._bagFullText:Show()
end
end
HideBagFullWarning = function()
if lootFrame and lootFrame._bagFullText then
lootFrame._bagFullText:Hide()
end
end
--------------------------------------------------------------------------------
-- Show current page
--------------------------------------------------------------------------------
ShowLootPage = function()
if not lootFrame then return end
local numItems = lootFrame._numItems or 0
local page = lootFrame._page or 1
local totalPages = lootFrame._totalPages or 1
local startSlot = (page - 1) * ITEMS_PER_PAGE + 1
local endSlot = startSlot + ITEMS_PER_PAGE - 1
if endSlot > numItems then endSlot = numItems end
local slotsOnPage = endSlot - startSlot + 1
if slotsOnPage < 0 then slotsOnPage = 0 end
while table.getn(lootRows) < ITEMS_PER_PAGE do
local idx = table.getn(lootRows) + 1
lootRows[idx] = CreateLootRow(lootFrame, idx)
end
for i = 1, table.getn(lootRows) do lootRows[i]:Hide() end
for i = 1, ITEMS_PER_PAGE do
local nb = _G["LootButton" .. i]
if nb then nb:Hide() end
end
local hasPages = totalPages > 1
local bottomPad = hasPages and (PAGE_BAR_H + 6) or 6
local totalH = TITLE_HEIGHT + (slotsOnPage * (ROW_HEIGHT + ROW_GAP)) + bottomPad + 4
lootFrame:SetHeight(totalH)
-- Build visual rows
for btnIdx = 1, slotsOnPage do
local slotIdx = startSlot + btnIdx - 1
local row = lootRows[btnIdx]
if not row then break end
row:ClearAllPoints()
row:SetPoint("TOPLEFT", lootFrame, "TOPLEFT", 7,
-(TITLE_HEIGHT + 2 + (btnIdx - 1) * (ROW_HEIGHT + ROW_GAP)))
row:SetWidth(ROW_WIDTH)
row.slotIndex = slotIdx
local texture, itemName, quantity, quality = GetLootSlotInfo(slotIdx)
if texture then
row.icon:SetTexture(texture)
local r, g, b = QColor(quality)
row._qualColor = { r, g, b }
row.qBar:SetVertexColor(r, g, b, 0.90)
row.iconFrame:SetBackdropBorderColor(r, g, b, 0.65)
row:SetBackdropBorderColor(r, g, b, 0.30)
row:SetBackdropColor(row._slotBg[1], row._slotBg[2], row._slotBg[3], row._slotBg[4] or 0.85)
row.iconFrame:SetAlpha(1)
row.nameFS:SetText("|cff" .. ColorHex(r, g, b) .. (itemName or "") .. "|r")
if quantity and quantity > 1 then
row.countFS:SetText(tostring(quantity))
else
row.countFS:SetText("")
end
else
row._qualColor = nil
row.icon:SetTexture("")
row.iconFrame:SetAlpha(0.25)
row.qBar:SetVertexColor(0.3, 0.3, 0.3, 0.30)
row.nameFS:SetText("")
row.countFS:SetText("")
row:SetBackdropColor(0.04, 0.04, 0.06, 0.40)
row:SetBackdropBorderColor(0.12, 0.12, 0.18, 0.25)
row.iconFrame:SetBackdropBorderColor(0.15, 0.15, 0.20, 0.30)
end
row:Show()
end
-- Let the ORIGINAL Blizzard LootFrame_Update run so that native
-- LootButton1-4 get their IDs, slot data, and OnClick set up
-- through the trusted native code path (required for LootSlot).
if LootFrame then
LootFrame.page = page
if not LootFrame:IsShown() then LootFrame:Show() end
end
if origLootFrameUpdate then origLootFrameUpdate() end
-- Now reposition the native buttons on top of our visual rows
for btnIdx = 1, ITEMS_PER_PAGE do
local nb = _G["LootButton" .. btnIdx]
local row = lootRows[btnIdx]
if nb and row and row:IsShown() and row._qualColor then
nb:ClearAllPoints()
nb:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
nb:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
nb:SetFrameStrata("FULLSCREEN_DIALOG")
nb:SetFrameLevel(row:GetFrameLevel() + 10)
nb:SetAlpha(0)
nb:EnableMouse(true)
nb:Show()
nb._nanamiRow = row
nb:SetScript("OnEnter", function()
local slot = this:GetID()
if slot then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetLootItem(slot)
if CursorUpdate then CursorUpdate() end
end
local r2 = this._nanamiRow
if r2 and r2._qualColor then
local qc = r2._qualColor
r2:SetBackdropBorderColor(qc[1], qc[2], qc[3], 0.70)
r2:SetBackdropColor(qc[1]*0.15, qc[2]*0.15, qc[3]*0.15, 0.90)
end
end)
nb:SetScript("OnLeave", function()
GameTooltip:Hide()
local r2 = this._nanamiRow
if r2 then
if r2._qualColor then
local qc = r2._qualColor
r2:SetBackdropBorderColor(qc[1], qc[2], qc[3], 0.30)
else
r2:SetBackdropBorderColor(r2._slotBd[1], r2._slotBd[2],
r2._slotBd[3], r2._slotBd[4] or 0.60)
end
r2:SetBackdropColor(r2._slotBg[1], r2._slotBg[2],
r2._slotBg[3], r2._slotBg[4] or 0.85)
end
end)
else
if nb then nb:Hide() end
end
end
if hasPages then
lootFrame._pageText:SetText(page .. "/" .. totalPages)
lootFrame._pageBar:Show()
else
lootFrame._pageBar:Hide()
end
end
--------------------------------------------------------------------------------
-- Update loot frame (direct slot mapping, no compaction)
--------------------------------------------------------------------------------
local function UpdateLootFrame()
local db = GetDB()
@@ -316,124 +573,18 @@ local function UpdateLootFrame()
CreateLootFrame()
local validSlots = {}
for i = 1, numItems do
local texture, itemName, quantity, quality = GetLootSlotInfo(i)
if texture then
table.insert(validSlots, i)
end
lootFrame._numItems = numItems
lootFrame._totalPages = math.ceil(numItems / ITEMS_PER_PAGE)
if not lootFrame._page or lootFrame._page > lootFrame._totalPages then
lootFrame._page = 1
end
local numValid = table.getn(validSlots)
if numValid == 0 then
if lootFrame then lootFrame:Hide() end
return
end
local totalH = TITLE_HEIGHT + (numValid * (ROW_HEIGHT + ROW_GAP)) + 10
lootFrame:SetWidth(ROW_WIDTH + 14)
lootFrame:SetHeight(totalH)
lootFrame:SetScale(db.scale or 1.0)
while table.getn(lootRows) < numValid do
local idx = table.getn(lootRows) + 1
lootRows[idx] = CreateLootRow(lootFrame, idx)
end
ShowLootPage()
for i = 1, table.getn(lootRows) do lootRows[i]:Hide() end
for displayIdx = 1, numValid do
local slotIdx = validSlots[displayIdx]
local row = lootRows[displayIdx]
if not row then break end
row:ClearAllPoints()
row:SetPoint("TOPLEFT", lootFrame, "TOPLEFT", 7, -(TITLE_HEIGHT + 2 + (displayIdx - 1) * (ROW_HEIGHT + ROW_GAP)))
row:SetWidth(ROW_WIDTH)
local texture, itemName, quantity, quality = GetLootSlotInfo(slotIdx)
row.slotIndex = slotIdx
row.icon:SetTexture(texture or "Interface\\Icons\\INV_Misc_QuestionMark")
local r, g, b = QColor(quality)
row._qualColor = { r, g, b }
row.qBar:SetVertexColor(r, g, b, 0.90)
row.iconFrame:SetBackdropBorderColor(r, g, b, 0.65)
row:SetBackdropBorderColor(r, g, b, 0.30)
if itemName then
row.nameFS:SetText("|cff" .. ColorHex(r, g, b) .. itemName .. "|r")
else
row.nameFS:SetText("")
end
if quantity and quantity > 1 then
row.countFS:SetText(tostring(quantity))
else
row.countFS:SetText("")
end
row:Show()
-- Overlay the Blizzard LootButton on top for click handling
local maxBtns = LOOTFRAME_NUMITEMS or 4
if displayIdx <= maxBtns then
local blizzBtn = _G["LootButton" .. displayIdx]
if blizzBtn then
blizzBtn:SetID(slotIdx)
blizzBtn:SetParent(lootFrame)
blizzBtn:ClearAllPoints()
blizzBtn:SetAllPoints(row)
blizzBtn:SetFrameStrata("FULLSCREEN_DIALOG")
blizzBtn:SetFrameLevel(row:GetFrameLevel() + 10)
blizzBtn:SetAlpha(0)
blizzBtn:EnableMouse(true)
blizzBtn:Show()
local rowRef = row
blizzBtn._nanamiRow = rowRef
blizzBtn:SetScript("OnEnter", function()
local rw = this._nanamiRow
if rw and rw._acc then
rw:SetBackdropBorderColor(rw._acc[1], rw._acc[2], rw._acc[3], 0.85)
rw:SetBackdropColor(rw._hoverBd[1], rw._hoverBd[2], rw._hoverBd[3], 0.35)
end
if rw and rw.slotIndex then
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
if LootSlotIsItem(rw.slotIndex) then
GameTooltip:SetLootItem(rw.slotIndex)
else
local t, n = GetLootSlotInfo(rw.slotIndex)
if n then GameTooltip:SetText(n) end
end
GameTooltip:Show()
end
end)
blizzBtn:SetScript("OnLeave", function()
local rw = this._nanamiRow
if rw and rw._slotBg then
rw:SetBackdropColor(rw._slotBg[1], rw._slotBg[2], rw._slotBg[3], rw._slotBg[4] or 0.85)
if rw._qualColor then
rw:SetBackdropBorderColor(rw._qualColor[1], rw._qualColor[2], rw._qualColor[3], 0.35)
else
rw:SetBackdropBorderColor(rw._slotBd[1], rw._slotBd[2], rw._slotBd[3], rw._slotBd[4] or 0.60)
end
end
GameTooltip:Hide()
end)
end
end
end
-- Hide unused Blizzard buttons
local maxBtns = LOOTFRAME_NUMITEMS or 4
for i = numValid + 1, maxBtns do
local blizzBtn = _G["LootButton" .. i]
if blizzBtn then blizzBtn:Hide() end
end
-- Position: use saved mover position if exists, otherwise follow cursor
if not lootFrame._posApplied then
local hasSaved = false
if SFrames.Movers and SFrames.Movers.ApplyPosition then
@@ -455,19 +606,18 @@ local function UpdateLootFrame()
end
local function CloseLootFrame()
-- Return Blizzard buttons to LootFrame
local maxBtns = LOOTFRAME_NUMITEMS or 4
for i = 1, maxBtns do
local blizzBtn = _G["LootButton" .. i]
if blizzBtn and LootFrame then
blizzBtn:SetParent(LootFrame)
blizzBtn:SetAlpha(1)
blizzBtn:Hide()
blizzBtn._nanamiRow = nil
end
if lootFrame then
lootFrame._closingLoot = true
lootFrame:Hide()
lootFrame._closingLoot = nil
end
for i = 1, ITEMS_PER_PAGE do
local nb = _G["LootButton" .. i]
if nb then nb:Hide() end
end
if lootFrame then lootFrame:Hide() end
for i = 1, table.getn(lootRows) do lootRows[i]:Hide() end
HideBagFullWarning()
if LootFrame then LootFrame:Hide() end
end
--------------------------------------------------------------------------------
@@ -750,7 +900,6 @@ function LD:Initialize()
CreateLootFrame()
CreateAlertAnchor()
-- Apply saved positions so frames have valid coordinates for the Mover system
if SFrames.Movers and SFrames.Movers.ApplyPosition then
local applied = SFrames.Movers:ApplyPosition("LootFrame", lootFrame,
"TOPLEFT", "UIParent", "TOPLEFT", 50, -200)
@@ -768,19 +917,41 @@ function LD:Initialize()
end
SFrames:RegisterEvent("LOOT_OPENED", function()
if GetDB().enable then UpdateLootFrame() end
if GetDB().enable then
if lootFrame then lootFrame._page = 1 end
HideBagFullWarning()
UpdateLootFrame()
end
end)
SFrames:RegisterEvent("LOOT_SLOT_CLEARED", function()
if GetDB().enable and lootFrame and lootFrame:IsShown() then
HideBagFullWarning()
UpdateLootFrame()
end
end)
SFrames:RegisterEvent("UI_ERROR_MESSAGE", function()
if lootFrame and lootFrame:IsShown() then
local msg = arg1
if msg == ERR_INV_FULL
or (INVENTORY_FULL and msg == INVENTORY_FULL)
or (msg and (string.find(msg, "背包已满")
or string.find(msg, "Inventory is full"))) then
ShowBagFullWarning()
end
end
end)
SFrames:RegisterEvent("LOOT_CLOSED", function()
CloseLootFrame()
end)
SFrames:RegisterEvent("LOOT_BIND_CONFIRM", function()
local slot = arg1
if slot then ConfirmLootSlot(slot) end
end)
SFrames:RegisterEvent("CHAT_MSG_LOOT", function()
local playerName = UnitName("player")
if not playerName then return end
@@ -790,27 +961,49 @@ function LD:Initialize()
end
end)
local function HideBlizzardLoot()
if LootFrame then
LootFrame:EnableMouse(false)
LootFrame:SetAlpha(0)
LootFrame:ClearAllPoints()
LootFrame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", -10000, 10000)
local origShow = LootFrame.Show
LootFrame.Show = function(self)
origShow(self)
self:EnableMouse(false)
self:SetAlpha(0)
self:ClearAllPoints()
self:SetPoint("TOPLEFT", UIParent, "TOPLEFT", -10000, 10000)
-- Save the original LootFrame_Update so ShowLootPage can call it
-- to let native code set up LootButton IDs and OnClick handlers.
origLootFrameUpdate = LootFrame_Update
if LootFrame then
-- Prevent the XML-defined OnHide from calling CloseLoot()
LootFrame:SetScript("OnHide", function() end)
-- Keep LootFrame shown but invisible while our UI is active
local origShow = LootFrame.Show
LootFrame.Show = function(self)
origShow(self)
self:SetAlpha(0)
self:EnableMouse(false)
end
-- Block native LootFrame from hiding while we are looting
local origHide = LootFrame.Hide
LootFrame.Hide = function(self)
if lootFrame and lootFrame:IsShown() then
return
end
origHide(self)
end
end
-- After the native LootFrame_Update runs (called by the engine or
-- by us), reposition native buttons onto our visual rows.
LootFrame_Update = function()
if origLootFrameUpdate then origLootFrameUpdate() end
if not (lootFrame and lootFrame:IsShown()) then return end
for i = 1, ITEMS_PER_PAGE do
local nb = _G["LootButton" .. i]
local row = lootRows[i]
if nb and row and row:IsShown() and row._qualColor then
nb:ClearAllPoints()
nb:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
nb:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
nb:SetFrameStrata("FULLSCREEN_DIALOG")
nb:SetFrameLevel(row:GetFrameLevel() + 10)
nb:SetAlpha(0)
nb:EnableMouse(true)
end
end
end
HideBlizzardLoot()
local lootHook = CreateFrame("Frame")
lootHook:RegisterEvent("ADDON_LOADED")
lootHook:SetScript("OnEvent", function()
if arg1 == "Blizzard_Loot" then HideBlizzardLoot() end
end)
end