WoWInterface SVN kRestack

[/] [trunk/] [kRestack/] [kRestack.lua] - Rev 19

Compare with Previous | Blame | View Log

--[[    Usage:
                        /restack [bags, bank, guild] - manual run
                        /restack auto [bags, bank, guild] - toggles auto mode
]]

SlashCmdList["RESTACK"] = function(s) kRestack(s) end
SLASH_RESTACK1 = "/restack"

--[[ pretty chat colors! ]]
local col = { w = "|cffFFFFFF", g = "|cff55FF55", r = "|cffFF5555" }
local kR = "|cff44CCFFk|cffFFFFAARestack"..col.w

--[[ ldb plugin ]]
local menu = {
        { text = "= Restack =", isTitle = true },
        { text = "Backpack", func = function() kRestack("bags") end },
        { text = "Bank", disabled = true, func = function() kRestack("bank") end },
        { text = "Guild Vault", disabled = true, func = function() kRestack("guild") end },
        { disabled = true },
        { text = "= Set Auto =", isTitle = true },
        { text = "Backpack", checked = function() return (AutoRestack.bags and true or false) end, func = function() kRestack("auto bags") end },
        { text = "Bank", checked = function() return (AutoRestack.bank and true or false) end, func = function() kRestack("auto bank") end },
        { text = "Guild Vault", checked = function() return (AutoRestack.guild and true or false) end, func = function() kRestack("auto guild") end },
}
local drop = CreateFrame("frame", "kRestack", nil, "UIDropDownMenuTemplate")
LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("kRestack", {
    type = "data source",
        label = "kRestack",
    icon = "Interface\\Icons\\INV_Crate_09",
    OnClick = function(self, button)
        if button == "RightButton" then
                        GameTooltip:Hide()
                        UIDropDownMenu_Initialize(drop, function() for _, opt in pairs(menu) do UIDropDownMenu_AddButton(opt, 1) end end, "MENU")
                        return ToggleDropDownMenu(1, nil, drop, self, 0, 0)
                elseif button == "LeftButton" then
                        kRestack("bags")
                end
    end,
})

