Version 1.4.0

willneedit [05-27-17 - 15:02]
Version 1.4.0
 * Made banking fully functional for ESO+ again
 * Made Deconstruction fully functional for ESO+ again
 * Added 'no trait' as selectable trait filter
Filename
InventoryManager.txt
Modules/Banking.lua
Modules/Data.lua
Modules/Extractor.lua
Rulesets.lua
lang/de.lua
lang/en.lua
diff --git a/InventoryManager.txt b/InventoryManager.txt
index 21252a4..43e6ae7 100644
--- a/InventoryManager.txt
+++ b/InventoryManager.txt
@@ -2,7 +2,7 @@
 ## APIVersion: 100019
 ## OptionalDependsOn: LibAddonMenu-2.0
 ## SavedVariables: IMSavedVars
-## Version: 1.3.1
+## Version: 1.4.0
 ## Author: iwontsay
 ## Description: iwontsay's Inventory Manager

diff --git a/Modules/Banking.lua b/Modules/Banking.lua
index 44de1e0..29e0352 100644
--- a/Modules/Banking.lua
+++ b/Modules/Banking.lua
@@ -15,7 +15,7 @@ local function CreateReverseCache(bagType)
 	local _revCache = { }
 	local _empties = { }

-	for i = 0, GetBagSize(bagType)-1, 1 do
+	for i = 0, GetBagUseableSize(bagType)-1, 1 do
 		local curStack, maxStack = GetSlotStackSize(bagType, i)
 		if curStack > 0 then
 			local id = GetItemInstanceId(bagType, i)
@@ -34,8 +34,9 @@ end
 local function CreateReverseCaches()
 	RevCaches = { }
 	Empties = { }
-	RevCaches[BAG_BACKPACK], 	Empties[BAG_BACKPACK] 	= CreateReverseCache(BAG_BACKPACK)
-	RevCaches[BAG_BANK], 		Empties[BAG_BANK] 		= CreateReverseCache(BAG_BANK)
+	RevCaches[BAG_SUBSCRIBER_BANK],	Empties[BAG_SUBSCRIBER_BANK] 	= CreateReverseCache(BAG_SUBSCRIBER_BANK)
+	RevCaches[BAG_BACKPACK], 		Empties[BAG_BACKPACK] 			= CreateReverseCache(BAG_BACKPACK)
+	RevCaches[BAG_BANK], 			Empties[BAG_BANK] 				= CreateReverseCache(BAG_BANK)
 end

 local function UpdateCaches(srcBagType, srcSlotId, tgtBagType, tgtSlotId, count)
@@ -72,49 +73,79 @@ local function UpdateCaches(srcBagType, srcSlotId, tgtBagType, tgtSlotId, count)
 	end
 end

--- Returns (empties source?), tgtSlotId, transferCount
+-- Returns tgtSlotId, count
+local function FindStackToFill(id, count, tgtBagType)
+	local stacks = RevCaches[tgtBagType][id]
+	if not stacks then return nil end
+
+	local tgtSlotId = nil
+	local tgtCount = 0
+
+	for slotId, v in pairs(stacks) do
+		local missing = v[2] - v[1]
+		if missing >= count then return slotId, count end
+		if missing > tgtCount then
+			tgtSlotId = slotId
+			tgtCount = missing
+		end
+	end
+
+	return tgtSlotId, tgtCount
+end
+
+-- Returns (empties source?), tgtSlotId, transferCount, tgtBagType
+-- We try the subscriber bank before the regular one, to keep the slots of the regular one free in case
+-- someone unsubs.
 local function FindTargetSlot(srcBagType, srcSlotId, tgtBagType)
 	local curStack, maxStack = GetSlotStackSize(srcBagType, srcSlotId)
 	local id = GetItemInstanceId(srcBagType, srcSlotId)

