-- 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 local GetItemInfo = C_Item and C_Item.GetItemInfo or GetItemInfo ------------------------------------------------------------------------------- -- 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() if BUILD_NUMBER >= 100000 then print("XLoot Group does not yet work on this version and will not be loaded") return end -- Register events eframe:RegisterEvent('START_LOOT_ROLL') eframe:RegisterEvent('MODIFIER_STATE_CHANGED') -- if BUILD_HAS_TRANSMOG_GREED or C_Item then -- 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 = C_Item.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 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 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 if top_type == 'need' then player = [[|TInterface\Buttons\UI-GroupLoot-Dice-Up:16:16:-1:-1|t]]..player elseif top_type == 'greed' then player = [[|TInterface\Buttons\UI-GroupLoot-Coin-Up:16:16:-1:-2|t]]..player 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() 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 = C_Item.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)