--[[ set user's container identifers ]]
local container = { bags = { 0 }, bank = { -1 }, guild = { 42 } }
for i = 1, NUM_BAG_SLOTS do table.insert(container.bags, i) end
for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do table.insert(container.bank, i) end

--[[ watching some events ]]
local atBank, atVault
local f = CreateFrame("frame")
local events = { "ADDON_LOADED", "BANKFRAME_OPENED", "BANKFRAME_CLOSED", "LOOT_OPENED", "GUILDBANKFRAME_OPENED", "GUILDBANKFRAME_CLOSED" }
for _, r in pairs(events) do f:RegisterEvent(r) end
f:SetScript("OnEvent", function(_, e)
        --[[ ldb stuff ]]
        if e == events[2] then 
                menu[3].disabled = false
        elseif e == events[3] then
                menu[3].disabled = true
        elseif e == events[5] then
                local _, _, isViewable, canDeposit = GetGuildBankTabInfo(GetCurrentGuildBankTab())
                if IsGuildLeader() or (isViewable and canDeposit) then menu[4].disabled = false end
        elseif e == events[6] then
                menu[4].disabled = true
        end
        if e == events[1] then
                --[[ initialize saved variables ]]
                if type(AutoRestack) ~= "table" then AutoRestack = { } end
                if AutoRestack.bags == nil then AutoRestack.bags = true end
                if AutoRestack.bank == nil then AutoRestack.bank = true end
                if AutoRestack.guild == nil then AutoRestack.guild = false end
        elseif AutoRestack.bank and e == events[2] then
                kRestack("bank", true)
        elseif AutoRestack.bags and e == events[4] then
                kRestack("bags", true)
        elseif AutoRestack.guild and e == events[5] then
                kRestack("guild", true)
        end
end)

--[[ hooking auto-stacking to ToggleBackpack and guild vault tab switching ]]
local oToggleBackpack = ToggleBackpack
ToggleBackpack = function()
        if AutoRestack.bags then kRestack("bags", true) end
        return oToggleBackpack()
end

local oSetCurrentGuildBankTab = SetCurrentGuildBankTab
SetCurrentGuildBankTab = function(tab)
        if type(restacker) ~= "thread" or coroutine.status(restacker) == "dead" then
                if AutoRestack.guild then kRestack("guild", true, tab) end
                return oSetCurrentGuildBankTab(tab)
        end
end

local function coYield(loc, lb, ls, count)
        --[[ yielding function; can't be stacking too fast or else bad stuff happens ]]
        f:SetScript("OnUpdate", function()
                if coroutine.status(restacker) == "suspended" then
                        local locked = true
                        if loc == "guild" then
                                locked = select(2, GetGuildBankItemInfo(lb, ls)) == count and true or false
                        else
                                locked = select(3, GetContainerItemInfo(lb, ls))
                        end
                        if not locked then coroutine.resume(restacker) end
                end
        end)
        coroutine.yield()
end

local function getPartial(cb, cs, id, loc)
        local vault = loc == "guild"
        for _, bag in pairs(container[loc]) do
                local slots = vault and MAX_GUILDBANK_SLOTS_PER_TAB or GetContainerNumSlots(bag)
                for slot = slots, 1, -1 do
                        bag = vault and cb or bag
                        if not (bag == cb and slot == cs) then
                                local item = vault and GetGuildBankItemLink(bag, slot) or GetContainerItemLink(bag, slot)
                                if item then
                                        local itemid = select(3, strfind(item, "item:(%d+)"))
                                        if itemid == id then
                                                local stack = select(8, GetItemInfo(itemid))
                                                local count = vault and select(2, GetGuildBankItemInfo(bag, slot)) or select(2, GetContainerItemInfo(bag, slot))
                                                if stack > count then return true, bag, slot end
                                        end
                                end
                        end
                end
        end
        return false
end

--[[ main function ]]
function kRestack(loc, nowarn, tab)
        if loc ~= "bags" and loc ~= "bank" and loc ~= "guild" then
                --[[ more slash commands ]]
                if loc == "resume" then
                        if coroutine.status(restacker) == "suspended" then
                                print(kR..col.w, "Resuming suspended thread.")
                                coroutine.resume(restacker)
                        else
                                print(kR..col.w, "No suspended threads to resume.")
                        end
                else
                        loc = select(3, strfind(loc, "auto (%a+)"))
                        if loc == "bags" or loc == "bank" or loc == "guild" then
                                local togl
                                if not AutoRestack[loc] then
                                        AutoRestack[loc] = true
                                        togl = col.g.."ON"
                                else
                                        AutoRestack[loc] = false
                                        togl = col.r.."OFF"
                                end
                                loc = loc == "bags" and "your backpack" or loc
                                loc = loc == "bank" and "your bank" or loc
                                loc = loc == "guild" and "the guild vault" or loc
                                print(kR, "Auto-stacking for", loc, "toggled", togl)
                        else
                                local bags = AutoRestack.bags and col.g or col.r
                                local bank = AutoRestack.bank and col.g or col.r
                                local guild = AutoRestack.guild and col.g or col.r
                        
                                print(kR, "Usage:")
                                print(col.w.."-    /restack [bags, bank, guild] - Run restacker manually")
                                print(col.w.."-    /restack auto ["..bags.."bags"..col.w..",", bank.."bank"..col.w..",", guild.."guild"..col.w.."] - Toggle auto-stacking")
                        end
                end
                do return end
        end
        
        if type(restacker) ~= "thread" or coroutine.status(restacker) == "dead" then
                restacker = coroutine.create(function()
                        --[[ core restacking function ]]
                        local changed = true
                        while changed do
                                changed = false
                                vault = loc == "guild"
                                if vault then
                                        vbag = tab or GetCurrentGuildBankTab()
                                        local _, _, isViewable, canDeposit = GetGuildBankTabInfo(vbag)
                                        if not (IsGuildLeader() or (isViewable and canDeposit)) then break end
                                end
                                for _, bag in pairs(container[loc]) do
                                        bag = vault and vbag or bag
                                        for slot = 1, (vault and MAX_GUILDBANK_SLOTS_PER_TAB or GetContainerNumSlots(bag)) do
                                                while true and not vault do
                                                        local locked = select(3, GetContainerItemInfo(bag, slot))
                                                        if locked then coYield(loc, bag, slot) else break end
                                                end
                                                local item = vault and GetGuildBankItemLink(bag, slot) or GetContainerItemLink(bag, slot)
                                                if item then
                                                        local itemid = select(3, strfind(item, "item:(%d+)"))
                                                        local stack = select(8, GetItemInfo(itemid))
                                                        local count = vault and select(2, GetGuildBankItemInfo(bag, slot)) or select(2, GetContainerItemInfo(bag, slot))
                                                        local moved = false
                                                        
                                                        --[[ do "special" things with "special" items by moving them to "special" bags ]]
                                                        if not vault then
                                                                for _, sbag in pairs(container[loc]) do
                                                                        if sbag > 0 and GetContainerNumSlots(sbag) > 0  then
                                                                                local bagID = ContainerIDToInventoryID(sbag)
                                                                                local bagType = GetItemFamily(GetInventoryItemLink("player", bagID))
                                                                                local itemType = GetItemFamily(item)
                                                                                if bagType > 0 and bagType == itemType and bag ~= sbag then
                                                                                        PickupContainerItem(bag, slot)
                                                                                        PutItemInBag(bagID)
                                                                                        moved = true
                                                                                end
                                                                        end
                                                                end
                                                        end
                                                        
                                                        if count < stack and not moved then
                                                                --[[ found a partial stack ]]
                                                                local locked, found, pbag, pslot
                                                                while true do
                                                                        --[[ look for another partial stack ]]
                                                                        found, pbag, pslot = getPartial(bag, slot, itemid, loc)
                                                                        if vault then break end
                                                                        locked = found and select(3, GetContainerItemInfo(pbag, pslot)) or false
                                                                        if locked then coYield(loc, pbag, pslot) else break end
                                                                end
                                                                
                                                                if found then
                                                                        --[[ stack 'em up ]]
                                                                        ClearCursor()
                                                                        if vault then
                                                                                PickupGuildBankItem(pbag, pslot)
                                                                                PickupGuildBankItem(bag, slot)
                                                                                ClearCursor()
                                                                                coYield(loc, bag, slot, count)
                                                                        else
                                                                                PickupContainerItem(pbag, pslot)
                                                                                PickupContainerItem(bag, slot)
                                                                        end
                                                                        changed = true
                                                                end
                                                        end
                                                end
                                        end
                                end
                        end
                        --[[ turn off yielding function ]]
                        f:SetScript("OnUpdate", nil)
                end)
                coroutine.resume(restacker)
        else
                if not nowarn then
                        --[[ user is impatient or there is a stall, tell them so ]]
                        print(kR..col.r, "Restacking is already in progress. (use '/restack resume' if stuck)")
                end
        end     
end

Compare with Previous | Blame