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.

1130 lines
32 KiB

-- Create module
local addon, L = XLoot:NewModule("Group")
-- Prepare global
XLootGroup = addon
-- Grab locals
local opt, anchor, alert_anchor, mouse_focus, Skinner
local rolls = {}
local RAID_CLASS_COLORS = CUSTOM_CLASS_COLORS or _G.RAID_CLASS_COLORS
local GetLootRollItemInfo, GetLootRollItemLink, GetLootRollTimeLeft, RollOnLoot, UnitGroupRolesAssigned, print, string_format
= GetLootRollItemInfo, GetLootRollItemLink, GetLootRollTimeLeft, RollOnLoot, UnitGroupRolesAssigned, print, string.format
local HistoryGetItem, HistoryGetPlayerInfo, HistoryGetNumItems
= C_LootHistory.GetItem, C_LootHistory.GetPlayerInfo, C_LootHistory.GetNumItems
local CanEquipItem, IsItemUpgrade, FancyPlayerName = XLoot.CanEquipItem, XLoot.IsItemUpgrade, XLoot.FancyPlayerName
local RollFramePrototype
local BUILD_NUMBER = select(4, GetBuildInfo())
local BUILD_HAS_DISENCHANT = BUILD_NUMBER >= 30300
local BUILD_HAS_TRANSMOG_GREED = BUILD_NUMBER >= 49407
-------------------------------------------------------------------------------
-- Settings
local defaults = {
profile = {
role_icon = true,
win_icon = false,
show_decided = true,
show_undecided = false,
show_time_remaining = false,
text_ilvl = false,
equip_prefix = true,
prefix_equippable = "*",
prefix_upgrade = "+",
hook_alert = false,
alert_skin = true,
alert_alpha = 1,
alert_scale = 1,
alert_offset = 4,
alert_background = false,
alert_icon_frame = false,
hook_bonus = false,
bonus_skin = true,
roll_button_size = 28,
roll_width = 325,
font = STANDARD_TEXT_FONT,
font_flag = "OUTLINE",
roll_anchor = {
direction = 'up',
spacing = 2,
offset = 0,
visible = true,
draggable = true,
scale = 1.0,
x = UIParent:GetWidth() * .75,
y = UIParent:GetHeight() * .4
},
alert_anchor = {
visible = true,
direction = 'up',
draggable = true,
scale = 1.0,
x = AlertFrame:GetLeft(),
y = AlertFrame:GetTop()
},
track_all = false,
track_player_roll = false,
track_by_threshold = true,
track_threshold = 3,
expire_won = 20,
expire_lost = 10,
shown_hook_warning = false
}
}
opt = defaults.profile
-------------------------------------------------------------------------------
-- Module init
local eframe = CreateFrame("Frame")
function addon:OnInitialize()
self:InitializeModule(defaults, eframe)
opt = self.db.profile
XLootGroup.opt = opt
-- Extra slash command
XLoot:SetSlashCommand("xlg", self.SlashHandler)
end
function addon:OnEnable()
-- Register events
eframe:RegisterEvent('START_LOOT_ROLL')
eframe:RegisterEvent('MODIFIER_STATE_CHANGED')
if BUILD_HAS_TRANSMOG_GREED then
print("XLoot Group does not yet work on this version and will not be loaded")
return
eframe:RegisterEvent('LOOT_HISTORY_UPDATE_DROP')
else
eframe:RegisterEvent('LOOT_HISTORY_ROLL_CHANGED')
eframe:RegisterEvent('LOOT_HISTORY_ROLL_COMPLETE')
eframe:RegisterEvent('LOOT_ROLLS_COMPLETE')
end
-- Disable default frame
UIParent:UnregisterEvent("START_LOOT_ROLL")
UIParent:UnregisterEvent("CANCEL_LOOT_ROLL")
-- Set up skins
Skinner = {}
XLoot:MakeSkinner(Skinner, {
anchor = { r = .4, g = .4, b = .4, a = .6, gradient = false },
anchor_pretty = { r = .6, g = .6, b = .6, a = .8 },
row = { gradient = false },
item = { backdrop = false },
alert = { gradient = true },
alert_item = { gradient = true, backdrop = false },
bonus = { }
}, 'row')
-- Create Roll anchor
anchor = XLoot.Stack:CreateStaticStack(function() return RollFramePrototype:New() end, L.anchor, opt.roll_anchor)
anchor:SetFrameLevel(7)
anchor:Scale(opt.roll_anchor.scale)
addon.anchor = anchor
-- Create alert anchor
alert_anchor = XLoot.Stack:CreateAnchor(L.alert_anchor, opt.alert_anchor)
alert_anchor:SetFrameLevel(7)
addon.alert_anchor = alert_anchor
-- DISABLED-PATCH: LEGION PRE-PATCH
alert_anchor.Show = alert_anchor.Hide
alert_anchor:Hide()
-- Skin anchor
Skinner:Skin(anchor, XLoot.opt.skin_anchors and 'anchor_pretty' or 'anchor')
Skinner:Skin(alert_anchor, XLoot.opt.skin_anchors and 'anchor_pretty' or 'anchor')
-- Row fader
local fader = CreateFrame('Frame')
local timer = 0
fader:SetScript('OnUpdate', function(self, elapsed)
if timer < 1 then
timer = timer + elapsed
else
timer = 0
local time = GetTime()
-- Extend expiration for mouseovered frames
if mouse_focus and mouse_focus.aexpire and (mouse_focus.aexpire - time) < 5 then
mouse_focus.aexpire = time + 5
end
for i=#anchor.expiring,1,-1 do
local frame = anchor.expiring[i]
if frame.aexpire and time > frame.aexpire then
anchor:Pop(frame)
table.remove(anchor.expiring, i)
end
end
if not next(anchor.expiring) then
self:Hide()
end
end
end)
anchor.expiring = {}
function anchor:Expire(frame, time)
fader:Show()
table.insert(self.expiring, frame)
frame.aexpire = GetTime() + time
end
-- Find and show active rolls
if IsInGroup() and (GetLootMethod() == 'group' or GetLootMethod() == 'needbeforegreed') then
for i=1,300 do
local time = GetLootRollTimeLeft(i)
if time > 0 and time < 300000 then
self:START_LOOT_ROLL(i, time, true)
end
end
end
end
-------------------------------------------------------------------------------
-- Frame helpers
-------------------------------------------------------------------------------
-- Event handlers
addon.bars = {}
local type_strings = {
need = NEED,
greed = GREED,
disenchant = ROLL_DISENCHANT,
pass = PASS
}
local rtypes = { [0] = 'pass', 'need', 'greed', 'disenchant' } -- Tekkub. Writing smaller addons than me since ever.
function addon:START_LOOT_ROLL(id, length, uid, ongoing)
local icon, name, count, quality, bop, need, greed, de, reason_need, reason_greed, reason_de, de_skill = GetLootRollItemInfo(id)
-- LootFrame.lua includes this sanity check
if name == nil then
print('XLoot Group: Ignoring START_LOOT_ROLL with no name')
return
end
local link = GetLootRollItemLink(id)
local r, g, b = GetItemQualityColor(quality)
local start = length
if ongoing then
if quality == 2 then
length = 60000
else
length = 180000
end
end
length, start = length/1000, start/1000
local frame = anchor:Push()
rolls[id] = frame
frame.need:Show()
frame.greed:Show()
if BUILD_HAS_DISENCHANT then
frame.disenchant:Show()
end
frame.pass:Show()
frame.text_status:Hide()
frame.text_status:SetText()
frame.text_time:SetShown(opt.show_time_remaining)
frame.need.texture_special:SetTexture()
frame.greed.texture_special:SetTexture()
if opt.equip_prefix then
local canequip, isupgrade = CanEquipItem(link), IsItemUpgrade(link)
if canequip or isupgrade then
4 years ago
name = string_format("|cFF%s%s|r%s", isupgrade and "44FF22" or "BBBBBB", isupgrade and opt.prefix_upgrade or opt.prefix_equippable, name)
if isupgrade then
(need and frame.need or frame.greed).texture_special:SetTexture([[Interface\AddOns\Pawn\Textures\UpgradeArrow.tga]])
end
end
end
frame.need:Toggle(need)
frame.greed:Toggle(greed)
frame.disenchant:Toggle(de)
frame.need:SetText()
frame.greed:SetText()
frame.pass:SetText()
frame.disenchant:SetText()
frame.need.reason = reason_need ~= 0 and reason_need or nil
frame.greed.reason = reason_greed ~= 0 and reason_greed or nil
frame.disenchant.reason = reason_de ~= 0 and reason_de or nil
frame.disenchant.skill = de_skill ~= 0 and de_skill or nil
local bar = frame.bar
bar.length = length
bar.expires = GetTime() + start
frame.link = link
frame.rollid = id
frame.rollended = nil
frame.quality = quality
frame.expires = bar.expires
frame.over = nil
frame.have_rolled = false
frame.lead_type = 'pass'
frame.text_bind:SetText(bop and '|cffff4422BoP' or '')
frame.text_loot:SetText(name)
local ilvl = GetDetailedItemLevelInfo(link)
frame.text_ilvl:SetText(ilvl > 1 and ilvl or nil)
frame.text_loot:SetVertexColor(r, g, b)
frame.overlay:SetBorderColor(r, g, b)
frame.icon_frame:SetBorderColor(r, g, b)
bar:SetStatusBarColor(r, g, b, .7)
frame.icon:SetTexture(icon)
bar:SetMinMaxValues(0, length)
bar:SetValue(start)
return frame
end
local tidx = { [0] = 1, [3] = 2, [2] = 2, [1] = 3 }
function addon:LOOT_HISTORY_ROLL_COMPLETE()
-- Locate history item
local hid, frame, rollid, players, done, _ = 1, nil, nil, nil, nil, nil
while true do
rollid, _, players, done = HistoryGetItem(hid)
if not rollid or (rolls[rollid] and rolls[rollid].over) then
return
elseif done and rolls[rollid] then
frame = rolls[rollid]
break
end
hid = hid+1
end
-- Active frame found
frame.over = true
local top_type, top_roll, top_pid, top_is_me = 0, 0, nil, nil
for j=1, players do
local name, class, rtype, roll, is_winner, is_me = HistoryGetPlayerInfo(hid, j)
-- roll = roll and roll or true
if is_winner then
top_pid = j
4 years ago
top_is_me = is_me
break
elseif rtype ~= 0 and tidx[rtype] >= tidx[top_type] and (not roll or roll > top_roll) then
top_type = rtype
top_roll = roll
top_pid = j
end
end
-- Winner or lead
if top_pid then
local name, class = HistoryGetPlayerInfo(hid, top_pid)
local player, r, g, b = FancyPlayerName(name, class, opt)
if opt.win_icon then
4 years ago
if top_type == 'need' then
player = [[|TInterface\Buttons\UI-GroupLoot-Dice-Up:16:16:-1:-1|t]]..player
4 years ago
elseif top_type == 'greed' then
player = [[|TInterface\Buttons\UI-GroupLoot-Coin-Up:16:16:-1:-2|t]]..player
4 years ago
elseif top_type == 'disenchant' then
player = [[|TInterface\Buttons\UI-GroupLoot-DE-Up:16:16:-1:-1|t]]..player
end
end
frame.text_status:SetText(player)
frame.text_status:SetTextColor(r, g, b)
frame.bar.expires = GetTime()
4 years ago
anchor:Expire(frame, top_is_me and opt.expire_won or opt.expire_lost)
else
-- No winner/lead
frame.text_status:SetText(string_format('%s: %s', PASS, ALL))
frame.text_status:SetTextColor(.7, .7, .7)
frame.bar.expires = GetTime()
anchor:Expire(frame, opt.expire_lost)
end
-- Refresh tooltip
if frame and mouse_focus == frame then
frame:OnEnter()
end
end
addon.LOOT_ROLLS_COMPLETE = addon.LOOT_HISTORY_ROLL_COMPLETE
local rweights = { need = 3, greed = 2, disenchant = 2, pass = 1 }
function addon:LOOT_HISTORY_ROLL_CHANGED(hid, pid)
-- Acquire roll information and frame
local rollid, link, players, done = HistoryGetItem(hid)
local frame = rolls[rollid]
if not frame or frame.rollid ~= rollid or not frame:IsShown() then
return nil
end
-- Acquire player information
local name, class, rtypeid, roll, winner, is_me = HistoryGetPlayerInfo(hid, pid)
local rtype = rtypes[rtypeid]
-- Transition or expire frame on player roll
if is_me then
if opt.track_all
or (opt.track_player_roll and rtype ~= 'pass')
or (opt.track_by_threshold and frame.quality >= opt.track_threshold) then
frame.need:Hide()
frame.greed:Hide()
frame.disenchant:Hide()
frame.pass:Hide()
frame.text_status:Show()
frame.have_rolled = true
else
anchor:Pop(frame)
return
end
end
-- Update post-player-roll status text
if frame.have_rolled then
local rtype = rtype == 'disenchant' and 'greed' or rtype
-- Roll of leading type or higher
if rweights[rtype] >= rweights[frame.lead_type] then
frame.lead_type = rtype
local bracket, mtype = 0, nil
for i=1, players do
local _, _, ptype, _, _, is_me = HistoryGetPlayerInfo(hid, i)
local ptype = rtypes[ptype == 3 and 2 or ptype]
if ptype == rtype then
bracket = bracket + 1
end
if is_me then
mtype = ptype
end
end
local r, g, b = .7, .7, .7
if mtype == rtype then
r, g, b = .2, 1, .1
elseif mtype and mtype ~= 0 then
r, g, b = 1, .2, .1
end
frame.text_status:SetText(string_format('%s: %s', type_strings[rtype], bracket))
frame.text_status:SetTextColor(r, g, b)
end
-- Update roll button counters
else
local bracket = 0
for i=1, players do
local _, _, thistype = HistoryGetPlayerInfo(hid, i)
if thistype == rtypeid then
bracket = bracket + 1
end
end
frame[rtype]:SetText(bracket)
end
-- Refresh tooltip
if frame and mouse_focus == frame then
frame:OnEnter()
end
end
function addon:MODIFIER_STATE_CHANGED()
if mouse_focus and MouseIsOver(mouse_focus) and mouse_focus.OnEnter then
mouse_focus:OnEnter()
end
end
local alert_frames = {}
function addon.AlertFrameHook(alert)
-- Reskin toast
local elements = alert_frames[alert]
if not elements then
elements = {}
if not (opt.alert_background and opt.alert_icon_frame) then
local name
if alert.ItemName then
name = alert.ItemName
alert.Label:ClearAllPoints()
alert.Label:SetPoint('TOPLEFT', alert.Icon, 'TOPRIGHT', 15, -5)
elseif alert.BaseQualityItemName then
name = alert.BaseQualityItemName
alert.TitleText:ClearAllPoints()
alert.TitleText:SetPoint('TOPLEFT', alert.Icon, 'TOPRIGHT', 15, -2)
elseif alert.Amount then
name = alert.Amount
alert.Label:ClearAllPoints()
alert.Label:SetPoint('TOPLEFT', alert.Icon, 'TOPRIGHT', 15, -2)
end
name:ClearAllPoints()
name:SetPoint('LEFT', alert.Icon, 'RIGHT', 10, -6)
end
if opt.alert_skin then
local overlay = CreateFrame('Frame', nil, alert, BackdropTemplateMixin and "BackdropTemplate")
overlay:SetPoint('TOPLEFT', 11, -11)
overlay:SetPoint('BOTTOMRIGHT', -11, 11)
overlay:SetFrameLevel(alert:GetFrameLevel())
elements.overlay = overlay
Skinner:Skin(overlay, 'alert')
if opt.alert_background then
local backdrop = CreateFrame('Frame', nil, alert, BackdropTemplateMixin and "BackdropTemplate")
backdrop:SetAllPoints(overlay)
backdrop:SetFrameLevel(alert:GetFrameLevel()-1)
overlay.gradient:SetParent(backdrop)
end
local icon_frame = CreateFrame('Frame', nil, alert, BackdropTemplateMixin and "BackdropTemplate")
icon_frame:SetPoint('CENTER', alert.Icon, 'CENTER', 0, 0)
icon_frame:SetWidth(alert.Icon:GetWidth() + 4)
icon_frame:SetHeight(alert.Icon:GetHeight() + 4)
elements.icon_frame = icon_frame
Skinner:Skin(icon_frame, 'alert_item')
end
alert_frames[alert] = elements
end
alert.Background:SetShown(opt.alert_background)
alert.IconBorder:SetShown(opt.alert_icon_frame)
alert.BaseQualityBorder:SetShown(opt.alert_icon_frame)
alert.UpgradeQualityBorder:SetShown(opt.alert_icon_frame)
alert:SetAlpha(opt.alert_alpha)
alert:SetScale(opt.alert_scale)
-- Update toast
if opt.alert_skin then
local c
if alert.hyperlink then
local _, _, rarity = GetItemInfo(alert.hyperlink)
c = ITEM_QUALITY_COLORS[rarity]
else
c = {r = 1, g = .8, b = 0.1}
end
if type(c) == "table" then -- Sanity check due to 5.4.1 reported error
elements.overlay:SetGradientColor(c.r, c.g, c.b, .2)
elements.icon_frame:SetGradientColor(c.r, c.g, c.b, .2)
elements.overlay:SetBorderColor(c.r, c.g, c.b)
elements.icon_frame:SetBorderColor(c.r, c.g, c.b)
end
end
end
local AlertFrameTables = {
'LOOT_WON_ALERT_FRAMES',
'LOOT_UPGRADE_ALERT_FRAMES',
'MONEY_WON_ALERT_FRAMES'
}
function addon.SlashHandler(msg)
if msg == 'reset' then
anchor:Position()
alert_anchor:Position()
elseif msg == 'opt' or msg == 'options' then
addon.ShowOptions()
else
addon.ToggleAnchors()
end
end
function addon:UpdateAnchors()
anchor:SetShown(opt.roll_anchor.visible)
alert_anchor:SetShown(opt.alert_anchor.visible)
end
function addon.ToggleAnchors()
local state = anchor:IsShown()
anchor:SetShown(not state)
alert_anchor:SetShown(not state)
end
-------------------------------------------------------------------------------
-- Frame creation
do
local sf = string.format
-- Add a specific roll type to the tooltip
local function RollLines(list, hid)
for _,pid in pairs(list) do
local name, class, rtype, roll, is_winner, is_me = HistoryGetPlayerInfo(hid, pid)
-- TODO- ACCOUNT FOR MISSING PLAYERS BETTER
if not name then return nil end
local text, r, g, b, color = FancyPlayerName(name, class, opt)
if roll ~= nil then
if is_winner then
color = '44ff22'
elseif is_me then
color = 'ff2244'
else
color = 'CCCCCC'
end
GameTooltip:AddLine(sf(' |cff%s%s|r %s', color, roll, text), r, g, b)
else
GameTooltip:AddLine(' '..text, r, g, b)
end
end
end
-- Add roll status or summary to tooltip
local tneed, tgreed, tpass, trolls, tnone, table_sort
= {}, {}, {}, {}, {}, table.sort
local function rsort(a, b)
a, b = trolls[a] or 0, trolls[b] or 0
return a > b and true or false
end
local function AddIneligibleReason(button, r, g, b)
if button.reason and _G["LOOT_ROLL_INELIGIBLE_REASON"..button.reason] then
GameTooltip:AddLine(string_format(_G["LOOT_ROLL_INELIGIBLE_REASON"..button.reason], button.skill), r or .6, g or .6, b or .6)
GameTooltip:Show()
end
end
local function AddTooltipLines(self, show_all, show)
-- Locate history item
local rollid, hid = self.rollid, 1
local hrollid, link, players, done
while true do
hrollid, link, players, done = HistoryGetItem(hid)
if not hrollid then
return
elseif hrollid == rollid then
break
end
hid = hid+1
end
-- Generate player lists
local tneed, tgreed, tpass, tnone, trolls
= wipe(tneed), wipe(tgreed), wipe(tpass), wipe(tnone), wipe(trolls)
for pid=1, players do
local _, _, rtype, roll = HistoryGetPlayerInfo(hid, pid)
local t
if rtype then
if rtype == 0 then
t = tpass
elseif rtype == 1 then
t = tneed
elseif rtype == 2 or rtype == 3 then
t = tgreed
end
trolls[pid] = roll
else
t = tnone
end
if t then
t[#t+1] = pid
end
end
table_sort(tneed, rsort)
table_sort(tgreed, rsort)
table_sort(tpass, rsort)
-- Generate tooltip
if show_all then
GameTooltip:AddLine('.', 0, 0, 0)
end
if #tneed ~= 0 and (show_all or show == 1) then
GameTooltip:AddLine(NEED, .2, 1, .1)
RollLines(tneed, hid)
end
if #tgreed ~= 0 and (show_all or (show == 2 or show == 3)) then
GameTooltip:AddLine(GREED, .1, .2, 1)
RollLines(tgreed, hid)
end
if #tpass ~= 0 and (show_all or show == 0) then
GameTooltip:AddLine(PASS, .7, .7, .7)
RollLines(tpass, hid)
end
if show_all and opt.show_undecided then
GameTooltip:AddLine(L.undecided, .7, .3, .2)
RollLines(tnone, hid)
end
-- Force tooltip to refresh
GameTooltip:Show()
return true
end
---------------------------------------------------------------------------
-- Roll buttons
---------------------------------------------------------------------------
local RollButtonPrototype = XLoot.NewPrototype()
do
function RollButtonPrototype:OnClick()
RollOnLoot(self.parent.rollid, self.type)
end
function RollButtonPrototype:Toggle(status)
if status then
self:Enable()
self:SetAlpha(1)
else
self:Disable()
self:SetAlpha(.6)
end
SetDesaturation(self:GetNormalTexture(), not status)
end
function RollButtonPrototype:OnEnter()
mouse_focus = self
GameTooltip:SetOwner(self, 'ANCHOR_TOPLEFT')
AddTooltipLines(self.parent, false, self.type)
-- This isn't working for some stupid reason
if GameTooltip:NumLines() == 0 then
GameTooltip:SetText(self.label, unpack(self.label_colors))
GameTooltip:Show()
end
-- This is for those people who think they should be able
-- to roll on something, can't, and then come complain to me.
if self.reason then
AddIneligibleReason(self, 1, .2, 0)
end
end
function RollButtonPrototype:OnLeave()
mouse_focus = nil
GameTooltip:Hide()
end
function RollButtonPrototype:SetText(text)
if text and text > 0 then
self.text:SetText(text)
else
self.text:SetText()
end
end
local path = [[Interface\Buttons\UI-GroupLoot-%s-%s]]
function RollButtonPrototype:New(parent, roll, label, tex, anchor_to, x, y, label_colors)
local b = self:_New(CreateFrame('Button', nil, parent))
b:SetPoint('LEFT', anchor_to, 'RIGHT', x, y)
b:SetNormalTexture(path:format(tex, 'Up'))
if tex ~= 'Pass' then
b:SetHighlightTexture(path:format(tex, 'Highlight'))
b:SetPushedTexture(path:format(tex, 'Down'))
else
b:SetHighlightTexture(path:format(tex, 'Up'))
b:GetNormalTexture():SetVertexColor(0.8, 0.7, 0.7)
b:GetHighlightTexture():SetAlpha(0.5)
end
b.parent = parent
local texture_special = b:CreateTexture(nil, 'OVERLAY')
texture_special:SetAllPoints(b)
texture_special:SetAlpha(0.5)
b.texture_special = texture_special
local text = b:CreateFontString(nil, 'OVERLAY')
text:SetPoint("CENTER", -x + 1, tex == 'DE' and -y +2 or -y)
b.text = text
b:SetScript('OnEnter', self.OnEnter)
b:SetScript('OnLeave', self.OnLeave)
b:SetScript('OnClick', self.OnClick)
b:SetMotionScriptsWhileDisabled(true)
b:Enable()
b.type = roll
b.label = label
b.label_colors = label_colors
b:ApplyOptions()
return b
end
function RollButtonPrototype:ApplyOptions()
self.text:SetFont(opt.font, 12, opt.font_flag)
self:SetWidth(opt.roll_button_size)
self:SetHeight(opt.roll_button_size)
end
end
---------------------------------------------------------------------------
-- Roll frames
---------------------------------------------------------------------------
RollFramePrototype = XLoot.NewPrototype()
-- Events
function RollFramePrototype:OnEnter()
mouse_focus = self
GameTooltip:SetOwner(self.icon_frame, 'ANCHOR_TOPLEFT', 28, 0)
GameTooltip:SetHyperlink(self.link)
if opt.show_decided or opt.show_undecided then
AddTooltipLines(self, true)
if self.need.reason then
AddIneligibleReason(self.need, 1, .2, 0)
end
if self.greed.reason and self.greed.reason ~= self.need.reason then
AddIneligibleReason(self.greed, .8, .1, 0)
end
if self.disenchant.reason then
AddIneligibleReason(self.disenchant, .6, .05, 0)
end
end
if IsShiftKeyDown() then
GameTooltip_ShowCompareItem()
end
if IsModifiedClick('DRESSUP') then
ShowInspectCursor()
else
ResetCursor()
end
end
function RollFramePrototype:OnLeave()
mouse_focus = nil
GameTooltip:Hide()
end
function RollFramePrototype:OnClick(button)
if IsControlKeyDown() then
DressUpItemLink(self.link)
elseif IsShiftKeyDown() then
ChatEdit_InsertLink(self.link)
end
end
-- Status bar update
local max = math.max
function RollFramePrototype:OnBarUpdate()
local parent = self.parent
if parent.over then
self.spark:Hide()
self:SetValue(0)
parent.text_time:SetText()
return
end
local time = GetTime()
-- TODO: Remove?
local status, result = pcall(GetLootRollTimeLeft, parent.rollid)
if not status or result == 0 then
local ended = parent.rollended
if ended then
if time - ended > 10 then
anchor:Pop(parent)
end
else
parent.rollended = time
end
end
-- /TODO
local remaining = self.expires - time
if remaining < -4 then
anchor:Pop(parent)
else
local now, length = max(remaining, -1), self.length
self.spark:SetPoint('CENTER', self, 'LEFT', (now / length) * self:GetWidth(), 0)
self:SetValue(now)
self.spark:Show()
if opt.show_time_remaining then
if remaining >= 0 then
parent.text_time:SetText(sf('%.0f', max(0, remaining)))
parent.text_time:Show()
else
parent.text_time:Hide()
end
end
end
end
function RollFramePrototype:Popped()
rolls[self.rollid] = nil
end
-- Create roll frame
function RollFramePrototype:New()
-- Base frame
local frame = self:_New(CreateFrame('Button', nil, UIParent))
frame:SetFrameLevel(anchor:GetFrameLevel())
frame:SetHeight(24)
frame:RegisterForClicks('LeftButtonUp', 'RightButtonUp')
frame:SetScript('OnEnter', self.OnEnter)
frame:SetScript('OnLeave', self.OnLeave)
frame:SetScript('OnClick', self.OnClick)
-- Overlay (For skin border)
local overlay = CreateFrame('frame', nil, frame, BackdropTemplateMixin and "BackdropTemplate")
overlay:SetFrameLevel(frame:GetFrameLevel())
overlay:SetAllPoints()
frame.overlay = overlay
local skin = Skinner:Skin(overlay, 'row')
-- Item icon (For skin border)
local icon_frame = CreateFrame('Frame', nil, frame)
icon_frame:SetPoint('LEFT', 0, 0)
icon_frame:SetWidth(28)
icon_frame:SetHeight(28)
frame.icon_frame = icon_frame
Skinner:Skin(icon_frame, 'item')
-- 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
-- Timer bar
local bar = CreateFrame('StatusBar', nil, frame)
bar:SetFrameLevel(frame:GetFrameLevel())
local pad = skin.padding or 2
bar:SetPoint('TOPRIGHT', -pad - 3, -pad - 3)
bar:SetPoint('BOTTOMRIGHT', -pad - 3, pad + 3)
bar:SetPoint('LEFT', icon_frame, 'RIGHT', -pad, 0)
bar:SetStatusBarTexture(skin.bar_texture)
bar:SetScript('OnUpdate', self.OnBarUpdate)
bar.parent = frame
frame.bar = bar
-- Reference bar for quick re-skinning when XLoot skin changes
table.insert(addon.bars, bar)
local spark = bar:CreateTexture(nil, 'OVERLAY')
spark:SetWidth(14)
spark:SetHeight(38)
spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
spark:SetBlendMode('ADD')
bar.spark = spark
-- Bind text
local bind = icon_frame:CreateFontString(nil, 'OVERLAY')
bind:SetPoint('BOTTOM', 0, 1)
frame.text_bind = bind
-- Time text
local time = icon_frame:CreateFontString(nil, 'OVERLAY')
time:SetPoint('CENTER', 0, 2)
frame.text_time = time
-- Item level
local ilvl = icon_frame:CreateFontString(nil, 'OVERLAY')
ilvl:SetPoint('TOPLEFT', 3, -3)
frame.text_ilvl = ilvl
-- Roll buttons
local n = RollButtonPrototype:New(frame, 1, NEED, 'Dice', icon_frame, 3, -1, {.2, 1, .1})
local g = RollButtonPrototype:New(frame, 2, GREED, 'Coin', n, 0, -2, {.1, .2, 1})
local d = RollButtonPrototype:New(frame, 3, ROLL_DISENCHANT, 'DE', g, 0, 2, {.1, .2, 1})
local p_to = d
if not BUILD_HAS_DISENCHANT then
p_to = g
d:Hide()
end
local p = RollButtonPrototype:New(frame, 0, PASS, 'Pass', p_to, 0, 2, {.7, .7, .7})
frame.need, frame.greed, frame.disenchant, frame.pass = n, g, d, p
-- Roll status text
local status = frame:CreateFontString(nil, 'OVERLAY')
status:SetHeight(16)
status:SetJustifyH('LEFT')
status:SetPoint('LEFT', icon_frame, 'RIGHT', 1, 0)
status:SetPoint('RIGHT', p, 'RIGHT', 2, 0)
frame.text_status = status
-- Loot name/link
local loot = frame:CreateFontString(nil, 'OVERLAY')
loot:SetHeight(16)
loot:SetJustifyH('LEFT')
loot:SetPoint('LEFT', p, 'RIGHT', 3, -1)
loot:SetPoint('RIGHT', frame, 'RIGHT', -5, 0)
frame.text_loot = loot
frame:ApplyOptions()
return frame
end
function RollFramePrototype:ApplyOptions()
self:SetWidth(opt.roll_width)
-- Status bar is reskinned with SkinUpdate
self.need:ApplyOptions()
self.greed:ApplyOptions()
self.disenchant:ApplyOptions()
self.pass:ApplyOptions()
self.text_ilvl:SetFont(opt.font, 8, 'OUTLINE')
self.text_bind:SetFont(opt.font, 8, 'THICKOUTLINE')
self.text_time:SetFont(opt.font, 12, 'OUTLINE')
self.text_status:SetFont(opt.font, 12, opt.font_flag)
self.text_loot:SetFont(opt.font, 12, opt.font_flag)
self.text_time:SetShown(opt.show_time_remaining)
self.text_ilvl:SetShown(opt.text_ilvl)
end
end
---------------------------------------------------------------------------
-- AddOn setup and events
---------------------------------------------------------------------------
-- Update skins when XLoot skin changes
function addon:SkinUpdate()
local skin = Skinner:Reskin()
local padding = skin.padding or 2
local p, n = padding + 3, -padding - 3
for _,bar in pairs(addon.bars) do
bar:ClearAllPoints()
bar:SetPoint('TOPRIGHT', n, n)
bar:SetPoint('BOTTOMRIGHT', n, p)
bar:SetPoint('LEFT', bar.parent.icon_frame, 'RIGHT', -padding, 0)
bar:SetStatusBarTexture(skin.bar_texture)
local link = bar.parent.link
if link then
local r, g, b = GetItemQualityColor(select(3, GetItemInfo(link)))
bar.parent.overlay:SetBorderColor(r, g, b)
bar.parent.icon_frame:SetBorderColor(r, g, b)
end
end
end
-- Move anchors when scale changes
function addon:ApplyOptions()
opt = self.opt
anchor:UpdateSVData(opt.roll_anchor)
alert_anchor:UpdateSVData(opt.alert_anchor)
self:SkinUpdate()
anchor:Restack()
for _,frame in pairs(anchor.children) do
if frame.ApplyOptions then
frame:ApplyOptions()
end
end
end
---------------------------------------------------------------------------
-- Test rolls
---------------------------------------------------------------------------
local preview_loot = {
{ 52722, false, true, true, true },
{ 31304, true, false, true, true, 1 },
{ 37254, true, false, false, true, 2, 2 },
{ 13262, true, false, false, false, 4, 4, 4, 69 },
{ 15487, false, true, true, true }
}
-- Activate items
for i, t in ipairs(preview_loot) do
GetItemInfo(t[1])
end
local init, tests, links, StartFakeRoll = false, {}, {}, nil
local deframe = CreateFrame('Frame')
-- Currently only debugs one roll at a time.
function XLootGroup.TestSettings()
local FakeHistory
local schedule = {}
local type_index = { 'need', 'greed', 'disenchant', [0] = 'pass' }
if not init then
print(L.debug_warning)
init = true
local tick = 0
deframe:SetScript('OnUpdate', function(self, elapsed)
tick = tick + elapsed
if tick > 1 then
tick = 0
local time = GetTime()
for k,v in pairs(schedule) do
if v[1] < time then
local e = v
schedule[k] = nil
e[2]()
e[3](unpack(e[4]))
end
end
end
end)
FakeHistory = {
rolls = {},
links = {},
items = {}
}
local function after(seconds, func, target, ...)
table.insert(schedule, { GetTime() + seconds, func, target, {...} } )
end
local function changed(...)
addon:LOOT_HISTORY_ROLL_CHANGED(...)
end
function GetLootRollItemInfo(id)
return unpack(FakeHistory.rolls[id])
end
function GetLootRollItemLink(id)
return FakeHistory.links[id]
end
function GetLootRollTimeLeft()
return 1
end
function RollOnLoot(rollid, rtypeid)
FakeHistory.items[1].players[1][3] = rtypeid
changed(1, 1)
end
function UnitGroupRolesAssigned(player)
local s = math.random(1, 3)
if s == 1 then
return "HEALER"
elseif s == 2 then
return "DAMAGER"
end
return "TANK"
end
function HistoryGetItem(hid)
return unpack(FakeHistory.items[hid].item)
end
function HistoryGetPlayerInfo(hid, pid)
return unpack(FakeHistory.items[hid].players[pid])
end
function StartFakeRoll()
local fake = {}
local item = preview_loot[random(1, #preview_loot)]
local iname, ilink, iquality, _, _, _, _, _, _, itex = GetItemInfo(item[1])
local rollid = #FakeHistory.rolls + 1
fake.item = { rollid, ilink, 5, false, nil, false }
fake.players = {
{ me, select(2, UnitClass('player')), nil, nil, false, true },
{ 'Player1', 'MAGE', nil, nil, false, false },
{ 'Player2', 'PRIEST', nil, nil, false, false },
{ 'Player3', 'WARRIOR', nil, nil, false, false },
{ 'Player4', 'SHAMAN', nil, nil, false, false }
}
FakeHistory.rolls[rollid] = { itex, iname, 1, iquality, select(2, unpack(item)) }
FakeHistory.links[rollid] = ilink
table.insert(FakeHistory.items, 1, fake)
addon:START_LOOT_ROLL(rollid, random(20000, 40000), true)
after(5, function() fake.players[2][3] = 0 end, changed, 1, 2)
after(7, function() fake.players[3][3] = 2 end, changed, 1, 3)
after(9, function() fake.players[4][3] = 3 end, changed, 1, 4)
after(11, function() fake.players[5][3] = 1 end, changed, 1, 5)
end
end
StartFakeRoll()
end
XLoot:SetSlashCommand('xlgd', XLootGroup.TestSettings)