You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 lines
13 KiB

5 years ago
-- Create module
local addon, L = XLoot:NewModule("Monitor")
local XLootMonitor = CreateFrame("Frame", "XLootMonitor", UIParent)
XLootMonitor.addon = addon
-- Grab locals
local print, opt, eframe, anchor = print
local CopperToString, FancyPlayerName = XLoot.CopperToString, XLoot.FancyPlayerName
local table_insert, table_remove = table.insert, table.remove
local me = UnitName("player")
-------------------------------------------------------------------------------
-- Settings
local defaults = {
profile = {
anchor = {
direction = "up",
alignment = "left",
visible = true,
scale = 1.0,
draggable = true,
x = UIParent:GetWidth() * .75,
y = UIParent:GetHeight() * .15,
},
name_width = 50,
gradients = false,
threshold_own = 2,
threshold_other = 3,
show_coin = false,
show_currency = true,
show_crafted = false,
show_totals = false,
totals_delay = 0.5,
use_altoholic = true,
show_ilvl = false,
fade_own = 10,
fade_other = 5,
font = STANDARD_TEXT_FONT,
font_size_loot = 12,
font_size_quantity = 10,
font_size_ilvl = 8,
font_flag = "OUTLINE",
}
}
----------------------------------------------------------------------
-- Helpers
local numberize = function(v)
if v <= 9999 then return v end
if v >= 1000000 then
local value = string.format("%.1fm", v/1000000)
return value
elseif v >= 10000 then
local value = string.format("%.0fk", v/1000)
return value
end
end
-------------------------------------------------------------------------------
-- Module init
function addon:OnInitialize()
eframe = CreateFrame("Frame")
self:InitializeModule(defaults, eframe)
XLoot:SetSlashCommand("xlm", self.SlashHandler)
opt = self.db.profile
end
function addon:OnEnable()
-- Register for loot events
LibStub("LootEvents"):RegisterLootCallback(self.LOOT_EVENT)
eframe:RegisterEvent("MODIFIER_STATE_CHANGED")
-- Set up skins
XLoot:MakeSkinner(self, {
default = { gradient = opt.gradients },
anchor = { r = .4, g = .4, b = .4, a = .6, gradient = false },
anchor_pretty = { r = .6, g = .6, b = .6, a = .8 },
item = { backdrop = false, gradient = opt.gradients },
item_highlight = { type = "highlight", layer = "overlay" },
row_highlight = { type = "highlight" }
})
-- Set up anchor
anchor = XLoot.Stack:CreateStaticStack(self.CreateRow, L.anchor, opt.anchor)
self:Skin(anchor, XLoot.opt.skin_anchors and 'anchor_pretty' or 'anchor')
end
function addon:ApplyOptions()
opt = self.opt
anchor:UpdateSVData(opt.anchor)
addon:Restack()
end
local events = {}
function events.item(player, link, num)
if link and link:match("|Hitem:") then -- Proper items
local name, _, quality, level, _, _, _, _, _, icon = GetItemInfo(link)
if not name or type(quality) ~= "number" then
print(name and "Quality is not a number" or "Name is nil")
return false
end
if (player == me and opt.threshold_own or opt.threshold_other) > quality then
return -- Doesn't meet threshold requirements
end
local r, g, b = GetItemQualityColor(quality)
local nr, ng, nb
if player ~= me then
player, nr, ng, nb = FancyPlayerName(player, select(2, UnitClass(player)), opt)
else
player = nil
end
local row = addon:AddRow(icon, (player and opt.fade_other or opt.fade_own), r, g, b)
local num = tonumber(num) or 1
row:SetTexts(player, num > 1 and ("%sx%d"):format(link, num) or link, nil, nr, ng, nb)
if opt.show_totals then
row.timeToTotal = opt.totals_delay
end
if opt.show_ilvl and level > 1 then
local ilvl = GetDetailedItemLevelInfo(link)
row.ilvl:SetText(ilvl)
end
row.item = link
elseif link and link:match("|Hbattlepet:") then -- Battlepets. Really?
-- local _, speciesID, level, breedQuality, maxHealth, power, speed, battlePetID = strsplit(":", link)
else
-- print("Unknown or invalid link type")
-- return false
end
end
function events.coin(coin_string, copper)
if opt.show_coin then
addon:AddRow(GetCoinIcon(copper), opt.fade_own, .5, .5, .5, .5, .5, .5):SetTexts(nil, CopperToString(copper))
end
end
function events.currency(id, num)
if opt.show_currency then
local num = tonumber(num) or 1
local c = C_CurrencyInfo.GetCurrencyInfo(id)
if c then
addon:AddRow(c.iconFileID, opt.fade_own, 1, 1, 1, 1, 1, 1):SetTexts(nil, num > 1 and ("%s x%d"):format(c.name, num) or c.name, c.quantity)
end
end
end
function events.crafted(link, num)
if opt.show_crafted then
events.item(me, link, num)
end
end
function addon.LOOT_EVENT(event, pattern, ...)
if events[event] and events[event](...) == false then
print("XLoot Monitor: Error handling event", event, pattern, ...)
end
end
local mouse_focus
function addon:MODIFIER_STATE_CHANGED(self, modifier, state)
if mouse_focus and MouseIsOver(mouse_focus) then
mouse_focus:ShowTooltip()
end
end
local pool, stack, active = {}, {}, false
stack[0] = anchor
local timer = 0
function addon.EframeUpdate(self, elapsed)
timer = timer + elapsed
for i,row in ipairs(stack) do
-- Deferred total calculation due to GetItemCount reliability
local ttt = row.timeToTotal
if ttt then
ttt = ttt - elapsed
if ttt <= 0 then
ttt = nil
local total
if opt.use_altoholic and Altoholic then
total = Altoholic:GetItemCount(Altoholic:GetIDFromLink(row.item))
else
total = GetItemCount(row.item)
end
if total and total > 1 then
row.total:SetText(numberize(total))
end
end
row.timeToTotal = ttt
end
-- Animation
local remaining = row.expires - timer
if remaining < 0 then
row:SetAlpha(0)
addon:RemoveRow(row)
elseif remaining <= .5 then
row:SetAlpha(remaining * 2)
else
local since = timer - row.started
if since <= .5 then
row:SetAlpha(since * 2)
end
end
end
end
function addon:RemoveRow(row)
row:Hide()
for i,v in ipairs(stack) do
if v == row then
table_remove(stack, i)
table_insert(pool, row)
if stack[i] then
anchor:AnchorChild(stack[i], stack[i-1])
end
break
end
end
-- Disable OnUpdate
if #stack == 0 then
active, timer = false, 0
eframe:SetScript("OnUpdate", nil)
end
end
function addon:AddRow(icon, fade_time, ir, ig, ib, rr, rg, rb)
-- Acquire
local row = table_remove(pool)
if not row then
row = self.CreateRow()
end
-- Set up row
row.icon:SetTexture(icon)
row.icon_frame:SetBorderColor(ir, ig, ib)
row:SetBorderColor(rr or ir, rg or ig, rb or ib)
row.expires = timer + fade_time
row.started = timer
row.item = nil
-- Anchor
anchor:AnchorChild(row)
table_insert(stack, 1, row)
-- Reanchor second newest
if stack[2] then
anchor:AnchorChild(stack[2], row)
end
row:Show()
-- Enable OnUpdate
if not active then
active = true
eframe:SetScript("OnUpdate", self.EframeUpdate)
end
return row
end
function addon:Restack()
for i,v in ipairs(stack) do
anchor:AnchorChild(v, i > 1 and stack[i-1] or nil)
v:ApplyOptions()
end
end
-------------------------------------------------------------------------------
-- Frame methods
do
local function ShowTooltip(self)
if self.item then
GameTooltip:SetOwner(self, 'ANCHOR_TOPLEFT', 28, 0)
GameTooltip:SetHyperlink(self.item)
CursorUpdate(self)
end
end
local function OnEnter(self)
mouse_focus = self
if self._highlights then
self.icon_frame:ShowHighlight()
end
self:ShowTooltip()
end
local function OnLeave(self)
mouse_focus = nil
if self._highlights then
self.icon_frame:HideHighlight()
end
GameTooltip:Hide()
ResetCursor()
end
local function OnClick(self, button)
if IsModifiedClick() and self.item then
HandleModifiedItemClick(self.item)
end
end
local function FitToText(self)
self:SetWidth(self.name:GetWidth() + self.text:GetWidth() + 8 + self.icon_frame:GetWidth())
end
local function SetTexts(self, name, text, total, nr, ng, nb, tr, tg, tb)
self.name:SetText(name and name.." " or nil)
self.text:SetText(text)
self.ilvl:SetText()
-- Show total after 0.5 seconds to get a valid count
self.total:SetText()
self.name:SetVertexColor(nr or 1, ng or 1, nb or 1)
self.text:SetVertexColor(nr or 1, ng or 1, nb or 1)
if name then
self.name:SetWidth(opt.name_width)
else
self.name:SetWidth(0)
end
self:FitToText()
end
local widgets = {
{ "icon_frame", 0 },
{ "name", 2 },
{ "text", 0 },
}
local function ApplyOptions(self)
local a, b, xmod = "LEFT", "RIGHT", 1
if opt.anchor.alignment == "right" then
a, b, xmod = b, a, -1
end
self.icon_frame:ClearAllPoints()
self.icon_frame:SetPoint(a)
self.name:ClearAllPoints()
self.name:SetPoint(a, self.icon_frame, b, 2 * xmod, 0)
self.name:SetJustifyH(a)
self.name:SetFont(opt.font, opt.font_size_loot, opt.font_flag)
self.text:ClearAllPoints()
self.text:SetPoint(a, self.name, b)
self.text:SetJustifyH(a)
self.text:SetFont(opt.font, opt.font_size_loot, opt.font_flag)
self.ilvl:SetFont(opt.font, opt.font_size_ilvl, opt.font_flag)
self.total:SetFont(opt.font, opt.font_size_quantity, opt.font_flag)
self:FitToText()
end
function addon.CreateRow()
local frame = CreateFrame("Button", nil, UIParent, BackdropTemplateMixin and "BackdropTemplate")
frame:SetFrameLevel(anchor:GetFrameLevel())
frame:SetHeight(24)
frame:SetWidth(250)
addon:Skin(frame)
addon:Highlight(frame, "row_highlight")
frame:SetHighlightColor(.8, .8, .8)
frame:RegisterForClicks("LeftButtonUp", "RightButtonUp")
frame:SetScript("OnClick", OnClick)
frame:SetScript("OnEnter", OnEnter)
frame:SetScript("OnLeave", OnLeave)
-- Item icon (For skin border)
local icon_frame = CreateFrame("Frame", nil, frame, BackdropTemplateMixin and "BackdropTemplate")
icon_frame:SetWidth(28)
icon_frame:SetHeight(28)
addon:Skin(icon_frame, "item")
addon:Highlight(icon_frame, "item_highlight")
frame.icon_frame = icon_frame
-- Item texture
local icon = icon_frame:CreateTexture(nil, "BACKGROUND")
icon:SetPoint("TOPLEFT", 3, -3)
icon:SetPoint("BOTTOMRIGHT", -3, 3)
icon:SetTexCoord(.07,.93,.07,.93)
frame.icon = icon
local ilvl = icon_frame:CreateFontString(nil, "OVERLAY")
ilvl:SetPoint("BOTTOM", icon_frame, "BOTTOM", 0, 0)
ilvl:SetJustifyH("CENTER")
frame.ilvl = ilvl
frame.name = frame:CreateFontString(nil, "OVERLAY")
frame.text = frame:CreateFontString(nil, "OVERLAY")
local total = icon_frame:CreateFontString(nil, "OVERLAY")
total:SetPoint("CENTER", icon_frame, "CENTER", 0, 0)
total:SetJustifyH("CENTER")
frame.total = total
frame.FitToText = FitToText
frame.SetTexts = SetTexts
frame.ShowTooltip = ShowTooltip
frame.ApplyOptions = ApplyOptions
frame:ApplyOptions()
return frame
end
end
function addon:UpdateAnchors(...)
if opt.anchor.visible then
anchor:Show()
else
anchor:Hide()
end
end
function addon.SlashHandler(msg)
if anchor:IsShown() then
anchor:Hide()
else
anchor:Show()
end
end
local items = {
{ 52722 },
{ 31304 },
{ 37254 },
{ 13262 },
{ 15487 },
{ 72120 },
{ 2589 }
}
local currencies = {
81,
1728
}
for i,v in ipairs(items) do
GetItemInfo(v[1])
end
local players = {
{ UnitName("player"), select(2, UnitClass('player')) },
{ 'Player1', 'MAGE' },
{ 'Player2', 'PRIEST' },
{ 'Player3', 'WARRIOR' },
{ 'Player4', 'SHAMAN' }
}
local random = math.random
local function random_player(is_me)
return is_me and players[1][1] or players[random(2, 5)][1]
end
local function random_item_num(max)
return random(1, 2) == 2 and random(1, max or 20) or 1
end
local function random_item_link()
return select(2, GetItemInfo(items[random(1, #items)][1]))
end
local function test_item(event, is_me)
addon.LOOT_EVENT('item', event, random_player(is_me), random_item_link(), random_item_num())
end
local function test_coin(event, is_me)
addon.LOOT_EVENT('coin', event, random_player(is_me), random(1, 500000), "TODO")
end
local function test_currency(event)
for _,id in ipairs(currencies) do
addon.LOOT_EVENT('currency', event, id, random_item_num(5))
end
end
local function test_crafted(event)
addon.LOOT_EVENT('crafted', event, random_item_link(), random_item_num())
end
local function test_battlepet(event)
print("Testing battlepet event")
addon.LOOT_EVENT('item', "LOOT_ITEM_PUSHED_SELF", random_player(true), "|cff0070dd|Hbattlepet:868:1:3:158:10:12:0x0000000000000000|h[Pandaren Water Spirit]|h|r", 1)
end
local tests = {
{ test_item, "LOOT_ITEM" },
{ test_item, "LOOT_ITEM_SELF", true },
{ test_item, "LOOT_ITEM_MULTIPLE" },
{ test_item, "LOOT_ITEM_SELF_MULTIPLE", true },
{ test_item, "LOOT_ITEM_PUSHED_SELF", true },
{ test_item, "LOOT_ITEM_PUSHED_SELF_MULTIPLE", true },
{ test_coin, "LOOT_MONEY" },
{ test_coin, "LOOT_MONEY_SPLIT", true },
{ test_coin, "YOU_LOOT_MONEY", true },
{ test_currency, "CURRENCY_GAINED" },
{ test_currency, "CURRENCY_GAINED_MULTIPLE" },
{ test_crafted, "LOOT_ITEM_CREATED_SELF" },
{ test_crafted, "CURRENCY_GAINED_MULTIPLE" },
{ test_battlepet }
}
local queue, queueframe, tick = {}, CreateFrame("Frame"), 0
local function queue_update(self, elapsed)
tick = tick + elapsed
if tick > 0.5 then
tick = 0
local time = GetTime()
for k,v in pairs(queue) do
if v[1] < time then
if v[3] then
print("Testing "..v[3])
end
v[2](select(3, unpack(v)))
queue[k] = nil
end
end
end
end
local qactive = false
function XLootMonitor.TestSettings()
local now = GetTime()
-- for i=1,15 do
-- table.insert(queue, { now + i, unpack(tests[random(1, #tests)]) })
-- end
if not qactive then
queueframe:SetScript("OnUpdate", queue_update)
qactive = true
end
for i,v in ipairs(tests) do
table.insert(queue, { now + i * .5, unpack(v) })
end
end
XLoot:SetSlashCommand("xlmd", XLootMonitor.TestSettings)