local addonName, BGLM = ... -- used to distinguish between raid loot and inventory loot hooksecurefunc("UseContainerItem", function(...) BGLM.privateLoot = GetTime() end) -- register events local frame = CreateFrame("Frame") local function eventHandler(self, event, arg1, ...) if event == "ADDON_LOADED" and arg1 == addonName then BGLM.CheckSettings() frame:UnregisterEvent("ADDON_LOADED") elseif event == "ITEM_PUSH" and BGLM_LocalDB.autoDestroy and BGLM_LocalDB.autoDestroyInstant then local freeSlots = Broker_Garbage:GetVariable("totalFreeSlots") if freeSlots < BGLM_GlobalDB.tooFewSlots then BGLM.TrimInventory(BGLM_GlobalDB.tooFewSlots - freeSlots) end elseif event == "UI_ERROR_MESSAGE" then if arg1 and (arg1 == ERR_INV_FULL or arg1 == INVENTORY_FULL) then BGLM:Print(BGLM.locale.errorInventoryFull, BGLM_GlobalDB.warnInvFull) end elseif event == "LOOT_OPENED" then -- restack inventory BGLM.DoFullRestack() -- looting local disable = Broker_Garbage:GetVariable("disableKey") disable = disable[Broker_Garbage:GetOption("disableKey", true)] if not (disable and disable()) and (not InCombatLockdown() or BGLM_GlobalDB.useInCombat) then if BGLM.currentRestackItems ~= nil then BGLM.afterRestack = function() securecall(BGLM.SelectiveLooting, arg1) end else securecall(BGLM.SelectiveLooting, arg1) end end -- this only gets called for restacking elseif event == "ITEM_UNLOCKED" then if BGLM:RestackStep() then -- wait for next update BGLM:Debug("Still restacking...", BGLM.currentRestackItems) else -- we're done frame:UnregisterEvent("ITEM_UNLOCKED") BGLM.currentRestackItems = nil BGLM:Debug("Unregistered ITEM_UNLOCKED") if BGLM.afterRestack ~= nil then BGLM:afterRestack() BGLM.afterRestack = nil end end end end frame:RegisterEvent("ADDON_LOADED") frame:RegisterEvent("ITEM_PUSH") frame:RegisterEvent("UI_ERROR_MESSAGE") frame:RegisterEvent("LOOT_OPENED") -- frame:RegisterEvent("LOOT_CLOSED") frame:SetScript("OnEvent", eventHandler) -- --------------------------------------------------------- -- initialize full inventory restacking, if the settings allow for it function BGLM.DoFullRestack() if BGLM_GlobalDB.restackInventory then local numSlots = 0 local justStacked = {} for container = 0, 4 do numSlots = GetContainerNumSlots(container) if numSlots then for slot = 1, numSlots do local itemID = GetContainerItemID(container,slot) if itemID and not BGLM:Find(justStacked, itemID) then BGLM:Restack(itemID) table.insert(justStacked, itemID) end end end end end end -- restacks items so when deleting you lose as few items as possible function BGLM.Restack(itemID) if BGLM.currentRestackItems then tinsert(BGLM.currentRestackItems, itemID) else BGLM.currentRestackItems = { itemID } if BGLM:RestackStep() then -- wait for moved items frame:RegisterEvent("ITEM_UNLOCKED") else -- nothing to restack if BGLM.afterRestack ~= nil then BGLM:afterRestack() BGLM.afterRestack = nil end end end end local function NextRestackStep() -- go to next item if there is one tremove(BGLM.currentRestackItems, 1) if #(BGLM.currentRestackItems) <= 0 then BGLM.currentRestackItems = nil return false else return BGLM:RestackStep() end end -- move 1 item for restacking function BGLM:RestackStep() if not BGLM.currentRestackItems then return false end local itemID = BGLM.currentRestackItems[1] if not itemID then return NextRestackStep() end local count = GetItemCount(itemID) if not count or count <= 1 then return NextRestackStep() end local locations = Broker_Garbage:FindSlotToDelete(itemID, true) local maxLoc = #locations if maxLoc <= 1 then return NextRestackStep() end -- we're done, nothing to restack if GetContainerItemInfo(locations[1].bag, locations[1].slot) then ClearCursor() securecall(PickupContainerItem, locations[1].bag, locations[1].slot) securecall(PickupContainerItem, locations[maxLoc].bag, locations[maxLoc].slot) BGLM:Debug("Restack from/to", locations[1].count, locations[maxLoc].count) end return true end -- calls restack and deletes as many items as needed function BGLM:DeletePartialStack(itemID, num) local locations = Broker_Garbage:FindSlotToDelete(itemID) local maxStack = select(8, GetItemInfo(itemID)) if GetContainerItemID(locations[1].bag, locations[1].slot) ~= itemID then BGLM:Print("Error! DeletePartialStack: This is not the item I expected.") return end securecall(SplitContainerItem, locations[1].bag, locations[1].slot, num) if CursorHasItem() then BGLM:Delete("cursor", num) BGLM:Debug("DeletePartialStack", select(2,GetItemInfo(itemID)), num, locations[1].bag, locations[1].slot) end end -- returns true if the requested mob is skinnable with our skinning skill function BGLM:CanSkin(mobLevel) local skinning = Broker_Garbage:GetProfessionSkill(8613) if not skinning then return false end local maxLevel if skinning < 100 then maxLevel = floor(skinning/10) + 10 else maxLevel = floor(skinning/5) end return maxLevel >= mobLevel end -- determines if an item should be looted function BGLM:IsInteresting(itemLink) local itemID = BGLM:GetItemID(itemLink) local alwaysLoot = BGLM_GlobalDB.forceClear or false -- items we don't want local negativeList = BGLM:JoinTables(Broker_Garbage:GetOption("include")) if not itemID or negativeList[itemID] then return false, false elseif BGLM.PT then -- check if the item belongs to a category local inCategory for setName,_ in pairs(negativeList) do if type(setName) == "string" then _, inCategory = BGLM.PT:ItemInSet(itemID, setName) end if inCategory then return false, alwaysLoot end end end -- items we always want local positiveList = BGLM:JoinTables(Broker_Garbage:GetOption("exclude")) if positiveList[itemID] then alwaysLoot = true elseif BGLM.PT then -- check if the item belongs to a category local inCategory for setName,_ in pairs(positiveList) do if type(setName) == "string" then _, inCategory = BGLM.PT:ItemInSet(itemID, setName) end if inCategory then alwaysLoot = true end end end if select(6,GetItemInfo(itemLink)) == select(12, GetAuctionItemClasses()) then -- Quest Item alwaysLoot = true end -- items we don't care about return true, alwaysLoot end -- hook UpdateButton function for non-autoloot function BGLM.UpdateLootFrame(index) if not index then return end local slot = (LOOTFRAME_NUMBUTTONS * (LootFrame.page - 1)) + index itemLink = GetLootSlotLink(slot) if not itemLink then return end local isInteresting, alwaysLoot = BGLM:IsInteresting(itemLink) if isInteresting or alwaysLoot then _G["LootButton"..index.."IconTexture"]:SetDesaturated(false) _G["LootButton"..index.."IconTexture"]:SetAlpha(1) else _G["LootButton"..index.."IconTexture"]:SetDesaturated(true) _G["LootButton"..index.."IconTexture"]:SetAlpha(0.5) end end hooksecurefunc("LootFrame_UpdateButton", BGLM.UpdateLootFrame) -- --------------------------------------------------------- -- lootmanager functionality from here on -- --------------------------------------------------------- -- for use in CHAT_MSG_LOOT event - destroys watched items as needed; UNUSED function BGLM.AutoDestroy() local location = {} local itemLink for itemID, maxCount in pairs(BGLM:JoinTables(Broker_Garbage:GetOption("include"))) do count = 0 if type(itemID) == "number" and type(maxCount) == "number" then BGLM:Debug(itemID, maxCount) -- delete excess items location = Broker_Garbage:FindSlotToDelete(itemID) for i = #location, 1, -1 do if count >= maxCount then itemLink = select(2, GetItemInfo(itemID)) Broker_Garbage:Delete(itemLink, {location[i].bag, location[i].slot}) else count = count + location[i].count end end end end end function BGLM.TrimInventory(emptySlotNum) if not emptySlotNum then return end for i = 1, emptySlotNum do local deleteThis = select(i, Broker_Garbage:GetVariable("cheapestItems")) if not deleteThis then BGLM:Print("Error! I tried to make space but there is nothing left for me to delete!") return end Broker_Garbage:Delete(deleteThis) end end -- for use in LOOT_OPENED event function BGLM.SelectiveLooting(autoloot) -- jwehgH"G$(&/&ยง$/!!" stupid . vs. : notation if InCombatLockdown() and not BGLM_GlobalDB.useInCombat then return end if BGLM.privateLoot then if GetTime() - BGLM.privateLoot <= BGLM_GlobalDB.privateLootTimer then BGLM:Debug("Item is private loot") else BGLM.privateLoot = nil -- reset, data is too old end end local autoLoot = autoloot ~= 0 or BGLM_GlobalDB.autoLoot local prepareSkinning = BGLM_GlobalDB.autoLootSkinning and UnitExists("target") and UnitIsDead("target") and UnitCreatureType("target") == BGLM.locale.CreatureTypeBeast and BGLM:CanSkin(UnitLevel("target")) if autoLoot or (BGLM_GlobalDB.autoLootPickpocket and Broker_Garbage:GetVariable("playerClass") == "ROGUE" and IsStealthed()) or (BGLM_GlobalDB.autoLootFishing and IsFishingLoot()) or prepareSkinning then BGLM:Debug("SelectiveLooting: Check passed, figure out what to do. Autoloot:", autoloot) local close = true BGLM:Debug("close initialized: true") for slot = 1, GetNumLootItems() do local lootAction local _, _, quantity, quality, locked = GetLootSlotInfo(slot) local itemLink = GetLootSlotLink(slot) local itemID = BGLM:GetItemID(itemLink) local compareTo = Broker_Garbage:GetVariable("cheapestItems") compareTo = compareTo and compareTo[1] or nil if itemLink then -- needed because faulty loot slots or slots that contain money break things local value = Broker_Garbage:GetItemValue(itemLink, quantity) or 0 local isInteresting, alwaysLoot = BGLM:IsInteresting(itemLink) local maxStack = select(8, GetItemInfo(itemID)) local inBags = mod(GetItemCount(itemID), maxStack) local stackOverflow = quantity + mod(inBags, maxStack) - maxStack local lootMethod, groupLM, raidLM = GetLootMethod() if isInteresting or alwaysLoot then if not alwaysLoot and value < BGLM_LocalDB.itemMinValue then BGLM:Print(format(BGLM.locale.couldNotLootValue, itemLink), BGLM_GlobalDB.printValue) lootAction = "none" elseif Broker_Garbage:GetVariable("totalFreeSlots") <= BGLM_GlobalDB.tooFewSlots then -- dropping low on bag space BGLM:Debug("Free bag space below minimum treshold! Thinking ...", itemLink) if inBags > 0 and stackOverflow <= 0 then -- delete nothing. this item fits without us doing anything BGLM:Debug("Item stacks, do nothing special", itemLink) lootAction = "take" elseif not alwaysLoot and BGLM_LocalDB.autoDestroy and stackOverflow > 0 and (prepareSkinning or (compareTo and (Broker_Garbage:GetItemValue(itemLink, stackOverflow) or 0) < compareTo.value)) then -- delete partial stack. throw away partial stacks to squeeze in a little more BGLM:Debug("Item can be made to fit.", itemLink) lootAction = "deletePartial" elseif BGLM_LocalDB.autoDestroy and compareTo and compareTo.value and (alwaysLoot or prepareSkinning or value > compareTo.value) then -- delete only if it's worth more, if it's an item we really need or if we want to skin the mob BGLM:Debug("Deleting item", compareTo.itemLink, "to make room for", itemLink) lootAction = "delete" elseif not alwaysLoot and compareTo and compareTo.value and value and value <= compareTo.value then BGLM:Debug("Taking this item by throwing away stuff would make us loose money.", itemLink) BGLM:Print(format(BGLM.locale.couldNotLootCompareValue, itemLink), BGLM_GlobalDB.printCompareValue) lootAction = "none" else -- we'd like to take the item but have no bag space (and can't make any) close = false if BGLM.privateLoot or lootMethod == "freeforall" or quality < GetLootThreshold() or (GetNumPartyMembers() == 0 and GetNumRaidMembers() == 0) then -- we should be able to loot. if we can't it's because the inventory is full BGLM:Print(format(BGLM.locale.couldNotLootSpace, itemLink), BGLM_GlobalDB.printSpace) end lootAction = "none" end else lootAction = "take" end else -- item is on junk list BGLM:Print(format(BGLM.locale.couldNotLootBlacklist, itemLink), BGLM_GlobalDB.printJunk) lootAction = "none" end -- last update & starting delete actions if needed if lootAction ~= "none" and locked and quality < GetLootThreshold() then -- we should probably be able to loot this, but something went wrong BGLM:Print(format(BGLM.locale.couldNotLootLocked, itemLink), BGLM_GlobalDB.printLocked) lootAction = "none" close = false elseif lootMethod == "master" and quality >= GetLootThreshold() and ((GetNumRaidMembers() > 1 and UnitIsUnit("raid"..raidLM, "player")) or (GetNumPartyMembers() > 0 and groupLM == 0)) then -- we have loot master messages enabled and are loot master BGLM:Print(format(BGLM.locale.couldNotLootLM, itemLink), BGLM_GlobalDB.warnLM) lootAction = "none" close = false break -- prevent multiple messages when you're the LM elseif not BGLM.privateLoot and lootAction ~= "none" and lootMethod ~= "freeforall" and quality >= GetLootThreshold() and (GetNumPartyMembers() > 0 or GetNumRaidMembers() > 1) then -- item is above the group's loot treshold and we're in a group BGLM:Debug("Item is above loot treshold. Leave it for the user to decide.") lootAction = "none" close = false elseif lootAction == "deletePartial" then Broker_Garbage:DeletePartialStack(itemID, stackOverflow) lootAction = "take" elseif lootAction == "delete" then Broker_Garbage:Delete(compareTo) lootAction = "take" end end if lootAction == "take" or not LootSlotIsItem(slot) then -- finally, take it!; not itemLink or BGLM:Debug("Taking item", itemLink or "???") LootSlot(slot) if BGLM.privateLoot or BGLM_GlobalDB.autoConfirmBoP then BGLM:Debug("Confirming loot") ConfirmLootSlot(slot) end end end if close and BGLM_GlobalDB.closeLootWindow and (not IsFishingLoot() or not IsAddOnLoaded("FishingBuddy")) then CloseLoot() end end -- TODO: maybe consider LOOT_SLOT_CLEARED event ... or not. BGLM.privateLoot = nil -- if we used this, reset; if we didn't we don't need its value anyway end