diff --git a/LookingForSatchels.lua b/LookingForSatchels.lua new file mode 100644 index 0000000..dc4e0a1 --- /dev/null +++ b/LookingForSatchels.lua @@ -0,0 +1,616 @@ +SatchelQueue = LibStub("AceAddon-3.0"):NewAddon("Looking for Satchels", "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0", "AceTimer-3.0") +local SatchelQueue = SatchelQueue + +local BUTTON_TEXT = "Satchel" +local BUTTON_QUEUED = "Cancel" + +local timer = nil + +local sound_all = nil +local sound_bg = nil + +local should_auto_loot = false + +StaticPopupDialogs["SATCHEL_QUEUE"] = { + text = "", + button1 = ACCEPT, + button2 = DECLINE, + OnAccept = function(self) + SetLFGDungeon(LE_LFG_CATEGORY_LFD, self.data) + JoinLFG(LE_LFG_CATEGORY_LFD) + end, + OnShow = function(self) + local name = GetLFGDungeonInfo(self.data) + self.text:SetFormattedText(LFG_CALL_TO_ARMS .. name, "\n") -- :( + end, + hideOnEscape = 1, + whileDead = 1 +} + +function SatchelQueue:OnInitialize() + local defaults = { + global = { + icon = true, + tooltip = false, + requeue = false, + prevent_auto_loot = true, + fake_auto_loot = false, + prioritise = false, + exclusive = false, + flash_enable = false, + sound_enable = true, + sound_force = false, + sound_file = "Sound/Spells/Clearcasting_Impact_Chest.wav", + + saved_uids = {}, + items = {}, + count = 0, + coin_min = -1, + coin_max = -1, + coin_total = 0 + } + } + self.db = LibStub("AceDB-3.0"):New("SatchelQueueDB", defaults, true) + + local option_table = { + type = "group", + set = function(info, val) self.db.global[info[#info]] = val end, + get = function(info) return self.db.global[info[#info]] end, + args = { + header_queueing = { + order = 0, + type = "header", + name = "Queueing" + }, + prioritise = { + order = 1, + type = "toggle", + width = "double", + name = "Prioritise newer dungeons" + }, + exclusive = { + order = 2, + type = "toggle", + name = "Exclusively", + desc = "Oueue exclusively for the newer dungeons when prioritised", + disabled = function(info) return not self.db.global.prioritise end + }, + requeue = { + order = 3, + type = "toggle", + width = "full", + name = "Requeue after leaving" + }, + icon = { + order = 4, + type = "toggle", + width = "full", + name = "Enable LFG minimap icon while waiting" + }, + header_satchel = { + order = 5, + type = "header", + name = "Satchels" + }, + prevent_auto_loot = { + order = 6, + type = "toggle", + width = "double", + name = "Prevent auto loot" + }, + fake_auto_loot = { + order = 7, + type = "toggle", + name = "Only protect BoP items", + desc = "Non-BoP items will still be looted", + disabled = function(info) return not self.db.global.prevent_auto_loot end + }, + tooltip = { + order = 8, + type = "toggle", + width = "double", + name = "Enable contents in tooltip" + }, + header_notification = { + order = 9, + type = "header", + name = "Notification" + }, + flash_enable = { + order = 10, + type = "toggle", + width = "full", + name = "Enable Screen Flash" + }, + sound_enable = { + order = 11, + type = "toggle", + width = "double", + name = "Enable Sound" + }, + sound_force = { + order = 12, + type = "toggle", + name = "Force unmute for alert", + disabled = function(info) return not self.db.global.sound_enable end + }, + sound_file = { + order = 13, + type = "input", + width = "full", + name = "Sound File", + disabled = function(info) return not self.db.global.sound_enable end + } + } + } + local stats_table = { + type = "group", + args = { + text = { + type = "input", + name = "Statistics", + width = "full", + multiline = 24, + get = function(info) + local s = "total satchels: " .. self.db.global.count .. "\n" + if self.db.global.coin_min > 0 then + s = s .. "\ntotal gold: " .. GetMoneyString(self.db.global.coin_total) + s = s .. "\nmin: " .. GetMoneyString(self.db.global.coin_min) + s = s .. "\nmax: " .. GetMoneyString(self.db.global.coin_max) + s = s .. "\navg: " .. GetMoneyString(self.db.global.coin_total / self.db.global.count) .. "\n" + end + s = s .. "\nitems:" + local items = {} + for k, v in pairs(self.db.global.items) do + table.insert(items, { id = k, count = v }) + end + table.sort(items, function(a, b) return a.count > b.count end) + for i = 1, #items do + s = s .. "\n" .. (GetItemInfo(items[i].id) or "<item not cached>") .. ": " .. items[i].count + end + return s + end, + set = nil + }, + reset = { + type = "execute", + name = "reload", + width = "full", + func = function(info) + LibStub("AceConfigRegistry-3.0"):NotifyChange("Statistics") + end + } + } + } + + LibStub("AceConfig-3.0"):RegisterOptionsTable("Looking for Satchels", option_table) + + local option_frame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Looking for Satchels", "Looking for Satchels") + + self:RegisterChatCommand("satchel", function(...) InterfaceOptionsFrame_OpenToCategory(option_frame) end) + +end + +local function SatchelID(link) + if not link then + return nil + end + return select(3, string.find(link, "|Hitem:69903:0:0:0:0:0:0:(%d+):")) or select(3, string.find(link, "|Hitem:90818:0:0:0:0:0:0:(%d+):")) or select(3, string.find(link, "|Hitem:104260:0:0:0:0:0:0:(%d+):")) or nil +end + +local function Satchel_OnLeave(self, motion) + SatchelQueue:Unhook(self, "OnLeave") + SatchelQueue:Unhook(self, "PreClick") + SatchelQueue:Unhook(self, "PostClick") +end + +local toggle_loot, default_loot = nil, nil +local function Satchel_PreClick(self, button) + if button == "RightButton" and GetCVarBool("autoLootDefault") ~= IsModifiedClick("AUTOLOOTTOGGLE") then + should_auto_loot = true + toggle_loot, default_loot = true, GetCVarBool("autoLootDefault") + SetCVar("autoLootDefault", default_loot and 0 or 1) + end +end + +local function Satchel_PostClick(self, button) + if toggle_loot then + toggle_loot = false + SetCVar("autoLootDefault", default_loot) + end +end + +local function Satchel_OnEnter(self, motion) + if SatchelQueue.db.global.prevent_auto_loot and not SatchelQueue:IsHooked(self, "OnLeave") and SatchelID(GetContainerItemLink(self:GetParent():GetID(), self:GetID())) then + SatchelQueue:HookScript(self, "OnLeave", Satchel_OnLeave) + SatchelQueue:HookScript(self, "PreClick", Satchel_PreClick) + SatchelQueue:HookScript(self, "PostClick", Satchel_PostClick) + end +end + +local function Satchel_TooltipText(self) + local _, link = self:GetItem() + local id = link and SatchelID(link) or nil + if id and SatchelQueue.db.global.tooltip then + if type(SatchelQueue.db.global.saved_uids[id]) == "table" then + self:AddLine(" ") + for slot = 1, #SatchelQueue.db.global.saved_uids[id] do + local content = SatchelQueue.db.global.saved_uids[id][slot] + if content < 0 then + self:AddLine("|cffffffff" .. GetCoinTextureString(-content) .. "|r") + else + local name, _, quality = GetItemInfo(content) + if name then + local r, g, b = GetItemQualityColor(quality) + self:AddLine(name, r, g, b) + end + end + end + end + end +end + +function SatchelQueue:OnEnable() + self:Hook("ContainerFrameItemButton_OnEnter", Satchel_OnEnter, true) + self:HookScript(GameTooltip, "OnTooltipSetItem", Satchel_TooltipText) + self:SecureHook("QueueStatusFrame_Update", "UpdateStatus") + self:RegisterEvent("LOOT_OPENED") + self:RegisterEvent("LOOT_SLOT_CLEARED") + SatchelQueue_Button:Show() +end + +function SatchelQueue:OnDisable() + self:UnregisterEvent("LOOT_OPENED") + self:UnregisterEvent("LOOT_SLOT_CLEARED") + self:TimerStop() + SatchelQueue_Button:Hide() + self:UnhookAll() +end + +function SatchelQueue:Toggle() + if timer then + self:TimerStop() + else + self:TimerStart() + end + self:UpdateStatus(QueueStatusFrame) +end + +function SatchelQueue:TimerStart() + if not timer then + self:RegisterEvent("LFG_UPDATE_RANDOM_INFO") + timer = self:ScheduleRepeatingTimer(RequestLFDPlayerLockInfo, LFD_STATISTIC_CHANGE_TIME) + SatchelQueue_Button:SetText(BUTTON_QUEUED) + RequestLFDPlayerLockInfo() + end +end + +local function ResetSound() + if sound_all then + SetCVar("Sound_EnableAllSound", sound_all) + SetCVar("Sound_EnableSoundWhenGameIsInBG", sound_bg) + + sound_all = nil + sound_bg = nil + end +end + +function SatchelQueue:TimerStop() + if timer then + self:UnregisterEvent("LFG_UPDATE_RANDOM_INFO") + self:CancelTimer(timer, true) + timer = nil; + SatchelQueue_Button:SetText(BUTTON_TEXT) + ResetSound() + end +end + +-- code copied from Blizzard's QueueStatusFrame_Update, may be it can be hooked somehow instead? +function SatchelQueue:UpdateStatus(self) + local showMinimapButton, animateEye; + + local nextEntry = 1; + + local totalHeight = 4; --Add some buffer height + + --Try each LFG type + for i=1, NUM_LE_LFG_CATEGORYS do + local mode, submode = GetLFGMode(i); + if ( mode ) then + local entry = QueueStatusFrame_GetEntry(self, nextEntry); + QueueStatusEntry_SetUpLFG(entry, i); + entry:Show(); + totalHeight = totalHeight + entry:GetHeight(); + nextEntry = nextEntry + 1; + + showMinimapButton = true; + if ( mode == "queued" ) then + animateEye = true; + end + end + end + + --Try all PvP queues + for i=1, GetMaxBattlefieldID() do + local status, mapName, instanceID, levelRangeMin, levelRangeMax, teamSize, registeredMatch, eligibleInQueue, waitingOnOtherActivity = GetBattlefieldStatus(i); + if ( status and status ~= "none" ) then + local entry = QueueStatusFrame_GetEntry(self, nextEntry); + QueueStatusEntry_SetUpBattlefield(entry, i); + entry:Show(); + totalHeight = totalHeight + entry:GetHeight(); + nextEntry = nextEntry + 1; + + showMinimapButton = true; + if ( status == "queued" ) then + animateEye = true; + end + end + end + + --Try all World PvP queues + for i=1, MAX_WORLD_PVP_QUEUES do + local status, mapName, queueID = GetWorldPVPQueueStatus(i); + if ( status and status ~= "none" ) then + local entry = QueueStatusFrame_GetEntry(self, nextEntry); + QueueStatusEntry_SetUpWorldPvP(entry, i); + entry:Show(); + totalHeight = totalHeight + entry:GetHeight(); + nextEntry = nextEntry + 1; + + showMinimapButton = true; + if ( status == "queued" ) then + animateEye = true; + end + end + end + + --World PvP areas we're currently in + if ( CanHearthAndResurrectFromArea() ) then + local entry = QueueStatusFrame_GetEntry(self, nextEntry); + QueueStatusEntry_SetUpActiveWorldPVP(entry); + entry:Show(); + totalHeight = totalHeight + entry:GetHeight(); + nextEntry = nextEntry + 1; + + showMinimapButton = true; + end + + --Pet Battle PvP Queue + local pbStatus = C_PetBattles.GetPVPMatchmakingInfo(); + if ( pbStatus ) then + local entry = QueueStatusFrame_GetEntry(self, nextEntry); + QueueStatusEntry_SetUpPetBattlePvP(entry); + entry:Show(); + totalHeight = totalHeight + entry:GetHeight(); + nextEntry = nextEntry + 1; + + showMinimapButton = true; + if ( pbStatus == "queued" ) then + animateEye = true; + end + end + + --Satchel Queue + if SatchelQueue.db.global.icon then + local mode = GetLFGMode(LE_LFG_CATEGORY_LFD) + if not mode and timer then + local entry = QueueStatusFrame_GetEntry(self, nextEntry); + QueueStatusEntry_SetMinimalDisplay(entry, "Looking for Satchels", QUEUED_STATUS_QUEUED); + entry:Show(); + totalHeight = totalHeight + entry:GetHeight(); + nextEntry = nextEntry + 1; + + showMinimapButton = true; + animateEye = true; + end + end + + --Hide all remaining entries. + for i=nextEntry, #self.StatusEntries do + self.StatusEntries[i]:Hide(); + end + + --Update the size of this frame to fit everything + self:SetHeight(totalHeight); + + --Update the minimap icon + if ( showMinimapButton ) then + QueueStatusMinimapButton:Show(); + else + QueueStatusMinimapButton:Hide(); + end + + if ( animateEye ) then + EyeTemplate_StartAnimating(QueueStatusMinimapButton.Eye); + else + EyeTemplate_StopAnimating(QueueStatusMinimapButton.Eye); + end +end + +local function CheckQueueReward(dungeonID) + local leaderChecked, tankChecked, healerChecked, damageChecked = LFDQueueFrame_GetRoles() + local eligible, forTank, forHealer, forDamage, itemCount = GetLFGRoleShortageRewards(dungeonID, LFG_ROLE_SHORTAGE_RARE) + return eligible and itemCount > 0 and ((tankChecked and forTank) or (healerChecked and forHealer) or (damageChecked and forDamage)) +end + +local function CheckQueuePopReward(dungeonID, role) + local eligible, forTank, forHealer, forDamage, itemCount = GetLFGRoleShortageRewards(dungeonID, LFG_ROLE_SHORTAGE_RARE) + return eligible and itemCount > 0 and ((role == "TANK" and forTank) or (role == "HEALER" and forHealer) or (role == "DAMAGER" and forDamage)) +end + +function SatchelQueue:LFG_UPDATE_RANDOM_INFO() + if not timer then + return + end + + ResetSound() + + local mode, submode = GetLFGMode(LE_LFG_CATEGORY_LFD) + + if not mode then + local first, last, step = 1, GetNumRandomDungeons(), 1 + if self.db.global.prioritise then + first, last, step = last, first, -1 + + if self.db.global.exclusive then + last = first + end + end + + for i = first, last, step do + local id = GetLFGRandomDungeonInfo(i) + if IsLFGDungeonJoinable(id) and CheckQueueReward(id) then + --SetLFGDungeon(LE_LFG_CATEGORY_LFD, id) + --JoinLFG(LE_LFG_CATEGORY_LFD) + StaticPopup_Show("SATCHEL_QUEUE", nil, nil, id) + return + end + end + StaticPopup_Hide("SATCHEL_QUEUE") + elseif mode == "proposal" then + if submode == "unaccepted" then + local _, dungeonID, _, _, _, _, role = GetLFGProposal() + if CheckQueuePopReward(dungeonID, role) then + if self.db.global.sound_enable then + if self.db.global.sound_force then + if not sound_all then + sound_all = GetCVar("Sound_EnableAllSound") + sound_bg = GetCVar("Sound_EnableSoundWhenGameIsInBG") + end + + SetCVar("Sound_EnableAllSound", "1") + SetCVar("Sound_EnableSoundWhenGameIsInBG", "1") + end + PlaySoundFile(self.db.global.sound_file, "MASTER") + end + if self.db.global.flash_enable then + UIFrameFlash(SatchelQueue_Flash, 0.5, 0.5, 10.0, false, 0.0, 0.0) + end + elseif GetNumSubgroupMembers() == 0 then + RejectProposal() + end + end + elseif mode == "lfgparty" then + if not self.db.global.requeue then + self:TimerStop() + end + end +end + +--hack: if item is locked, it is being used. only check satchels +local function GuessOpenSatchel() + for bag = 0, NUM_BAG_SLOTS do + for slot = 1, GetContainerNumSlots(bag) do + local texture, count, locked, quality, readable, lootable, link = GetContainerItemInfo(bag, slot) + if locked and lootable then + local uid = SatchelID(GetContainerItemLink(bag, slot)) + if uid then + return uid + end + end + end + end + return nil +end + +--gold parsing liberated from Wowhead Looter (http://www.wowhead.com/client) +local WL_CURRENCY = { + ["1"] = COPPER_AMOUNT:gsub("%%d ", ""), + ["100"] = SILVER_AMOUNT:gsub("%%d ", ""), + ["10000"] = GOLD_AMOUNT:gsub("%%d ", ""), +}; + +local function wlParseCoin(strCoin) + local coin = 0; + for k, v in pairs(WL_CURRENCY) do + local found, _, a = strCoin:find("(%d+) "..v); + if found then + coin = coin + a * tonumber(k); + end + end + + return coin; +end + +local function IsBindOnPickup(link) + SatchelQueue_Tooltip:SetOwner(UIParent, "ANCHOR_NONE") + SatchelQueue_Tooltip:SetHyperlink(link) + + for _,region in ipairs({SatchelQueue_Tooltip:GetRegions()}) do + if region and region:GetObjectType() == "FontString" then + if region:GetText() == ITEM_BIND_ON_PICKUP then + return 1 + end + end + end + return nil +end + +function SatchelQueue:LOOT_OPENED() + local uid = GuessOpenSatchel() + if uid then + if not self.db.global.saved_uids[uid] then + self.db.global.count = self.db.global.count + 1 + + for slot = 1, GetNumLootItems() do + local slotType = GetLootSlotType(slot) + local texture, item, quantity, quality, locked = GetLootSlotInfo(slot) + + if slotType == LOOT_SLOT_MONEY then + local coin = wlParseCoin(item) + self.db.global.coin_total = self.db.global.coin_total + coin + + if self.db.global.coin_min == -1 then + self.db.global.coin_min = coin + self.db.global.coin_max = coin + else + self.db.global.coin_min = min(self.db.global.coin_min, coin) + self.db.global.coin_max = max(self.db.global.coin_max, coin) + end + elseif slotType == LOOT_SLOT_ITEM then + local _, _, id = string.find(GetLootSlotLink(slot), "|Hitem:(%d+):") + self.db.global.items[id] = (self.db.global.items[id] or 0) + quantity + end + end + end + + local closing = nil + if self.db.global.prevent_auto_loot and self.db.global.fake_auto_loot and should_auto_loot then + should_auto_loot = false + closing = true + + for slot = 1, GetNumLootItems() do + if GetLootSlotType(slot) == LOOT_SLOT_MONEY or not IsBindOnPickup(GetLootSlotLink(slot)) then + LootSlot(slot) + end + end + if GetNumLootItems() == 0 then + self.db.global.saved_uids[uid] = nil + end + end + + --rebuild stored items + if GetNumLootItems() > 0 then + self.db.global.saved_uids[uid] = {} + for slot = 1, GetNumLootItems() do + local _, item = GetLootSlotInfo(slot) + if item then --we havent just looted the slot + self.db.global.saved_uids[uid][slot] = GetLootSlotType(slot) == LOOT_SLOT_MONEY and -wlParseCoin(item) or tonumber(select(3, string.find(GetLootSlotLink(slot), "|Hitem:(%d+):"))) + end + end + end + + if closing then + CloseLoot() + end + end +end + +function SatchelQueue:LOOT_SLOT_CLEARED() + local uid = GuessOpenSatchel() + if uid then + if GetNumLootItems() == 1 and not GetLootSlotInfo(1) then + self.db.global.saved_uids[uid] = nil + end + end +end diff --git a/LookingforSatchels.toc b/LookingforSatchels.toc index 51e0b82..1f4d8a8 100644 --- a/LookingforSatchels.toc +++ b/LookingforSatchels.toc @@ -2,10 +2,10 @@ ## Title: Looking for Satchels ## Notes: Queues for Satchels! ## Author: Bediko -## Version: 0.1.2 +## Version: 0.1.7 ## SavedVariables: SatchelQueueDB embeds.xml -Looking for Satchels.lua +LookingForSatchels.lua SatchelQueue.xml diff --git a/LookingforStachels.lua b/LookingforStachels.lua deleted file mode 100644 index dc4e0a1..0000000 --- a/LookingforStachels.lua +++ /dev/null @@ -1,616 +0,0 @@ -SatchelQueue = LibStub("AceAddon-3.0"):NewAddon("Looking for Satchels", "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0", "AceTimer-3.0") -local SatchelQueue = SatchelQueue - -local BUTTON_TEXT = "Satchel" -local BUTTON_QUEUED = "Cancel" - -local timer = nil - -local sound_all = nil -local sound_bg = nil - -local should_auto_loot = false - -StaticPopupDialogs["SATCHEL_QUEUE"] = { - text = "", - button1 = ACCEPT, - button2 = DECLINE, - OnAccept = function(self) - SetLFGDungeon(LE_LFG_CATEGORY_LFD, self.data) - JoinLFG(LE_LFG_CATEGORY_LFD) - end, - OnShow = function(self) - local name = GetLFGDungeonInfo(self.data) - self.text:SetFormattedText(LFG_CALL_TO_ARMS .. name, "\n") -- :( - end, - hideOnEscape = 1, - whileDead = 1 -} - -function SatchelQueue:OnInitialize() - local defaults = { - global = { - icon = true, - tooltip = false, - requeue = false, - prevent_auto_loot = true, - fake_auto_loot = false, - prioritise = false, - exclusive = false, - flash_enable = false, - sound_enable = true, - sound_force = false, - sound_file = "Sound/Spells/Clearcasting_Impact_Chest.wav", - - saved_uids = {}, - items = {}, - count = 0, - coin_min = -1, - coin_max = -1, - coin_total = 0 - } - } - self.db = LibStub("AceDB-3.0"):New("SatchelQueueDB", defaults, true) - - local option_table = { - type = "group", - set = function(info, val) self.db.global[info[#info]] = val end, - get = function(info) return self.db.global[info[#info]] end, - args = { - header_queueing = { - order = 0, - type = "header", - name = "Queueing" - }, - prioritise = { - order = 1, - type = "toggle", - width = "double", - name = "Prioritise newer dungeons" - }, - exclusive = { - order = 2, - type = "toggle", - name = "Exclusively", - desc = "Oueue exclusively for the newer dungeons when prioritised", - disabled = function(info) return not self.db.global.prioritise end - }, - requeue = { - order = 3, - type = "toggle", - width = "full", - name = "Requeue after leaving" - }, - icon = { - order = 4, - type = "toggle", - width = "full", - name = "Enable LFG minimap icon while waiting" - }, - header_satchel = { - order = 5, - type = "header", - name = "Satchels" - }, - prevent_auto_loot = { - order = 6, - type = "toggle", - width = "double", - name = "Prevent auto loot" - }, - fake_auto_loot = { - order = 7, - type = "toggle", - name = "Only protect BoP items", - desc = "Non-BoP items will still be looted", - disabled = function(info) return not self.db.global.prevent_auto_loot end - }, - tooltip = { - order = 8, - type = "toggle", - width = "double", - name = "Enable contents in tooltip" - }, - header_notification = { - order = 9, - type = "header", - name = "Notification" - }, - flash_enable = { - order = 10, - type = "toggle", - width = "full", - name = "Enable Screen Flash" - }, - sound_enable = { - order = 11, - type = "toggle", - width = "double", - name = "Enable Sound" - }, - sound_force = { - order = 12, - type = "toggle", - name = "Force unmute for alert", - disabled = function(info) return not self.db.global.sound_enable end - }, - sound_file = { - order = 13, - type = "input", - width = "full", - name = "Sound File", - disabled = function(info) return not self.db.global.sound_enable end - } - } - } - local stats_table = { - type = "group", - args = { - text = { - type = "input", - name = "Statistics", - width = "full", - multiline = 24, - get = function(info) - local s = "total satchels: " .. self.db.global.count .. "\n" - if self.db.global.coin_min > 0 then - s = s .. "\ntotal gold: " .. GetMoneyString(self.db.global.coin_total) - s = s .. "\nmin: " .. GetMoneyString(self.db.global.coin_min) - s = s .. "\nmax: " .. GetMoneyString(self.db.global.coin_max) - s = s .. "\navg: " .. GetMoneyString(self.db.global.coin_total / self.db.global.count) .. "\n" - end - s = s .. "\nitems:" - local items = {} - for k, v in pairs(self.db.global.items) do - table.insert(items, { id = k, count = v }) - end - table.sort(items, function(a, b) return a.count > b.count end) - for i = 1, #items do - s = s .. "\n" .. (GetItemInfo(items[i].id) or "<item not cached>") .. ": " .. items[i].count - end - return s - end, - set = nil - }, - reset = { - type = "execute", - name = "reload", - width = "full", - func = function(info) - LibStub("AceConfigRegistry-3.0"):NotifyChange("Statistics") - end - } - } - } - - LibStub("AceConfig-3.0"):RegisterOptionsTable("Looking for Satchels", option_table) - - local option_frame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("Looking for Satchels", "Looking for Satchels") - - self:RegisterChatCommand("satchel", function(...) InterfaceOptionsFrame_OpenToCategory(option_frame) end) - -end - -local function SatchelID(link) - if not link then - return nil - end - return select(3, string.find(link, "|Hitem:69903:0:0:0:0:0:0:(%d+):")) or select(3, string.find(link, "|Hitem:90818:0:0:0:0:0:0:(%d+):")) or select(3, string.find(link, "|Hitem:104260:0:0:0:0:0:0:(%d+):")) or nil -end - -local function Satchel_OnLeave(self, motion) - SatchelQueue:Unhook(self, "OnLeave") - SatchelQueue:Unhook(self, "PreClick") - SatchelQueue:Unhook(self, "PostClick") -end - -local toggle_loot, default_loot = nil, nil -local function Satchel_PreClick(self, button) - if button == "RightButton" and GetCVarBool("autoLootDefault") ~= IsModifiedClick("AUTOLOOTTOGGLE") then - should_auto_loot = true - toggle_loot, default_loot = true, GetCVarBool("autoLootDefault") - SetCVar("autoLootDefault", default_loot and 0 or 1) - end -end - -local function Satchel_PostClick(self, button) - if toggle_loot then - toggle_loot = false - SetCVar("autoLootDefault", default_loot) - end -end - -local function Satchel_OnEnter(self, motion) - if SatchelQueue.db.global.prevent_auto_loot and not SatchelQueue:IsHooked(self, "OnLeave") and SatchelID(GetContainerItemLink(self:GetParent():GetID(), self:GetID())) then - SatchelQueue:HookScript(self, "OnLeave", Satchel_OnLeave) - SatchelQueue:HookScript(self, "PreClick", Satchel_PreClick) - SatchelQueue:HookScript(self, "PostClick", Satchel_PostClick) - end -end - -local function Satchel_TooltipText(self) - local _, link = self:GetItem() - local id = link and SatchelID(link) or nil - if id and SatchelQueue.db.global.tooltip then - if type(SatchelQueue.db.global.saved_uids[id]) == "table" then - self:AddLine(" ") - for slot = 1, #SatchelQueue.db.global.saved_uids[id] do - local content = SatchelQueue.db.global.saved_uids[id][slot] - if content < 0 then - self:AddLine("|cffffffff" .. GetCoinTextureString(-content) .. "|r") - else - local name, _, quality = GetItemInfo(content) - if name then - local r, g, b = GetItemQualityColor(quality) - self:AddLine(name, r, g, b) - end - end - end - end - end -end - -function SatchelQueue:OnEnable() - self:Hook("ContainerFrameItemButton_OnEnter", Satchel_OnEnter, true) - self:HookScript(GameTooltip, "OnTooltipSetItem", Satchel_TooltipText) - self:SecureHook("QueueStatusFrame_Update", "UpdateStatus") - self:RegisterEvent("LOOT_OPENED") - self:RegisterEvent("LOOT_SLOT_CLEARED") - SatchelQueue_Button:Show() -end - -function SatchelQueue:OnDisable() - self:UnregisterEvent("LOOT_OPENED") - self:UnregisterEvent("LOOT_SLOT_CLEARED") - self:TimerStop() - SatchelQueue_Button:Hide() - self:UnhookAll() -end - -function SatchelQueue:Toggle() - if timer then - self:TimerStop() - else - self:TimerStart() - end - self:UpdateStatus(QueueStatusFrame) -end - -function SatchelQueue:TimerStart() - if not timer then - self:RegisterEvent("LFG_UPDATE_RANDOM_INFO") - timer = self:ScheduleRepeatingTimer(RequestLFDPlayerLockInfo, LFD_STATISTIC_CHANGE_TIME) - SatchelQueue_Button:SetText(BUTTON_QUEUED) - RequestLFDPlayerLockInfo() - end -end - -local function ResetSound() - if sound_all then - SetCVar("Sound_EnableAllSound", sound_all) - SetCVar("Sound_EnableSoundWhenGameIsInBG", sound_bg) - - sound_all = nil - sound_bg = nil - end -end - -function SatchelQueue:TimerStop() - if timer then - self:UnregisterEvent("LFG_UPDATE_RANDOM_INFO") - self:CancelTimer(timer, true) - timer = nil; - SatchelQueue_Button:SetText(BUTTON_TEXT) - ResetSound() - end -end - --- code copied from Blizzard's QueueStatusFrame_Update, may be it can be hooked somehow instead? -function SatchelQueue:UpdateStatus(self) - local showMinimapButton, animateEye; - - local nextEntry = 1; - - local totalHeight = 4; --Add some buffer height - - --Try each LFG type - for i=1, NUM_LE_LFG_CATEGORYS do - local mode, submode = GetLFGMode(i); - if ( mode ) then - local entry = QueueStatusFrame_GetEntry(self, nextEntry); - QueueStatusEntry_SetUpLFG(entry, i); - entry:Show(); - totalHeight = totalHeight + entry:GetHeight(); - nextEntry = nextEntry + 1; - - showMinimapButton = true; - if ( mode == "queued" ) then - animateEye = true; - end - end - end - - --Try all PvP queues - for i=1, GetMaxBattlefieldID() do - local status, mapName, instanceID, levelRangeMin, levelRangeMax, teamSize, registeredMatch, eligibleInQueue, waitingOnOtherActivity = GetBattlefieldStatus(i); - if ( status and status ~= "none" ) then - local entry = QueueStatusFrame_GetEntry(self, nextEntry); - QueueStatusEntry_SetUpBattlefield(entry, i); - entry:Show(); - totalHeight = totalHeight + entry:GetHeight(); - nextEntry = nextEntry + 1; - - showMinimapButton = true; - if ( status == "queued" ) then - animateEye = true; - end - end - end - - --Try all World PvP queues - for i=1, MAX_WORLD_PVP_QUEUES do - local status, mapName, queueID = GetWorldPVPQueueStatus(i); - if ( status and status ~= "none" ) then - local entry = QueueStatusFrame_GetEntry(self, nextEntry); - QueueStatusEntry_SetUpWorldPvP(entry, i); - entry:Show(); - totalHeight = totalHeight + entry:GetHeight(); - nextEntry = nextEntry + 1; - - showMinimapButton = true; - if ( status == "queued" ) then - animateEye = true; - end - end - end - - --World PvP areas we're currently in - if ( CanHearthAndResurrectFromArea() ) then - local entry = QueueStatusFrame_GetEntry(self, nextEntry); - QueueStatusEntry_SetUpActiveWorldPVP(entry); - entry:Show(); - totalHeight = totalHeight + entry:GetHeight(); - nextEntry = nextEntry + 1; - - showMinimapButton = true; - end - - --Pet Battle PvP Queue - local pbStatus = C_PetBattles.GetPVPMatchmakingInfo(); - if ( pbStatus ) then - local entry = QueueStatusFrame_GetEntry(self, nextEntry); - QueueStatusEntry_SetUpPetBattlePvP(entry); - entry:Show(); - totalHeight = totalHeight + entry:GetHeight(); - nextEntry = nextEntry + 1; - - showMinimapButton = true; - if ( pbStatus == "queued" ) then - animateEye = true; - end - end - - --Satchel Queue - if SatchelQueue.db.global.icon then - local mode = GetLFGMode(LE_LFG_CATEGORY_LFD) - if not mode and timer then - local entry = QueueStatusFrame_GetEntry(self, nextEntry); - QueueStatusEntry_SetMinimalDisplay(entry, "Looking for Satchels", QUEUED_STATUS_QUEUED); - entry:Show(); - totalHeight = totalHeight + entry:GetHeight(); - nextEntry = nextEntry + 1; - - showMinimapButton = true; - animateEye = true; - end - end - - --Hide all remaining entries. - for i=nextEntry, #self.StatusEntries do - self.StatusEntries[i]:Hide(); - end - - --Update the size of this frame to fit everything - self:SetHeight(totalHeight); - - --Update the minimap icon - if ( showMinimapButton ) then - QueueStatusMinimapButton:Show(); - else - QueueStatusMinimapButton:Hide(); - end - - if ( animateEye ) then - EyeTemplate_StartAnimating(QueueStatusMinimapButton.Eye); - else - EyeTemplate_StopAnimating(QueueStatusMinimapButton.Eye); - end -end - -local function CheckQueueReward(dungeonID) - local leaderChecked, tankChecked, healerChecked, damageChecked = LFDQueueFrame_GetRoles() - local eligible, forTank, forHealer, forDamage, itemCount = GetLFGRoleShortageRewards(dungeonID, LFG_ROLE_SHORTAGE_RARE) - return eligible and itemCount > 0 and ((tankChecked and forTank) or (healerChecked and forHealer) or (damageChecked and forDamage)) -end - -local function CheckQueuePopReward(dungeonID, role) - local eligible, forTank, forHealer, forDamage, itemCount = GetLFGRoleShortageRewards(dungeonID, LFG_ROLE_SHORTAGE_RARE) - return eligible and itemCount > 0 and ((role == "TANK" and forTank) or (role == "HEALER" and forHealer) or (role == "DAMAGER" and forDamage)) -end - -function SatchelQueue:LFG_UPDATE_RANDOM_INFO() - if not timer then - return - end - - ResetSound() - - local mode, submode = GetLFGMode(LE_LFG_CATEGORY_LFD) - - if not mode then - local first, last, step = 1, GetNumRandomDungeons(), 1 - if self.db.global.prioritise then - first, last, step = last, first, -1 - - if self.db.global.exclusive then - last = first - end - end - - for i = first, last, step do - local id = GetLFGRandomDungeonInfo(i) - if IsLFGDungeonJoinable(id) and CheckQueueReward(id) then - --SetLFGDungeon(LE_LFG_CATEGORY_LFD, id) - --JoinLFG(LE_LFG_CATEGORY_LFD) - StaticPopup_Show("SATCHEL_QUEUE", nil, nil, id) - return - end - end - StaticPopup_Hide("SATCHEL_QUEUE") - elseif mode == "proposal" then - if submode == "unaccepted" then - local _, dungeonID, _, _, _, _, role = GetLFGProposal() - if CheckQueuePopReward(dungeonID, role) then - if self.db.global.sound_enable then - if self.db.global.sound_force then - if not sound_all then - sound_all = GetCVar("Sound_EnableAllSound") - sound_bg = GetCVar("Sound_EnableSoundWhenGameIsInBG") - end - - SetCVar("Sound_EnableAllSound", "1") - SetCVar("Sound_EnableSoundWhenGameIsInBG", "1") - end - PlaySoundFile(self.db.global.sound_file, "MASTER") - end - if self.db.global.flash_enable then - UIFrameFlash(SatchelQueue_Flash, 0.5, 0.5, 10.0, false, 0.0, 0.0) - end - elseif GetNumSubgroupMembers() == 0 then - RejectProposal() - end - end - elseif mode == "lfgparty" then - if not self.db.global.requeue then - self:TimerStop() - end - end -end - ---hack: if item is locked, it is being used. only check satchels -local function GuessOpenSatchel() - for bag = 0, NUM_BAG_SLOTS do - for slot = 1, GetContainerNumSlots(bag) do - local texture, count, locked, quality, readable, lootable, link = GetContainerItemInfo(bag, slot) - if locked and lootable then - local uid = SatchelID(GetContainerItemLink(bag, slot)) - if uid then - return uid - end - end - end - end - return nil -end - ---gold parsing liberated from Wowhead Looter (http://www.wowhead.com/client) -local WL_CURRENCY = { - ["1"] = COPPER_AMOUNT:gsub("%%d ", ""), - ["100"] = SILVER_AMOUNT:gsub("%%d ", ""), - ["10000"] = GOLD_AMOUNT:gsub("%%d ", ""), -}; - -local function wlParseCoin(strCoin) - local coin = 0; - for k, v in pairs(WL_CURRENCY) do - local found, _, a = strCoin:find("(%d+) "..v); - if found then - coin = coin + a * tonumber(k); - end - end - - return coin; -end - -local function IsBindOnPickup(link) - SatchelQueue_Tooltip:SetOwner(UIParent, "ANCHOR_NONE") - SatchelQueue_Tooltip:SetHyperlink(link) - - for _,region in ipairs({SatchelQueue_Tooltip:GetRegions()}) do - if region and region:GetObjectType() == "FontString" then - if region:GetText() == ITEM_BIND_ON_PICKUP then - return 1 - end - end - end - return nil -end - -function SatchelQueue:LOOT_OPENED() - local uid = GuessOpenSatchel() - if uid then - if not self.db.global.saved_uids[uid] then - self.db.global.count = self.db.global.count + 1 - - for slot = 1, GetNumLootItems() do - local slotType = GetLootSlotType(slot) - local texture, item, quantity, quality, locked = GetLootSlotInfo(slot) - - if slotType == LOOT_SLOT_MONEY then - local coin = wlParseCoin(item) - self.db.global.coin_total = self.db.global.coin_total + coin - - if self.db.global.coin_min == -1 then - self.db.global.coin_min = coin - self.db.global.coin_max = coin - else - self.db.global.coin_min = min(self.db.global.coin_min, coin) - self.db.global.coin_max = max(self.db.global.coin_max, coin) - end - elseif slotType == LOOT_SLOT_ITEM then - local _, _, id = string.find(GetLootSlotLink(slot), "|Hitem:(%d+):") - self.db.global.items[id] = (self.db.global.items[id] or 0) + quantity - end - end - end - - local closing = nil - if self.db.global.prevent_auto_loot and self.db.global.fake_auto_loot and should_auto_loot then - should_auto_loot = false - closing = true - - for slot = 1, GetNumLootItems() do - if GetLootSlotType(slot) == LOOT_SLOT_MONEY or not IsBindOnPickup(GetLootSlotLink(slot)) then - LootSlot(slot) - end - end - if GetNumLootItems() == 0 then - self.db.global.saved_uids[uid] = nil - end - end - - --rebuild stored items - if GetNumLootItems() > 0 then - self.db.global.saved_uids[uid] = {} - for slot = 1, GetNumLootItems() do - local _, item = GetLootSlotInfo(slot) - if item then --we havent just looted the slot - self.db.global.saved_uids[uid][slot] = GetLootSlotType(slot) == LOOT_SLOT_MONEY and -wlParseCoin(item) or tonumber(select(3, string.find(GetLootSlotLink(slot), "|Hitem:(%d+):"))) - end - end - end - - if closing then - CloseLoot() - end - end -end - -function SatchelQueue:LOOT_SLOT_CLEARED() - local uid = GuessOpenSatchel() - if uid then - if GetNumLootItems() == 1 and not GetLootSlotInfo(1) then - self.db.global.saved_uids[uid] = nil - end - end -end