-	local stacks = RevCaches[tgtBagType][id]
-	if stacks then
-		-- First, try to find a stack small enough to hold the entirety of the source
-		for tgtSlotId, v in pairs(stacks) do
-			if v[2]-v[1] >= curStack then
-				UpdateCaches(srcBagType, srcSlotId, tgtBagType, tgtSlotId, curStack)
-				return true, tgtSlotId, curStack
-			end
-		end
-
-		-- Now, fill any incomplete stack we might have, splitting the source stack
-		for tgtSlotId, v in pairs(stacks) do
-			local missing = v[2] - v[1]
-			if missing > 0 then
-				UpdateCaches(srcBagType, srcSlotId, tgtBagType, tgtSlotId, missing)
-				return false, tgtSlotId, missing
-			end
-		end
+	local tgtSlotId = nil
+	local transferCount = nil
+
+	-- Try the subscriber bank before the regular one if we target the bank
+	if tgtBagType == BAG_BANK then
+		tgtSlotId, transferCount = FindStackToFill(id, curStack, BAG_SUBSCRIBER_BANK)
 	end
-
+	if tgtSlotId ~= nil then
+		tgtBagType = BAG_SUBSCRIBER_BANK
+	else
+		tgtSlotId, transferCount = FindStackToFill(id, curStack, tgtBagType)
+	end
+
+	if tgtSlotId ~= nil then
+		UpdateCaches(srcBagType, srcSlotId, tgtBagType, tgtSlotId, transferCount)
+		return ( transferCount == curStack ), tgtSlotId, transferCount, tgtBagType
+	end
+
 	-- All the stacks we might have found are already full, we need to find a free slot
-	local empties = Empties[tgtBagType]
+	local empties = { }
+
+	-- Again, try the subscriber bank before the regular one if we target the bank
+	if tgtBagType == BAG_BANK then
+		empties = Empties[BAG_SUBSCRIBER_BANK]
+	end
+
+	if #empties ~= 0 then
+		tgtBagType = BAG_SUBSCRIBER_BANK
+	else
+		empties = Empties[tgtBagType]
+	end

 	-- No such luck?
-	if #empties == 0 then return false, -1, 0 end
+	if #empties == 0 then return false, -1, 0, tgtBagType end

