local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB local M = E:GetModule("Misc") --Lua functions local pairs, ipairs, unpack = pairs, ipairs, unpack local find, format = string.find, string.format local tinsert, twipe = table.insert, table.wipe --WoW API / Variables local ChatEdit_InsertLink = ChatEdit_InsertLink local CreateFrame = CreateFrame local CursorOnUpdate = CursorOnUpdate local CursorUpdate = CursorUpdate local DressUpItemLink = DressUpItemLink local GetLootRollItemInfo = GetLootRollItemInfo local GetLootRollItemLink = GetLootRollItemLink local GetLootRollTimeLeft = GetLootRollTimeLeft local IsModifiedClick = IsModifiedClick local ResetCursor = ResetCursor local RollOnLoot = RollOnLoot local SetDesaturation = SetDesaturation local UnitClass = UnitClass local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS local ROLL_DISENCHANT = ROLL_DISENCHANT local POSITION = "TOP" local FRAME_WIDTH, FRAME_HEIGHT = 328, 28 M.RollBars = {} local locale = GetLocale() local rollMessages = locale == "deDE" and { ["(.*) passt automatisch bei (.+), weil [ersi]+ den Gegenstand nicht benutzen kann.$"] = 0, ["(.*) würfelt nicht für: (.+|r)$"] = 0, ["(.*) hat für (.+) 'Bedarf' ausgewählt"] = 1, ["(.*) hat für (.+) 'Gier' ausgewählt"] = 2, ["(.*) hat für '(.+)' Entzauberung gewählt."] = 3, } or locale == "frFR" and { ["(.*) a passé pour : (.+) parce qu'((il)|(elle)) ne peut pas ramasser cette objet.$"] = 0, ["(.*) a passé pour : (.+)"] = 0, ["(.*) a choisi Besoin pour : (.+)"] = 1, ["(.*) a choisi Cupidité pour : (.+)"] = 2, ["(.*) a choisi Désenchantement pour : (.+)"] = 3, } or locale == "zhCN" and { ["(.*)自动放弃了:(.+),因为他无法拾取该物品$"] = 0, ["(.*)自动放弃了:(.+),因为她无法拾取该物品$"] = 0, ["(.*)放弃了:(.+)"] = 0, ["(.*)选择了需求取向:(.+)"] = 1, ["(.*)选择了贪婪取向:(.+)"] = 2, ["(.*)选择了分解取向:(.+)"] = 3, } or locale == "zhTW" and { ["(.*)自動放棄:(.+),因為他無法拾取該物品$"] = 0, ["(.*)自動放棄:(.+),因為她無法拾取該物品$"] = 0, ["(.*)放棄了:(.+)"] = 0, ["(.*)選擇了需求:(.+)"] = 1, ["(.*)選擇了貪婪:(.+)"] = 2, ["(.*)選擇了分解:(.+)"] = 3, } or locale == "ruRU" and { ["(.*) автоматически передает предмет (.+), поскольку не может его забрать"] = 0, ["(.*) пропускает розыгрыш предмета \"(.+)\", поскольку не может его забрать"] = 0, ["(.*) отказывается от предмета (.+)%."] = 0, ["Разыгрывается: (.+)%. (.*): \"Мне это нужно\""] = 1, ["Разыгрывается: (.+)%. (.*): \"Не откажусь\""] = 2, ["Разыгрывается: (.+)%. (.*): \"Распылить\""] = 3, } or locale == "koKR" and { ["(.*)님이 획득할 수 없는 아이템이어서 자동으로 주사위 굴리기를 포기했습니다: (.+)"] = 0, ["(.*)님이 주사위 굴리기를 포기했습니다: (.+)"] = 0, ["(.*)님이 입찰을 선택했습니다: (.+)"] = 1, ["(.*)님이 차비를 선택했습니다: (.+)"] = 2, ["(.*)님이 마력 추출을 선택했습니다: (.+)"] = 3, } or locale == "esES" and { ["^(.*) pasó automáticamente de: (.+) porque no puede despojar este objeto.$"] = 0, ["^(.*) pasó de: (.+|r)$"] = 0, ["(.*) eligió Necesidad para: (.+)"] = 1, ["(.*) eligió Codicia para: (.+)"] = 2, ["(.*) eligió Desencantar para: (.+)"] = 3, } or locale == "esMX" and { ["^(.*) pasó automáticamente de: (.+) porque no puede despojar este objeto.$"] = 0, ["^(.*) pasó de: (.+|r)$"] = 0, ["(.*) eligió Necesidad para: (.+)"] = 1, ["(.*) eligió Codicia para: (.+)"] = 2, ["(.*) eligió Desencantar para: (.+)"] = 3, } or { ["^(.*) automatically passed on: (.+) because s?he cannot loot that item.$"] = 0, ["^(.*) passed on: (.+|r)$"] = 0, ["(.*) has selected Need for: (.+)"] = 1, ["(.*) has selected Greed for: (.+)"] = 2, ["(.*) has selected Disenchant for: (.+)"] = 3 } local rollTypes = { [0] = { tooltipText = PASS, normalTexture = "Interface\\Buttons\\UI-GroupLoot-Pass-Up", pushedTexture = "Interface\\Buttons\\UI-GroupLoot-Pass-Down", highlightTexture = nil, }, [1] = { tooltipText = NEED, newbieText = NEED_NEWBIE, normalTexture = "Interface\\Buttons\\UI-GroupLoot-Dice-Up", pushedTexture = "Interface\\Buttons\\UI-GroupLoot-Dice-Down", highlightTexture = "Interface\\Buttons\\UI-GroupLoot-Dice-Highlight", }, [2] = { tooltipText = GREED, newbieText = GREED_NEWBIE, normalTexture = "Interface\\Buttons\\UI-GroupLoot-Coin-Up", pushedTexture = "Interface\\Buttons\\UI-GroupLoot-Coin-Down", highlightTexture = "Interface\\Buttons\\UI-GroupLoot-Coin-Highlight", }, [3] = { tooltipText = ROLL_DISENCHANT, newbieText = ROLL_DISENCHANT_NEWBIE, normalTexture = "Interface\\Buttons\\UI-GroupLoot-DE-Up", pushedTexture = "Interface\\Buttons\\UI-GroupLoot-DE-Down", highlightTexture = "Interface\\Buttons\\UI-GroupLoot-DE-Highlight", }, } local reasons = { LOOT_ROLL_INELIGIBLE_REASON1, LOOT_ROLL_INELIGIBLE_REASON2, LOOT_ROLL_INELIGIBLE_REASON3, LOOT_ROLL_INELIGIBLE_REASON4, LOOT_ROLL_INELIGIBLE_REASON5, } local function buttonOnEnter(self) GameTooltip:SetOwner(self, "ANCHOR_RIGHT") GameTooltip:SetText(self.tooltipText) if self.newbieText and SHOW_NEWBIE_TIPS == "1" then GameTooltip:AddLine(self.newbieText, 1, 0.82, 0, true) end if self:IsEnabled() == 0 then GameTooltip:AddLine(self.reason, 1, 0.1, 0.1, true) end for playerName, rollData in pairs(self.parent.rollResults) do if self.rollType == rollData[1] and rollData[2] then local classColor = RAID_CLASS_COLORS[rollData[2]] GameTooltip:AddLine(playerName, classColor.r, classColor.g, classColor.b) end end GameTooltip:Show() end local function buttonOnLeave() GameTooltip:Hide() end local function buttonOnClick(self) RollOnLoot(self.parent.rollID, self.rollType) end local function toggleLootButton(self, state, reason, reasonValue) if state then self:Enable() self:SetAlpha(1) self.reason = nil SetDesaturation(self:GetNormalTexture(), false) else self:Disable() self:SetAlpha(0.2) self.reason = reasonValue and format(reasons[reason], reasonValue) or reasons[reason] SetDesaturation(self:GetNormalTexture(), true) end end local function increaseRollCount(self, count) local text = self.text:GetText() if not text or text == "" then self.text:SetText(count or 1) else self.text:SetText(self.text:GetText() + (count or 1)) end end function M:CreateRollButton(parent, rollType) local data = rollTypes[rollType] local button = CreateFrame("Button", nil, parent) button:Size(FRAME_HEIGHT - 4) button:SetNormalTexture(data.normalTexture) button:SetPushedTexture(data.highlightTexture) button:SetHighlightTexture(data.pushedTexture) button:SetMotionScriptsWhileDisabled(true) button:SetScript("OnEnter", buttonOnEnter) button:SetScript("OnLeave", buttonOnLeave) button:SetScript("OnClick", buttonOnClick) button.ToggleLootButton = toggleLootButton button.IncreaseRollCount = increaseRollCount button.parent = parent button.rollType = rollType button.tooltipText = data.tooltipText button.newbieText = data.newbieText button.text = button:CreateFontString(nil, nil) button.text:FontTemplate(E.Media.Fonts.Homespun, nil, "MONOCHROMEOUTLINE") return button end local function itemOnEnter(self) GameTooltip:SetOwner(self, POSITION == "TOP" and "ANCHOR_BOTTOMLEFT" or "ANCHOR_TOPLEFT") GameTooltip:SetLootRollItem(self.rollID) CursorUpdate(self) end local function itemOnLeave() GameTooltip:Hide() ResetCursor() end local function itemOnUpdate(self) if GameTooltip:IsOwned(self) then GameTooltip:SetOwner(self, POSITION == "TOP" and "ANCHOR_BOTTOMLEFT" or "ANCHOR_TOPLEFT") GameTooltip:SetLootRollItem(self.rollID) end CursorOnUpdate(self) end local function itemOnClick(self) if IsModifiedClick("CHATLINK") then ChatEdit_InsertLink(self.link) elseif IsModifiedClick("DRESSUP") then DressUpItemLink(self.link) end end local function statusbarOnUpdate(self) local timeLeft = GetLootRollTimeLeft(self.parent.rollID) if timeLeft < 0 or timeLeft > self.parent.rollTime then timeLeft = 0 else self.spark:Point("CENTER", self, "LEFT", (timeLeft / self.parent.rollTime) * self:GetWidth(), 0) end self:SetValue(timeLeft) end function M:CreateRollFrame() self.numFrames = self.numFrames + 1 local frame = CreateFrame("Frame", format("ElvUI_GroupLootFrame%d", self.numFrames), E.UIParent) frame:Size(FRAME_WIDTH, FRAME_HEIGHT) frame:SetTemplate() frame:SetFrameStrata("DIALOG") frame:Hide() if POSITION == "TOP" then frame:Point("TOP", self.numFrames > 1 and self.RollBars[self.numFrames - 1] or AlertFrameHolder, "BOTTOM", 0, -4) else frame:Point("BOTTOM", self.numFrames > 1 and self.RollBars[self.numFrames - 1] or AlertFrameHolder, "TOP", 0, 4) end local itemButton = CreateFrame("Button", "$parentIconFrame", frame) itemButton:Size(FRAME_HEIGHT - (E.Border * 2)) itemButton:Point("RIGHT", frame, "LEFT", -(E.Spacing * 3), 0) itemButton:CreateBackdrop() itemButton:SetScript("OnEnter", itemOnEnter) itemButton:SetScript("OnLeave", itemOnLeave) itemButton:SetScript("OnUpdate", itemOnUpdate) itemButton:SetScript("OnClick", itemOnClick) itemButton.hasItem = 1 frame.itemButton = itemButton itemButton.icon = itemButton:CreateTexture(nil, "OVERLAY") itemButton.icon:SetAllPoints() itemButton.icon:SetTexCoord(unpack(E.TexCoords)) local fade = frame:CreateTexture(nil, "BORDER") fade:Point("TOPLEFT", frame, "TOPLEFT", 4, 0) fade:Point("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 0) fade:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") fade:SetBlendMode("ADD") fade:SetGradientAlpha("VERTICAL", 0.1, 0.1, 0.1, 0, 0.1, 0.1, 0.1, 0) frame.fade = fade local status = CreateFrame("StatusBar", "$parentStatusBar", frame) status:SetInside() status:SetFrameLevel(status:GetFrameLevel() - 1) status:SetStatusBarTexture(E.media.normTex) status:SetStatusBarColor(0.8, 0.8, 0.8, 0.9) status.parent = frame E:RegisterStatusBar(status) status:SetScript("OnUpdate", statusbarOnUpdate) frame.status = status status.bg = status:CreateTexture(nil, "BACKGROUND") status.bg:SetAlpha(0.1) status.bg:SetAllPoints() local spark = frame:CreateTexture(nil, "OVERLAY") spark:Size(14, FRAME_HEIGHT) spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark") spark:SetBlendMode("ADD") status.spark = spark frame.passButton = self:CreateRollButton(frame, 0) frame.needButton = self:CreateRollButton(frame, 1) frame.greedButton = self:CreateRollButton(frame, 2) frame.disenchantButton = self:CreateRollButton(frame, 3) frame.needButton:SetHitRectInsets(0, 0, -2, 0) frame.greedButton:SetHitRectInsets(0, 0, -3, 1) frame.disenchantButton:SetHitRectInsets(0, 0, -2, 0) frame.passButton:SetHitRectInsets(0, 0, 0, -2) frame.needButton:Point("LEFT", frame.itemButton, "RIGHT", 4, -1) frame.greedButton:Point("LEFT", frame.needButton, "RIGHT", 1, -1) frame.disenchantButton:Point("LEFT", frame.greedButton, "RIGHT", 0, 1) frame.passButton:Point("LEFT", frame.disenchantButton, "RIGHT", 0, 2) frame.needButton.text:Point("CENTER", -1, 4) frame.greedButton.text:Point("CENTER", 1, 5) frame.disenchantButton.text:Point("CENTER", 1, 4) frame.passButton.text:Point("CENTER", 1, 2) frame.bindText = frame:CreateFontString() frame.bindText:Point("LEFT", frame.passButton, "RIGHT", 2, 0) frame.bindText:FontTemplate(nil, nil, "OUTLINE") local itemName = frame:CreateFontString(nil, "ARTWORK") itemName:FontTemplate(nil, nil, "OUTLINE") itemName:Point("LEFT", frame.bindText, "RIGHT", 1, 0) itemName:Point("RIGHT", frame, "RIGHT", -5, 0) itemName:Size(200, 10) itemName:SetJustifyH("LEFT") frame.itemName = itemName frame.rollResults = {} frame.rollButtons = { [0] = frame.passButton, [1] = frame.needButton, [2] = frame.greedButton, [3] = frame.disenchantButton, } tinsert(self.RollBars, frame) return frame end function M:ReleaseFrame(frame) frame:Hide() frame.rollID = nil frame.rollTime = nil for i = 0, 3 do frame.rollButtons[i].text:SetText("") end twipe(frame.rollResults) end function M:GetFrame() for _, frame in ipairs(M.RollBars) do if not frame.rollID then return frame end end return self:CreateRollFrame() end function M:START_LOOT_ROLL(_, rollID, rollTime) local f = self:GetFrame() f.rollID = rollID f.rollTime = rollTime local texture, name, count, quality, bindOnPickUp, canNeed, canGreed, canDisenchant, reasonNeed, reasonGreed, reasonDisenchant, deSkillRequired = GetLootRollItemInfo(rollID) f.itemButton.icon:SetTexture(texture) f.itemButton.rollID = rollID f.itemButton.link = GetLootRollItemLink(rollID) if count > 1 then f.itemName:SetFormattedText("%dx %s", count, name) else f.itemName:SetText(name) end f.status:SetMinMaxValues(0, rollTime) f.status:SetValue(rollTime) local color = ITEM_QUALITY_COLORS[quality] f.status:SetStatusBarColor(color.r, color.g, color.b, 0.7) f.status.bg:SetTexture(color.r, color.g, color.b) f.bindText:SetText(bindOnPickUp and "BoP" or "BoE") f.bindText:SetVertexColor(bindOnPickUp and 1 or 0.3, bindOnPickUp and 0.3 or 1, bindOnPickUp and 0.1 or 0.3) f.needButton:ToggleLootButton(canNeed, reasonNeed) f.greedButton:ToggleLootButton(canGreed, reasonGreed) f.disenchantButton:ToggleLootButton(canDisenchant, reasonDisenchant, deSkillRequired) f:Show() AlertFrame_FixAnchors() if E.db.general.autoRoll and E.mylevel == MAX_PLAYER_LEVEL and quality == 2 and not bindOnPickUp then if canDisenchant then RollOnLoot(rollID, 3) else RollOnLoot(rollID, 2) end end end function M:CANCEL_LOOT_ROLL(_, rollID) for _, frame in ipairs(self.RollBars) do if frame.rollID == rollID then self:ReleaseFrame(frame) E:StaticPopup_Hide("CONFIRM_LOOT_ROLL", self.rollID) break end end end function M:ParseRollChoice(msg) for regex, rollType in pairs(rollMessages) do local _, _, playerName, itemName = find(msg, regex) if playerName and itemName and playerName ~= "Everyone" then if locale == "ruRU" and rollType ~= 0 then playerName, itemName = itemName, playerName end return playerName, itemName, rollType end end end function M:CHAT_MSG_LOOT(_, msg) local playerName, itemName, rollType = self:ParseRollChoice(msg) if playerName and itemName then local _, class = UnitClass(playerName) for _, frame in ipairs(self.RollBars) do if frame.rollID and frame.itemButton.link == itemName and not frame.rollResults[playerName] then frame.rollResults[playerName] = {rollType, class} frame.rollButtons[rollType]:IncreaseRollCount() break end end end end function M:LoadLootRoll() if not E.private.general.lootRoll then return end self.numFrames = 0 self:RegisterEvent("CHAT_MSG_LOOT") self:RegisterEvent("START_LOOT_ROLL") self:RegisterEvent("CANCEL_LOOT_ROLL") UIParent:UnregisterEvent("START_LOOT_ROLL") UIParent:UnregisterEvent("CANCEL_LOOT_ROLL") for i = 1, NUM_GROUP_LOOT_FRAMES do _G["GroupLootFrame"..i]:UnregisterEvent("CANCEL_LOOT_ROLL") end end