diff --git a/CraftStoreLink.lua b/CraftStoreLink.lua index 84258f1..bfb809f 100644 --- a/CraftStoreLink.lua +++ b/CraftStoreLink.lua @@ -2,8 +2,17 @@ local DEBUG = function() end -- d +local function _tr(str) + return str +end + local CSL = {} +local Used_CS +local Used_CSA + +local hasCS = nil + InventoryManager.CSL = CSL local function SplitLink(link,nr) @@ -12,18 +21,43 @@ local function SplitLink(link,nr) end function CSL:hasCSAddon() - return CS and CS.GetTrait and CS.account and CS.account.crafting and true + if hasCS ~= nil then + + -- This variable is late initialized, update if needed + if hasCS == "old" then + Used_CSA = Used_CS.account + elseif hasCS == "new" then + Used_CSA = Used_CS.Account + end + + return hasCS ~= false and true + end + + if CS then + Used_CS = CS + Used_CSA = Used_CS.account + CHAT_SYSTEM:AddMessage(GetString("IM_INIT_DETECTED_CS_OLD")) + hasCS = "old" + elseif CraftStoreFixedAndImprovedLongClassName then + Used_CS = CraftStoreFixedAndImprovedLongClassName + Used_CSA = Used_CS.Account + CHAT_SYSTEM:AddMessage(GetString("IM_INIT_DETECTED_CS_NEW")) + hasCS = "new" + else + hasCS = false + end + return hasCS end function CSL:IsTraitNeeded(itemLink) local need = { } - local craft, row, trait = CS.GetTrait(itemLink) + local craft, row, trait = Used_CS.GetTrait(itemLink) -- Loop all chars known by CS - for char, data in pairs(CS.account.crafting.studies) do + for char, data in pairs(Used_CSA.crafting.studies) do --if a char study this item if data[craft] and data[craft][row] and (data[craft][row]) then -- If this char didn't yet researched this item - local csr = CS.account.crafting.research + local csr = Used_CSA.crafting.research if csr[char][craft] and csr[char][craft][row] and csr[char][craft][row][trait] == false then need[char] = true need[#need + 1] = char @@ -38,8 +72,8 @@ local CURRENT_PLAYER = GetUnitName("player") function CSL:IsStyleNeeded(link) local id, need = SplitLink(link,3), { } if id then - for _, char in pairs(CS.GetCharacters()) do - if CS.account.style.tracking[char] and not CS.account.style.knowledge[char][id] then + for _, char in pairs(Used_CS.GetCharacters()) do + if Used_CSA.style.tracking[char] and not Used_CSA.style.knowledge[char][id] then need[char] = true need[#need + 1] = char end @@ -48,11 +82,11 @@ function CSL:IsStyleNeeded(link) return need end -function CSL:IsRecipeNeeded(link) +function CSL:IsCookRecipeNeeded(link) local id, need = SplitLink(link,3), { } if id then - for char,data in pairs(CS.account.cook.knowledge) do - if not data[id] and CS.account.cook.tracking[char] then + for char,data in pairs(Used_CSA.cook.knowledge) do + if not data[id] and Used_CSA.cook.tracking[char] then need[char] = true need[#need + 1] = char end @@ -61,6 +95,40 @@ function CSL:IsRecipeNeeded(link) return need end +function CSL:IsBlueprintNeeded(link) + local id, need = SplitLink(link,3), { } + + if not Used_CSA.furnisher then + return need + end + + if id then + for char,data in pairs(Used_CSA.furnisher.knowledge) do + if not data[id] and Used_CSA.furnisher.tracking[char] then + need[char] = true + need[#need + 1] = char + end + end + end + return need +end + +function CSL:IsRecipeNeeded(link) + local collate1 = self:IsCookRecipeNeeded(link) + local collate2 = self:IsBlueprintNeeded(link) + local all = { } + for k, v in pairs(collate1) do + if type(k) == "string" then all[k] = true end + end + for k, v in pairs(collate2) do + if type(k) == "string" then all[k] = true end + end + for k, v in pairs(all) do + all[#all+1] = k + end + return all +end + function CSL:isUnknown(itemLink) local chars local itemType diff --git a/FCOISLink.lua b/FCOISLink.lua new file mode 100644 index 0000000..86ddd2e --- /dev/null +++ b/FCOISLink.lua @@ -0,0 +1,119 @@ +local DEBUG = +-- function() end +d + +local function _tr(str) + return str +end + +local TXT_NO_CARE +local TXT_NO_MARK +local TXT_ANY_MARK + +local I_NO_CARE = -3 +local I_NO_MARK = -2 +local I_ANY_MARK = -1 + +local FCOISL = {} + +local hasFCOIS = nil + +InventoryManager.FCOISL = FCOISL + +local DIList = nil +local DIChoices = nil + +function FCOISL:hasAddon() + if hasFCOIS ~= nil then return hasFCOIS end + + TXT_NO_CARE = GetString("IM_FCOIS_NOCAREMARK") + TXT_NO_MARK = GetString("IM_FCOIS_NOMARK") + TXT_ANY_MARK = GetString("IM_FCOIS_ANYMARK") + + hasFCOIS = ( FCOIS ~= nil and FCOIsMarked ~= nil and FCOGetDynamicInfo ~= nil and FCOGetIconText ~= nil) + + return hasFCOIS +end + +function FCOISL:GetDynamicIconList() + if DIList then return DIList end + + DIList = { } + if not self:hasAddon() then return DIList end + + local totalNumberOfDynamicIcons, numberToDynamicIconNr = FCOGetDynamicInfo() + for index, dynamicIconNr in pairs(numberToDynamicIconNr) do + local dynIconName = FCOGetIconText(dynamicIconNr) + DIList[#DIList + 1] = { dynamicIconNr, dynIconName } + DIList[dynIconName] = dynamicIconNr + end + + return DIList +end + + +function FCOISL:GetIndexedMark(mark) + if mark == I_NO_MARK then return TXT_NO_MARK + elseif mark == I_ANY_MARK then return TXT_ANY_MARK + end + + return (FCOISL:hasAddon() and FCOGetIconText(mark)) or TXT_NO_CARE +end + +function FCOISL:GetMarkIndex(markText) + if not FCOISL:hasAddon() then return nil end + + if markText == TXT_NO_CARE then return nil + elseif markText == TXT_NO_MARK then return I_NO_MARK + elseif markText == TXT_ANY_MARK then return I_ANY_MARK + else return DIList[markText] + end +end + +function FCOISL:GetDefaultMark() return I_NO_CARE end + +function FCOISL:IsNoMark(mark) return mark == I_NO_MARK end + +function FCOISL:IsAnyMark(mark) return mark == I_ANY_MARK end + +function FCOISL:GetDynamicIconChoices() + if DIChoices then return DIChoices end + + DIChoices = { TXT_NO_CARE, TXT_NO_MARK, TXT_ANY_MARK } + if not self:hasAddon() then return DIChoices end + + local totalNumberOfDynamicIcons, numberToDynamicIconNr = FCOGetDynamicInfo() + for index, dynamicIconNr in pairs(numberToDynamicIconNr) do + local dynIconName = FCOGetIconText(dynamicIconNr) + DIChoices[#DIChoices + 1] = dynIconName + end + + return DIChoices +end + +function FCOISL:FitMark(instanceId, mark) + -- If we have switched off this addon, render this filter setting as irrelevant + if not FCOISL:hasAddon() then return true end + + if not mark then return true end + + if mark == I_NO_MARK then return not FCOISL:HasMark(instanceId, nil) + elseif mark == I_ANY_MARK then return FCOISL:HasMark(instanceId, nil) + end + + return FCOISL:HasMark(instanceId, mark) +end + +function FCOISL:HasMark(instanceId, mark) + local _tab = self:GetDynamicIconList() + + if mark then + return FCOIsMarked(instanceId, mark) + end + + for i = 1, #_tab, 1 do + if FCOIsMarked(instanceId, _tab[i][1]) then return true end + end + + return false +end diff --git a/InventoryManager.lua b/InventoryManager.lua index 74aff0d..3551f15 100644 --- a/InventoryManager.lua +++ b/InventoryManager.lua @@ -238,7 +238,8 @@ function InventoryManager:Init() ["maxGold"] = 5000, ["minGold"] = 1000, ["maxTV"] = 10, - ["minTV"] = 0 + ["minTV"] = 0, + ["autosell"] = true, } } @@ -263,11 +264,15 @@ function InventoryManager:Init() self.settings = self.charVariables.settings self.presetProfiles = loadProfile(self.presetProfiles) + + self.CSL.hasCSAddon() + self.FCOISL:hasAddon() self:InitializeUI() CHAT_SYSTEM:AddMessage(self.name .. " Addon Loaded.") CHAT_SYSTEM:AddMessage("Use /im help for an overview") + end function InventoryManager:Save() diff --git a/InventoryManager.txt b/InventoryManager.txt index d648a36..317f274 100644 --- a/InventoryManager.txt +++ b/InventoryManager.txt @@ -2,7 +2,7 @@ ## APIVersion: 100018 ## OptionalDependsOn: LibAddonMenu-2.0 ## SavedVariables: IMSavedVars -## Version: 1.0.1 +## Version: 1.1.0 ## Author: iwontsay ## Description: iwontsay's Inventory Manager @@ -29,7 +29,10 @@ Modules/Data.lua Modules/Banking.lua Modules/Junker.lua Modules/Seller.lua + CraftStoreLink.lua +FCOISLink.lua + Rulesets.lua UI/RuleEdit.lua UI/ProfileEdit.lua diff --git a/Modules/Banking.lua b/Modules/Banking.lua index 849b038..1492df6 100644 --- a/Modules/Banking.lua +++ b/Modules/Banking.lua @@ -33,21 +33,37 @@ local function ScanInventory(bagType) return { ["empties"] = _empties, ["items"] = _stackCount } end +local function RebuildStackCache(tgtBagType) + InvCache["tgtStackCache"][tgtBagType] = { } + + local tgtInv = InvCache[tgtBagType] + local tgtStackCache = InvCache["tgtStackCache"][tgtBagType] + + for k,v in pairs(tgtInv["items"]) do + local missing = v["max"] - v["current"] + if missing > 0 then + tgtStackCache[v["id"]] = { missing, k } + end + end +end + local function FindTargetSlot(srcBagType, srcSlotId, tgtBagType) local srcInv = InvCache[srcBagType] local tgtInv = InvCache[tgtBagType] + local tgtStackCache = InvCache["tgtStackCache"][tgtBagType] local iId = srcInv["items"][srcSlotId]["id"] local count = srcInv["items"][srcSlotId]["current"] - -- Try to fill up existing stacks, return even incomplete transfers doing so - for k,v in pairs(tgtInv["items"]) do - local missing = v["max"] - v["current"] - if iId == v["id"] and missing > 0 then + if tgtStackCache[iId] then + local missing = tgtStackCache[iId][1] + + if missing > 0 then + local k = tgtStackCache[iId][2] local empties = missing >= count return empties, false, k, (empties and count) or missing end - end + end -- No incomplete stack found, return an empty slot or a failure local emptyslots = tgtInv["empties"] @@ -79,8 +95,12 @@ end local function PrepareMoveCaches() InvCache = { [BAG_BACKPACK] = ScanInventory(BAG_BACKPACK), - [BAG_BANK] = ScanInventory(BAG_BANK) + [BAG_BANK] = ScanInventory(BAG_BANK), + ["tgtStackCache"] = { [BAG_BACKPACK] = { }, [BAG_BANK] = { } } } + + RebuildStackCache(BAG_BACKPACK) + RebuildStackCache(BAG_BANK) Moves = { ["stash"] = CollectSingleDirection(InventoryManager.IM_Ruleset.ACTION_STASH), @@ -93,6 +113,7 @@ local function CalculateSingleMove(direction) local IMR = InventoryManager.IM_Ruleset local srcBagType = (direction == 1 and BAG_BACKPACK) or BAG_BANK local tgtBagType = (direction == 1 and BAG_BANK) or BAG_BACKPACK + local tgtStackCache = InvCache["tgtStackCache"][tgtBagType] local srcSlotRepo = Moves[(direction == 1 and "stash") or "retrieve"] if #srcSlotRepo == 0 then @@ -136,12 +157,22 @@ local function CalculateSingleMove(direction) local emptyslots = InvCache[tgtBagType]["empties"] emptyslots[#emptyslots] = nil + + local tgtslot = InvCache[tgtBagType]["items"][tgtSlotId] + tgtStackCache[data.itemInstanceId] = { tgtslot["max"] - tgtslot["current"], tgtSlotId } else -- Stashed onto existing stack, increase count local tgtslot = InvCache[tgtBagType]["items"][tgtSlotId] tgtslot["current"] = tgtslot["current"] + count + tgtStackCache[data.itemInstanceId] = { tgtslot["max"] - tgtslot["current"], tgtSlotId } + + -- If we filled a stack, rescan, maybe there's another incomplete stack. + if tgtStackCache[data.itemInstanceId][1] == 0 then + RebuildStackCache(tgtBagType) + end end + return "ok", { ["srcbag"] = srcBagType, ["srcslot"] = srcSlotId, diff --git a/Modules/Seller.lua b/Modules/Seller.lua index ec76e08..b92b914 100644 --- a/Modules/Seller.lua +++ b/Modules/Seller.lua @@ -42,11 +42,15 @@ function InventoryManager:SellJunk(stolen) end local function OnOpenStore(eventCode) - InventoryManager:SellJunk(false) + if InventoryManager.settings.autosell then + InventoryManager:SellJunk(false) + end end local function OnOpenFence(eventCode) - InventoryManager:SellJunk(true) + if InventoryManager.settings.autosell then + InventoryManager:SellJunk(true) + end end EVENT_MANAGER:RegisterForEvent(InventoryManager.name, EVENT_OPEN_STORE, OnOpenStore) diff --git a/README.md b/README.md index c3d9f66..0c15503 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,10 @@ Not yet another Junker or Bank Addon, this Inventory Manager takes care of your This addon works in conjunction with the CraftStore addon to determine which recipes, style motifs or traits are wanted by your current character or your alts and lets you determine whether to put the given recipes into the bank or even get them from the bank if your character deems them suitable. Rather than working with a preset list of actions and item groupings, this addon allows to set a list of rules which are applied on the items you loot, your inventory or the bank, as you wish. Think the mail filter in Outlook or Thunderbird and you get the idea. + +To use the full range of functions within these AddOns, following AddOns are suggested for installation as well: + * CraftStore (old 3.00+ or CraftStore Fixed and Improved, both work) for identifying your character's and alt's needs + * FCO ItemSaver for customized marking options + +---- Special thanks to: + * Baertram (Your developer docs for FCO ItemSaver really saved the day) diff --git a/Rulesets.lua b/Rulesets.lua index 545a8ec..707dea6 100644 --- a/Rulesets.lua +++ b/Rulesets.lua @@ -1,3 +1,11 @@ +local DEBUG = +function() end +-- d + +local function _tr(str) + return str +end + local IM_Rule = {} local IM_Ruleset = InventoryManager.IM_Ruleset @@ -49,6 +57,18 @@ function IM_Rule:ToString() itemDescription = GetString("IM_RULETXT_WORTHLESS") .. " " .. itemDescription end + if InventoryManager.FCOISL:hasAddon() and self.FCOISMark then + if InventoryManager.FCOISL:IsNoMark(self.FCOISMark) then + itemDescription = GetString("IM_FCOIS_UNMARKED") .. " " .. itemDescription + elseif InventoryManager.FCOISL:IsAnyMark(self.FCOISMark) then + itemDescription = itemDescription .. " " .. GetString("IM_FCOIS_WITHANYMARK") + else + itemDescription = itemDescription .. " " .. zo_strformat( + GetString("IM_FCOIS_MARKEDASX"), + InventoryManager.FCOISL:GetIndexedMark(self.FCOISMark)) + end + end + if self.stolen then itemDescription = GetString("IM_RULETXT_STOLEN") .. " " .. itemDescription end @@ -78,12 +98,12 @@ end function IM_Rule:Filter(data) - filterList = InventoryManager.filtertypes[self.filterType][self.filterSubType] + local filterList = InventoryManager.filtertypes[self.filterType][self.filterSubType] if #filterList > 0 then - attrName = filterList[1] + local attrName = filterList[1] - found = false + local found = false for i = 2, #filterList, 1 do if data[attrName] == filterList[i] then found = true @@ -126,6 +146,9 @@ function IM_Rule:Filter(data) end end + -- FCO ItemSaver marker? + if not InventoryManager.FCOISL:FitMark(data.itemInstanceId, self.FCOISMark) then return false end + -- Part of a set? if self.isSet and not data.isSet then return false end diff --git a/TODO b/TODO index e69de29..19f0470 100644 --- a/TODO +++ b/TODO @@ -0,0 +1 @@ + * New Translations diff --git a/UI/RuleEdit.lua b/UI/RuleEdit.lua index e91e0ac..ec56a09 100644 --- a/UI/RuleEdit.lua +++ b/UI/RuleEdit.lua @@ -222,6 +222,14 @@ function RE:GetControls() setFunc = function(value) RE.editingRule.maxQuality = RE.qualityList["reverse"][value] end, }, { + type = "dropdown", + name = GetString("IM_FCOIS_CHOICE"), + choices = IM.FCOISL:GetDynamicIconChoices(), + getFunc = function() return IM.FCOISL:GetIndexedMark(RE.editingRule.FCOISMark) end, + setFunc = function(value) RE.editingRule.FCOISMark = IM.FCOISL:GetMarkIndex(value) end, + disabled = function() return not IM.FCOISL:hasAddon() end + }, + { type = "checkbox", name = GetString("IM_RE_STOLEN"), width = "half", diff --git a/UI/Settings.lua b/UI/Settings.lua index 4ff1e86..f653f38 100644 --- a/UI/Settings.lua +++ b/UI/Settings.lua @@ -71,6 +71,19 @@ function SE:GetControls() width = "half", --or "half" (optional) }, { + type = "checkbox", + name = GetString("IM_SET_AUTOSELL"), + tooltip = GetString("IM_SET_AUTOSELL_TOOLTIP"), + width = "half", + getFunc = function() return IM.settings.autosell end, + setFunc = function(value) IM.settings.autosell = value end, + }, + { + type = "description", + text = "", + width = "half", + }, + { type = "button", name = GetString("IM_SET_LIST"), tooltip = GetString("IM_SET_LIST_TOOLTIP"), diff --git a/lang/de.lua b/lang/de.lua index 7a6baf8..1f63803 100644 --- a/lang/de.lua +++ b/lang/de.lua @@ -128,6 +128,10 @@ local lang = { IM_SET_DRYRUN_TOOLTIP0 = "Listet die Aktionen, die auf die Gegenstände im Inventar ausgeführt würden", IM_SET_RUN0 = "Inventar bearbeiten", IM_SET_RUN_TOOLTIP0 = "Führt die Wegwerf/Zerstörungsaktionen über die Gegenstände im Inventar aus", + + IM_SET_AUTOSELL0 = "Gegenstände im Müll verkaufen", + IM_SET_AUTOSELL_TOOLTIP0 = "Wenn gesetzt, verkaufe alle Gegenstände im Müll, sobald man einen Händler oder Hehler besucht", + IM_PM_PROFILENAME_TOOLTIP0 = "Namen vom Profil hier eingeben", IM_RM_PRESETRULES0 = "--- Voreingestellte Profile ---", IM_RM_CUSTOMRULES0 = "--- Eigene Profile ---", @@ -145,6 +149,17 @@ local lang = { IM_CUR_GOLD0 = "Goldmnünzen", IM_CUR_TVSTONES0 = "Tel Var Steine", + IM_FCOIS_CHOICE0 = "FCO ItemSaver Markierung", + IM_FCOIS_UNMARKED0 = "unmarkiert(es)", + IM_FCOIS_WITHANYMARK0 = "mit einer Markierung", + IM_FCOIS_MARKEDASX0 = "markiert als <<z:1>>", + IM_FCOIS_NOCAREMARK0 = "Nicht relevant", + IM_FCOIS_NOMARK0 = "Keine Markierung", + IM_FCOIS_ANYMARK0 = "Irgendeine Markierung", + + IM_INIT_DETECTED_CS_OLD0 = "IM: Altes CraftStore 3.00+ AddOn erkannt. Es ist überholt, bitte aktualisieren Sie auf 'CraftStore Fixed And Improved'", + IM_INIT_DETECTED_CS_NEW0 = "IM: CraftStore Fixed And Improved AddOn erkannt", + } for stringId, stringValue in pairs(lang) do diff --git a/lang/en.lua b/lang/en.lua index 8cf8124..5b5ccae 100644 --- a/lang/en.lua +++ b/lang/en.lua @@ -128,6 +128,10 @@ local lang = { IM_SET_DRYRUN_TOOLTIP0 = "List the actions which would be performed on your inventory", IM_SET_RUN0 = "Run over Inventory", IM_SET_RUN_TOOLTIP0 = "Perform the junk/destroy options on your current inventory", + + IM_SET_AUTOSELL0 = "Auto-Sell junked items", + IM_SET_AUTOSELL_TOOLTIP0 = "When set, junked items will be sold whenever visiting a merchant or a fence", + IM_PM_PROFILENAME_TOOLTIP0 = "Enter the name of the new profile here", IM_RM_PRESETRULES0 = "--- Preset profiles ---", IM_RM_CUSTOMRULES0 = "--- Custom profiles ---", @@ -145,6 +149,17 @@ local lang = { IM_CUR_GOLD0 = "gold coins", IM_CUR_TVSTONES0 = "Tel Var stones", + IM_FCOIS_CHOICE0 = "FCO ItemSaver Marking", + IM_FCOIS_UNMARKED0 = "unmarked", + IM_FCOIS_WITHANYMARK0 = "with any mark", + IM_FCOIS_MARKEDASX0 = "marked as <<z:1>>", + IM_FCOIS_NOCAREMARK0 = "Don't care", + IM_FCOIS_NOMARK0 = "No mark", + IM_FCOIS_ANYMARK0 = "Any mark", + + IM_INIT_DETECTED_CS_OLD0 = "IM: Old CraftStore 3.00+ detected. It's outdated, please update to 'CraftStore Fixed And Improved'", + IM_INIT_DETECTED_CS_NEW0 = "IM: CraftStore Fixed And Improved detected", + } for stringId, stringValue in pairs(lang) do