diff --git a/changelog.txt b/changelog.txt
new file mode 100644
index 0000000..240ce01
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,62 @@
+40000.1 -- 2010-10-13
+- 4.0 toc update.
+- "arg1" bug fix.
+
+1.1.1 -- 2009-12-08
+- 3.3 toc update.
+
+1.1 -- 2009-08-14
+- Fixed a bug with multiple "special" bags conflicting with each other.
+- Version bump.
+
+1.0.9 -- 2009-08-04
+- TOC updated to 3.2
+
+1.0.8 -- 2009-07-08
+- Some memory optimizations.
+- Extended bag hooking to cover all of the bag open/close events.
+
+1.0.7 -- 2009-06-16
+- Code optimizations.
+- In addition to restacking the player's bags, left-clicking the LDB plugin will now restack bank or guild vault when viewing them.
+
+1.0.6 -- 2009-06-11
+- Item looping now resets properly after finding and moving a matching partial stack. The entire process should now be much more stable and orderly.
+- Special bags with partial stacks and no free bag slots should now be correctly filled with partial stacks from other bags.
+- Set the LDB plugin to load when an addon with LDB embedded is loaded.
+- Changed LDB data object type to "launcher".
+
+1.0.5a -- 2009-06-06
+- LDB conditionals in place.
+- File structure and toc changes. Removed LDB libs that should be loaded anyway if there's a display.
+
+1.0.5 -- 2009-06-06
+- Fixed a bug in class/profession item sorting. A check wasn't in place to see if the item of the same type was a container, the result was bags in the inventory being equipped over the one already there.
+
+1.0.4 -- 2009-06-02
+- Throttled "OnUpdate" yield script.
+- Minor backpack hook change.
+- SavedVariables now account wide; settings still per-character.
+
+1.0.3 -- 2009-05-23
+- Removed auto-stacking from the TRADE_SHOW event.
+
+1.0.2a -- 2009-05-22
+- Added missing label text for LDB plugin.
+
+1.0.2 -- 2009-05-22
+- Added support for Data Broker. Left-click stacks bags, right-click opens menu.
+
+1.0.1 -- 2009-05-19
+- Fixed the handling of initializing saved variables when updating from 0.9
+- Fixed a bug that disabled guild tab switching when guild bank auto-stacking is turned off.
+
+1.0 -- 2009-05-18
+- Added support for restacking guild vault tabs.
+- Hooked bag auto-stack to LOOT_OPEN and TRADE_SHOW, and guild bank auto-stack to GUILDBANKFRAME_OPENED and SetCurrentGuildBankTab(i).
+- Extended auto-stack toggle to enable or disable bags, bank, or guild bank individually.
+- Will now move items that can go into special class/profession bags into those bags if possible (soul shards, herbs, arrows, etc).
+- Cleaned up and commented code.
+
+0.9 -- 2009-05-13
+- Added option to auto-stack when opening bags or bank, off by default. Toggle with '/restack auto'
\ No newline at end of file
diff --git a/kRestack.lua b/kRestack.lua
new file mode 100644
index 0000000..6d90288
--- /dev/null
+++ b/kRestack.lua
@@ -0,0 +1,264 @@
+--[[ kRestack 1.1 : Katae @ Anvilmar US
+
+ Usage: /restack [bags, bank, guild] - manual run
+ /restack auto [bags, bank, guild] - toggles auto mode ]]
+
+local AS, atBank, atVault, isViewable, canDeposit, restacker, tabswap
+
+SLASH_RESTACK1 = "/restack"
+function SlashCmdList.RESTACK(s) kRestack(s) end
+
+-- pretty chat colors!
+local white, green, red = "|cffffffff", "|cff55ff55", "|cffff5555"
+local kR = "|cff44ccffk|cffffffaaRestack"..white
+
+-- 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
+
+-- ldb plugin
+local function ldb()
+ local drop = CreateFrame("Frame", "kRestack", nil, "UIDropDownMenuTemplate")
+ LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject("kRestack", {
+ type = "launcher",
+ 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 ipairs{
+ { text = "= Restack =", isTitle = true },
+ { text = "Backpack", func = function() kRestack("bags") end },
+ { text = "Bank", disabled = not atBank, func = function() kRestack("bank") end },
+ { text = "Guild Vault", disabled = not atVault, func = function() kRestack("guild", false, GetCurrentGuildBankTab()) end },
+ { disabled = true },
+ { text = "= Set Auto =", isTitle = true },
+ { text = "Backpack", checked = AS.bags, func = function() kRestack("auto bags") end },
+ { text = "Bank", checked = AS.bank, func = function() kRestack("auto bank") end },
+ { text = "Guild Vault", checked = AS.guild, func = function() kRestack("auto guild") end },
+ } do UIDropDownMenu_AddButton(opt, 1) end end, "MENU")
+ return ToggleDropDownMenu(1, nil, drop, self, 0, 0)
+ elseif button == "LeftButton" then
+ kRestack(atBank and "bank" or atVault and "guild" or "bags")
+ end
+ end,
+ })
+end
+
+-- watching some events
+local f = CreateFrame("Frame")
+f:RegisterEvent'ADDON_LOADED'
+f:RegisterEvent'BANKFRAME_OPENED'
+f:RegisterEvent'BANKFRAME_CLOSED'
+f:RegisterEvent'LOOT_OPENED'
+f:RegisterEvent'GUILDBANKFRAME_OPENED'
+f:RegisterEvent'GUILDBANKFRAME_CLOSED'
+f:SetScript("OnEvent", function(_, e, addon)
+ -- more ldb stuff
+ if e == "BANKFRAME_OPENED" then
+ atBank = true
+ elseif e == "BANKFRAME_CLOSED" then
+ atBank = false
+ elseif e == "GUILDBANKFRAME_OPENED" then
+ isViewable, canDeposit = select(3,GetGuildBankTabInfo(GetCurrentGuildBankTab()))
+ atVault = (IsGuildLeader() or (isViewable and canDeposit))
+ elseif e == "GUILDBANKFRAME_CLOSED" then
+ atVault = false
+ end
+ if e == "ADDON_LOADED" then
+ -- initialize SavedVariables
+ if addon == "kRestack" then
+ if type(AutoStack) ~= "table" then AutoStack = {} end
+ local c = UnitName("player").." - "..GetRealmName()
+ if type(AutoStack[c]) ~= "table" then AutoStack[c] = {} end
+ AS = AutoStack[c]
+ if AS.bags == nil then AS.bags = true end
+ if AS.bank == nil then AS.bank = true end
+ if AS.guild == nil then AS.guild = false end
+ -- loading ldb plugin
+ elseif LibStub and ldb then
+ if LibStub:GetLibrary("LibDataBroker-1.1", true) then ldb(); ldb = nil end
+ end
+ elseif AS.bank and e == "BANKFRAME_OPENED" then
+ kRestack("bank", true)
+ elseif AS.bags and e == "LOOT_OPENED" then
+ kRestack("bags", true)
+ elseif AS.guild and e == "GUILDBANKFRAME_OPENED" then
+ isViewable, canDeposit = select(3,GetGuildBankTabInfo(GetCurrentGuildBankTab()))
+ if (IsGuildLeader() or (isViewable and canDeposit)) then kRestack("guild", true, GetCurrentGuildBankTab()) end
+ end
+end)
+
+-- hooking auto-stacking
+for _, func in ipairs{"CloseAllBags","ToggleBackpack","ToggleBag","OpenBackpack","OpenAllBags","OpenBackpack","CloseBackpack"} do
+ hooksecurefunc(func, function() if AS.bags then kRestack("bags", true) end end)
+end
+
+local oSetCurrentGuildBankTab = SetCurrentGuildBankTab
+SetCurrentGuildBankTab = function(tab)
+ if type(restacker) == "thread" then
+ tabswap = tabswap + 1
+ if tabswap > 1 then restacker = nil end
+ end
+ -- going to lock tab switching while stacking is in progress
+ if type(restacker) ~= "thread" or coroutine.status(restacker) == "dead" then
+ oSetCurrentGuildBankTab(tab)
+ isViewable, canDeposit = select(3,GetGuildBankTabInfo(tab))
+ if AS.guild and (IsGuildLeader() or (isViewable and canDeposit)) then kRestack("guild", false, tab) end
+ end
+end
+
+-- yielding function; items will get locked and we have to wait
+local function coYield(loc, bag, slot, count)
+ local elapsed = 0
+ f:SetScript("OnUpdate", function(_, update)
+ elapsed = elapsed + update
+ if type(restacker) == "thread" and coroutine.status(restacker) == "suspended" and elapsed > 0.1 then
+ local locked = true
+ locked = loc == "guild" and (select(2, GetGuildBankItemInfo(bag, slot)) == count and true or false) or select(3, GetContainerItemInfo(bag, slot))
+ if not locked then coroutine.resume(restacker) end
+ elapsed = 0
+ end
+ end)
+ coroutine.yield()
+end
+
+-- main function
+function kRestack(loc, silent, tab)
+ if loc ~= "bags" and loc ~= "bank" and loc ~= "guild" then
+ if loc == "resume" then
+ -- manual resume if stuck
+ if coroutine.status(restacker) == "suspended" then
+ print(kR, "Resuming suspended thread.")
+ coroutine.resume(restacker)
+ else
+ print(kR, "No suspended threads to resume.")
+ end
+ else
+ loc = loc:match("auto (%a+)")
+ if loc == "bags" or loc == "bank" or loc == "guild" then
+ -- toggle auto-stack
+ AS[loc] = not AS[loc]
+ print(kR, "Auto-stacking for", loc:gsub("bags","your backpack"):gsub("bank","your bank"):gsub("guild","the guild vault"), "toggled", AS[loc] and green.."ON" or red.."OFF")
+ else
+ -- usage message
+ local bags = (AS.bags and green or red).."bags"..white
+ local bank = (AS.bank and green or red).."bank"..white
+ local guild = (AS.guild and green or red).."guild"..white
+
+ print(kR, "Usage:")
+ print(white.."- /restack [bags, bank, guild] - Run restacker manually")
+ print(white.."- /restack auto ["..bags..",", bank..",", guild.."] - Toggle auto-stacking")
+ end
+ end
+ return
+ end
+
+ if type(restacker) ~= "thread" or coroutine.status(restacker) == "dead" then
+ restacker = coroutine.create(function()
+ tabswap = 0
+ local changed = true
+ while changed do
+ changed = false
+ local vault = loc == "guild"
+ for _, bag in ipairs(container[loc]) do
+ if changed then break end
+ bag = vault and tab 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 = tonumber(item:match("item:(%d+)"))
+ local stack = select(8, GetItemInfo(itemid))
+ local count = vault and select(2, GetGuildBankItemInfo(bag, slot)) or select(2, GetContainerItemInfo(bag, slot))
+
+ -- do "special" things with "special" items by moving them into "special" bags
+ if not vault and select(9, GetItemInfo(itemid)) ~= "INVTYPE_BAG" then
+ local bagType = (bag ~= 0 and bag ~= -1) and GetItemFamily(GetInventoryItemLink("player", ContainerIDToInventoryID(bag))) or 0
+ for _, sbag in ipairs(container[loc]) do
+ if sbag > 0 and GetContainerNumFreeSlots(sbag) > 0 then
+ local sbagID = ContainerIDToInventoryID(sbag)
+ local sbagType = GetItemFamily(GetInventoryItemLink("player", sbagID))
+ local itemType = GetItemFamily(item)
+
+ if sbagType > 0 and sbagType == itemType and bagType == 0 then
+ PickupContainerItem(bag, slot)
+ PutItemInBag(sbagID)
+ coYield(loc, bag, slot)
+ break
+ end
+ end
+ end
+ end
+ if count < stack then
+ -- found a partial stack
+ local locked, found, done, pbag, pslot
+ while true do
+ -- search through bags backwards for another partial stack with a matching itemid
+ for i = #container[loc], 1, -1 do
+ local _bag = container[loc][i]
+ if found or done then break end
+ local _slots = vault and MAX_GUILDBANK_SLOTS_PER_TAB or GetContainerNumSlots(_bag)
+ for _slot = _slots, 1, -1 do
+ if vault then _bag = bag end
+ if not (_bag == bag and _slot == slot) then
+ local _item = vault and GetGuildBankItemLink(_bag, _slot) or GetContainerItemLink(_bag, _slot)
+ if _item then
+ local _itemid = tonumber(_item:match("item:(%d+):"))
+ if _itemid == itemid then
+ local _stack = select(8, GetItemInfo(_itemid))
+ local _count = vault and select(2, GetGuildBankItemInfo(_bag, _slot)) or select(2, GetContainerItemInfo(_bag, _slot))
+ if _count < _stack then found, pbag, pslot = true, _bag, _slot; break end
+ end
+ end
+ else done = true; break end
+ end
+ end
+ 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
+ ClearCursor()
+ if vault then
+ PickupGuildBankItem(pbag, pslot)
+ PickupGuildBankItem(bag, slot)
+ ClearCursor()
+ coYield(loc, bag, slot, count)
+ else
+ -- if second partial stack is inside a special bag, move the original stack into it
+ local bagType = (bag ~= 0 and bag ~= -1) and GetItemFamily(GetInventoryItemLink("player", ContainerIDToInventoryID(bag))) or 0
+ local pbagType = (pbag ~= 0 and pbag ~= -1) and GetItemFamily(GetInventoryItemLink("player", ContainerIDToInventoryID(pbag))) or 0
+ if pbagType > 0 and bagType == 0 then
+ PickupContainerItem(bag, slot)
+ PickupContainerItem(pbag, pslot)
+ else
+ PickupContainerItem(pbag, pslot)
+ PickupContainerItem(bag, slot)
+ end
+ ClearCursor()
+ end
+ changed = true
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ -- turn off yielding function
+ f:SetScript("OnUpdate", nil)
+ end)
+ coroutine.resume(restacker)
+ else
+ if not silent then
+ -- user is impatient or there is a stall, tell them so
+ print(kR..red, "Restacking in progress. (use '/restack resume' if stuck)")
+ end
+ end
+end
\ No newline at end of file
diff --git a/kRestack.toc b/kRestack.toc
new file mode 100644
index 0000000..c2efec2
--- /dev/null
+++ b/kRestack.toc
@@ -0,0 +1,7 @@
+## Interface: 40000
+## Title: |cff44ccffk|rRestack
+## Version: 40000.1
+## Author: Katae of Anvilmar
+## Notes: For all of your item stacking needs.
+## SavedVariables: AutoStack
+kRestack.lua
\ No newline at end of file