-	-- It's a complete move, remove the empty slot from the target list, and create a new one on the source list
+	-- It's a complete move, remove the empty slot from the target list
 	local tgtSlotId = empties[#empties]
 	empties[#empties] = nil

 	UpdateCaches(srcBagType, srcSlotId, tgtBagType, tgtSlotId, curStack)
-	return true, tgtSlotId, curStack
+	return true, tgtSlotId, curStack, tgtBagType

 end


-local function CollectSingleDirection(action)
-	local bagType = (action == InventoryManager.ACTION_STASH and BAG_BACKPACK) or BAG_BANK
+local function CollectSingleDirection(action, bagType)
 	local _moveSlots = { }

 	InventoryManager:SetCurrentInventory(bagType)
@@ -128,16 +159,25 @@ local function CollectSingleDirection(action)
 	return _moveSlots
 end

-local function CalculateSingleMove(direction)
-	local srcBagType = (direction == 1 and BAG_BACKPACK) or BAG_BANK
-	local tgtBagType = (direction == 1 and BAG_BANK) or BAG_BACKPACK
+local function CalculateSingleMove(srcBagType, tgtBagType)
+
+	local srcSlotRepo = Moves[srcBagType]
+
+	-- If we draw from the pending moves from the regular bank and there are none,
+	-- try the subscriber bank.
+	if #srcSlotRepo == 0 and srcBagType == BAG_BANK then
+		srcBagType = BAG_SUBSCRIBER_BANK
+		srcSlotRepo = Moves[srcBagType]
+	end

-	local srcSlotRepo = Moves[(direction == 1 and "stash") or "retrieve"]
 	if #srcSlotRepo == 0 then return "src_empty" end

 	local srcSlotId = srcSlotRepo[#srcSlotRepo]

-	local empties, tgtSlotId, count = FindTargetSlot(srcBagType, srcSlotId, tgtBagType)
+	-- if we target the bank, FindTargetSlot returns the subscriber bank first, if possible
+	local empties, tgtSlotId, count
+	empties, tgtSlotId, count, tgtBagType = FindTargetSlot(srcBagType, srcSlotId, tgtBagType)
+
 	if count == 0 then return "tgt_full" end

 	if empties then srcSlotRepo[#srcSlotRepo] = nil end
@@ -160,15 +200,16 @@ local function CalculateMoves()
 	InventoryManager.currentRuleset:ResetCounters()

     Moves = {
-		["stash"] = CollectSingleDirection(IM.ACTION_STASH),
-		["retrieve"] = CollectSingleDirection(IM.ACTION_RETRIEVE)
+		[BAG_BACKPACK] = CollectSingleDirection(IM.ACTION_STASH, BAG_BACKPACK),
+		[BAG_BANK] = CollectSingleDirection(IM.ACTION_RETRIEVE, BAG_BANK),
+		[BAG_SUBSCRIBER_BANK] = CollectSingleDirection(IM.ACTION_RETRIEVE, BAG_SUBSCRIBER_BANK),
     }

 	-- We alternate between stashing and retrieving to minimize the chance of one
 	-- of the inventories running full.
 	while continue do
-		local leftres, leftentry = CalculateSingleMove(1)
-		local rightres, rightentry = CalculateSingleMove(-1)
+		local leftres, leftentry = CalculateSingleMove(BAG_BACKPACK, BAG_BANK)
+		local rightres, rightentry = CalculateSingleMove(BAG_BANK, BAG_BACKPACK)

 		-- ZOS Spam limitation
 		if #_moveStack > 95 then
diff --git a/Modules/Data.lua b/Modules/Data.lua
index 52c7ff5..2e486d8 100644
--- a/Modules/Data.lua
+++ b/Modules/Data.lua
@@ -33,6 +33,7 @@ InventoryManager.ACTION_RETRIEVE	=  20
 IM_Ruleset.ITEM_TRAIT_TYPE_ANY				= -1
 IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKOTHERS		= -2
 IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKNOWN		= -3
+IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT			= -4


 InventoryManager.filterorder = {
@@ -124,7 +125,8 @@ InventoryManager.actionorder = {
 InventoryManager.traitsorder = {
 	["IM_FILTER_ANY"] = {
 		0, -- Redefined to "don't care about traits"
-		IM_Ruleset.ITEM_TRAIT_TYPE_ANY,
+		IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT,
+		IM_Ruleset.ITEM_TRAIT_TYPE_ANY,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKOTHERS,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKNOWN,
 		ITEM_TRAIT_TYPE_WEAPON_INTRICATE,
@@ -132,6 +134,7 @@ InventoryManager.traitsorder = {
 	},
 	["IM_FILTER_WEAPON"] = {
 		0, -- Redefined to "don't care about traits"
+		IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANY,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKOTHERS,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKNOWN,
@@ -149,6 +152,7 @@ InventoryManager.traitsorder = {
  	},
 	["IM_FILTER_APPAREL"] = {
 		0, -- Redefined to "don't care about traits"
+		IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANY,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKOTHERS,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKNOWN,
@@ -166,6 +170,7 @@ InventoryManager.traitsorder = {
 	},
 	["IM_FILTERSPEC_JEWELRY"] = {
 		0, -- Redefined to "don't care about traits"
+		IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANY,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKOTHERS,
 		IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKNOWN,
diff --git a/Modules/Extractor.lua b/Modules/Extractor.lua
index 94b96b5..5fdf1a8 100644
--- a/Modules/Extractor.lua
+++ b/Modules/Extractor.lua
@@ -101,6 +101,10 @@ local function InitDeconstruction(tradeskill)
 		function(data) return filter_for_deconstruction(tradeskill, data) end,
 		list)

+	list = IM:CreateInventoryList(BAG_SUBSCRIBER_BANK,
+		function(data) return filter_for_deconstruction(tradeskill, data) end,
+		list)
+
 	IM:DoEventProcessing(list,
 		function(data) return extract_single_item(tradeskill, data) end,
 		function() end,
diff --git a/Rulesets.lua b/Rulesets.lua
index 8686d8f..4828715 100644
--- a/Rulesets.lua
+++ b/Rulesets.lua
@@ -47,6 +47,7 @@ function IM_Rule:ToString()
 	if self.traitType then
 		local which = (self.filterType == "IM_FILTER_CONSUMABLE" and 1) or 0
 		if self.traitType < 0 then
+			if self.traitType == IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT then which = 2 end
 			local str = (self.traitType == IM_Ruleset.ITEM_TRAIT_TYPE_ANY and "") or GetString("IM_META_TRAIT_TYPE", -self.traitType)
 			itemDescription = zo_strformat(
 				GetString("IM_META_TRAIT_TYPE_FORMAT", which),
@@ -145,6 +146,8 @@ function IM_Rule:Filter(data)
 	if self.traitType then
 		if self.traitType == IM_Ruleset.ITEM_TRAIT_TYPE_ANY then
 			if traitType == ITEM_TRAIT_TYPE_NONE then return false end
+		elseif self.traitType == IM_Ruleset.ITEM_TRAIT_TYPE_NOTRAIT then
+			if traitType ~= ITEM_TRAIT_TYPE_NONE then return false end
 		elseif self.traitType == IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKOTHERS then
 			if not data.unknownothers then return false end
 		elseif self.traitType == IM_Ruleset.ITEM_TRAIT_TYPE_ANYUNKNOWN then
diff --git a/lang/de.lua b/lang/de.lua
index 695a10d..9e6eee0 100644
--- a/lang/de.lua
+++ b/lang/de.lua
@@ -80,10 +80,12 @@ local lang = {

 	IM_META_TRAIT_TYPE_FORMAT0	= "<<1>> mit jeder Eigenschaft, die <<2>> ist",
 	IM_META_TRAIT_TYPE_FORMAT1	= "<<1>> welches <<2>> ist",
+	IM_META_TRAIT_TYPE_FORMAT2	= "<<1>> ohne eine Eigenschaft",
 	IM_META_TRAIT_TYPE0			= "(irrelevant)",
 	IM_META_TRAIT_TYPE1			= "jede Eigenschaft",
 	IM_META_TRAIT_TYPE2			= "unbekannt für andere",
 	IM_META_TRAIT_TYPE3			= "unbekannt",
+	IM_META_TRAIT_TYPE4			= "keine Eigenschaft",

 	IM_RE_CURRENTRULES			= "Derzeitige Regeln",
 	IM_RE_DELETERULE			= "Regel löschen",
diff --git a/lang/en.lua b/lang/en.lua
index 23137dc..5637a8b 100644
--- a/lang/en.lua
+++ b/lang/en.lua
@@ -80,10 +80,12 @@ local lang = {

 	IM_META_TRAIT_TYPE_FORMAT0	= "<<1>> with any trait <<2>>",
 	IM_META_TRAIT_TYPE_FORMAT1	= "<<1>> which is <<2>>",
+	IM_META_TRAIT_TYPE_FORMAT2	= "<<1>> with no trait",
 	IM_META_TRAIT_TYPE0			= "(irrelevant)",
 	IM_META_TRAIT_TYPE1			= "any trait",
 	IM_META_TRAIT_TYPE2			= "unknown to others",
 	IM_META_TRAIT_TYPE3			= "unknown",
+	IM_META_TRAIT_TYPE4			= "no trait",

 	IM_RE_CURRENTRULES			= "Current Rules",
 	IM_RE_DELETERULE			= "Delete Rule",