-- $Revision: 212 $ -- Cauldron main file UIPanelWindows["CauldronQueueWindowFrame"] = { area = "left", pushable = 6 }; UIPanelWindows["CauldronFrame"] = { area = "left", pushable = 3 }; Cauldron = LibStub("AceAddon-3.0"):NewAddon("Cauldron", "AceEvent-3.0", "AceTimer-3.0", "AceConsole-3.0", "AceHook-3.0", "LibLogger-1.0"); local L = LibStub("AceLocale-3.0"):GetLocale("Cauldron"); Cauldron.version = "@project-revision@"; Cauldron.date = string.sub("$Date: 2010-08-27 21:03:36 -0700 (Fri, 27 Aug 2010) $", 8, 17); -- key binding names BINDING_HEADER_CAULDRON = "Cauldron"; BINDING_NAME_TOGGLE_CAULDRONSHOPPINGLIST = "Toggle Shopping List Window"; BINDING_NAME_TOGGLE_CAULDRONQUEUE = "Toggle Queue"; BINDING_NAME_CAULDRONRESET = "Reset Cauldron"; Cauldron.options = { buttons = {}, }; Cauldron.vars = { enabled = true, showQueue = true, inventory = {}, filterVersion = tonumber("@project-revision@") or 300, displayTimers = false, }; Cauldron.libs = {}; -- Cauldron.libs.Abacus = LibStub("LibAbacus-3.0"); -- Cauldron.libs.PT = LibStub("LibPeriodicTable-3.1"); Cauldron.libs.GUI = LibStub("AceGUI-3.0"); Cauldron.libs.AceConfigDialog = LibStub("AceConfigDialog-3.0"); -- logging Cauldron:SetLogLevel(Cauldron.logLevels.INFO); -- Cauldron:SetLogLevel(Cauldron.logLevels.DEBUG); if not CauldronLocalDB then CauldronLocalDB = { recipes = {}, window = {}, }; end CURRENT_TRADESKILL = ""; local function GetProfileOption(info) if not Cauldron.db.global.options then Cauldron.db.global.options = {}; end return Cauldron.db.global.options[info.arg]; end local function SetProfileOption(info, value) if not Cauldron.db.global.options then Cauldron.db.global.options = {}; end Cauldron.db.global.options[info.arg] = value; end function Cauldron:OnInitialize() local globalDbDefaults = { profile = { }, realm = { userdata = {}, -- Stores all known characters }, global = { options = { AutoOpenShoppingList = true, ModifyTooltip = true, }, } }; self.db = LibStub("AceDB-3.0"):New("CauldronDB", globalDbDefaults); -- self.localDb = LibStub("AceDB-3.0"):New("CauldronLocalDB", localDbDefaults); -- set up slash command options local options = { desc = L["Cauldron"], handler = Cauldron, type = 'group', args = { general = { type = 'group', cmdInline = true, order = -1, get = GetProfileOption, set = SetProfileOption, name = L["General"], args = { autoOpenShoppingList = { type = 'toggle', order = 1, width = "double", name = L["Auto-open shopping list?"], desc = L["Automatically open the shopping list when the bank, guild bank, or a merchant window is opened."], arg = "AutoOpenShoppingList", }, modifyTooltip = { type = 'toggle', order = 1, width = "double", name = L["Modify tooltip?"], desc = L["Adds information to the game tooltip when displaying information about certain crafting items."], arg = "ModifyTooltip", }, --[[ enableLilSparkysWorkshop = { type = 'toggle', order = 1, width = "double", name = L["Enable support for LilSparky's Workshop?"], desc = L["Registers Cauldron with LilSparky's Workshop so that pricing information will show up in the recipe list."], arg = "EnableLilSparkysWorkshop", }, --]] }, }, shoppinglist = { name = L["Shopping list"], desc = L["Open shopping list window"], type = 'execute', func = function() Cauldron:ShowShoppingList() end, }, enable = { name = L["Enable Cauldron"], desc = L["Use Cauldron as your tradeskill interface"], type = 'execute', func = function() Cauldron:Enable() end, }, disable = { name = L["Disable Cauldron"], desc = L["Use the standard Blizzard window as your tradeskill interface"], type = 'execute', func = function() Cauldron:Disable() end, }, version = { name = L["Version"], desc = L["Shows the version number of the addon"], type = 'execute', func = function() self:DisplayVersion() end, }, --@alpha@ debug = { name = L["Debug"], desc = L["Toggles whether Cauldron displays debug messages"], type = 'toggle', get = function() return Cauldron:GetLogLevel(); end, set = function(val) self:debug("val: "..tostring(val)); if val == "debug" then Cauldron:SetLogLevel(Cauldron.logLevels.DEBUG); else Cauldron:SetLogLevel(Cauldron.logLevels.INFO); end end, }, --@end-alpha@ --[[ forget = { name = L["Forget"], desc = L["Tells Cauldron to forget information for a character, recipe, or skill"], type = 'input', -- get = function() return; end, set = function(info, v) Cauldron:Forget(v); end, usage = L["forget [skill <name>||recipe <name>]"], validate = function(val) end, confirm = L["Forget skills for this character?"], -- func = function() self:Forget(arg1) end, }, --]] reset = { name = L["Reset"], desc = L["Resets Cauldron to a fresh state"], type = 'execute', func = function() self:Reset() end, }, -- debug = LibStub('LibLogDebug-1.0'):GetAce3OptionTable(self, 110), }, } -- register slash command with options LibStub("AceConfig-3.0"):RegisterOptionsTable("Cauldron", options, {"cauldron"}); --[[ initialize PT for i=1,GetNumAddOns() do local metadata = GetAddOnMetadata(i, "X-PeriodicTable-3.0-Module"); if metadata then local name, _, _, enabled = GetAddOnInfo(i); if enabled then LoadAddOn(name); end end end collectgarbage(); --]] local config = Cauldron.libs.AceConfigDialog; -- add config UI to Blizzard interface self.optionsFrames = {}; -- The ordering here matters, it determines the order in the Blizzard Interface Options self.optionsFrames.general = config:AddToBlizOptions("Cauldron", L["Cauldron"], nil, "general"); -- self.optionsFrames.profile = config:AddToBlizOptions("Cauldron", L["Profiles"], L["Cauldron"], "profile"); --@alpha@ -- register test suite if WoWUnit then WoWUnit:AddTestSuite("CauldronTestSuite", CauldronTestSuite); end --@end-alpha@ -- let the user know the addon is loaded self:Print(L["Cauldron loaded; version"],Cauldron.version); end function Cauldron:InitPlayer() -- check if the database needs to be updated if self.db.global.version then -- TODO: future checks else -- TODO: future checks end if not self.vars.playername then self.vars.playername = UnitName("player"); if not self.db.realm.userdata[self.vars.playername] then self.db.realm.userdata[self.vars.playername] = {}; end -- if not self.db.realm.userdata[self.vars.playername].knownRecipes then -- self.db.realm.userdata[self.vars.playername].knownRecipes = {}; -- end if not self.db.realm.userdata[self.vars.playername].skills then self.db.realm.userdata[self.vars.playername].skills = {}; end if not self.db.realm.userdata[self.vars.playername].queue then self.db.realm.userdata[self.vars.playername].queue = CauldronQueue:NewQueue(); end if not self.db.realm.userdata[self.vars.playername].options then self.db.realm.userdata[self.vars.playername].options = { autoBuy = false, compactView = false, }; end if not self.db.realm.shopping then self.db.realm.shopping = CauldronShopping:NewList(); end --[[ if not self.localDb.recipes then self.localDb.recipes = {}; end --]] end -- store the current revision in the database self.db.global.version = Cauldron.version; end function Cauldron:OnEnable() -- set init flag, for some callbacks self.initializing = true; self:InitPlayer(); -- scan bags self:ScanBags(); -- register for events we're interested in self:RegisterEvent("TRADE_SKILL_SHOW", "OnTradeShow"); self:RegisterEvent("TRADE_SKILL_CLOSE", "OnTradeClose"); self:RegisterEvent("ADDON_LOADED", "OnAddonLoaded"); self:RegisterEvent("BANKFRAME_OPENED", "OnBankOpened"); self:RegisterEvent("BANKFRAME_CLOSED", "OnBankClosed"); self:RegisterEvent("MERCHANT_SHOW", "OnMerchantShow"); self:RegisterEvent("MERCHANT_CLOSED", "OnMerchantClose"); self:RegisterEvent("UI_ERROR_MESSAGE", "OnError"); self:RegisterEvent("UNIT_QUEST_LOG_CHANGED", "OnQuestLogChanged"); self:RegisterEvent("ACHIEVEMENT_EARNED", "OnAchievementEarned"); -- setup hooks for tooltips self:HookTooltips(); -- initialize the achievement map self:CreateAchievementSkillMap(); -- replace the "show" function for the standard tradeskill frame LoadAddOn("Blizzard_TradeSkillUI"); self.blizzTradeSkillShow = TradeSkillFrame_Show; TradeSkillFrame_Show = function() -- Cauldron:info("TradeSkillFrame_Show"); end -- clear init flag self.initializing = false; end function Cauldron:OnDisable() -- TODO end function Cauldron:OnAddonLoaded(event, addon) -- show the shopping list? if self.db.profile.showShoppingList then Cauldron:ShowShoppingList(); else if CauldronShopping:ContainsItems(self.db.realm.shopping) then Cauldron:ShowShoppingList(); end end end function Cauldron:OnEvent(event, ...) --[[ if event == "UNIT_PORTRAIT_UPDATE" then local arg1 = ...; if arg1 == "player" then SetPortraitTexture(CauldronQueueWindowFramePortrait, "player"); end end --]] end function Cauldron:OnTradeShow() TradeSkillFrame_Update(); -- seems to fix the early bailout of trade skill iterations Cauldron:InitializeSkillData(); -- update our known skills -- if not Cauldron.updatingSkills then Cauldron.updatingSkills = true; -- Cauldron:NeedsSkillUpdate(); Cauldron:UpdateSkills(); -- Cauldron:NewUpdateSkills(); -- Cauldron:info("on trade show: skills="..tostring(Cauldron:GetSkillCount())); -- end -- register for events that are needed when the window is open self:RegisterEvent("UPDATE_TRADESKILL_RECAST", "OnTradeSkillRecast"); self:RegisterEvent("UNIT_SPELLCAST_START", "OnSpellcastStart"); self:RegisterEvent("UNIT_SPELLCAST_STOP", "OnSpellcastStop"); self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", "OnSpellcastSucceed"); self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED", "OnSpellcastInterrupt"); -- show the UI frame -- Cauldron.needsRedraw = true; self:Frame_Show(); end function Cauldron:OnTradeUpdate(event) -- TODO end function Cauldron:OnTradeClose() -- hide the window self:Frame_Hide(); -- unregister for events that are only needed when the window is open self:UnregisterEvent("UPDATE_TRADESKILL_RECAST"); self:UnregisterEvent("UNIT_SPELLCAST_START"); self:UnregisterEvent("UNIT_SPELLCAST_STOP"); self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED"); self:UnregisterEvent("UNIT_SPELLCAST_INTERRUPTED"); end function Cauldron:OnSkillUpdate(event) Cauldron:UpdateSkillItemCounts(); --[[ if CURRENT_TRADESKILL ~= "" then if not Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[CURRENT_TRADESKILL] then return; end -- TODO check if the skill rank has changed, and unselect -- Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[CURRENT_TRADESKILL].window.selected = 0; if not Cauldron.updatingSkills then -- Cauldron:info("Requesting skill update for skill update"); Cauldron.updatingSkills = Cauldron:NeedsSkillUpdate(); Cauldron:UpdateSkills(); Cauldron.updatingSkills = false; CauldronQueue:CalculateAllRequiredItems(Cauldron.db.realm.userdata[Cauldron.vars.playername].queue); Cauldron.needsRedraw = true; self:Frame_Update(); end end --]] end function Cauldron:OnTradeSkillRecast() -- keep the processing flag set self.processing = true; -- self:UpdateSkills(); CauldronInputBox:SetNumber(GetTradeskillRepeatCount()); Cauldron:UpdateSkillItemCounts(); Cauldron:UpdateSkillList(); -- Cauldron.needsRedraw = true; -- self:Frame_Update(); end function Cauldron:OnBagUpdate(event, bagid) -- make sure we're not reacting to initial bag update events when the DB isn't initialized yet if (self.initializing) or (not self.db) or (not self.vars) then return; end local queue = self.db.realm.userdata[self.vars.playername].queue; CauldronQueue:CalculateAllRequiredItems(queue); Cauldron:UpdateQueue(); Cauldron:UpdateSkillItemCounts(); Cauldron:UpdateSkillList(); Cauldron:UpdateShoppingList(); end function Cauldron:OnMerchantShow() -- check if there's anything in the shopping list if CauldronShopping:ContainsItems(Cauldron.db.realm.shopping) then if Cauldron.db.global.options[AutoOpenShoppingList] then Cauldron:ShowShoppingList(); end if Cauldron.db.realm.userdata[Cauldron.vars.playername].options.autoBuy then CauldronShopping:AutoBuyShoppingItems(Cauldron.db.realm.shopping, Cauldron.vars.playername); end end end function Cauldron:OnMerchantClose() if not CauldronShopping:ContainsItems(Cauldron.db.realm.shopping) then if Cauldron.db.global.options[AutoOpenShoppingList] then Cauldron:HideShoppingList(); end end end function Cauldron:OnBankOpened() -- check if there's anything in the shopping list if CauldronShopping:ContainsItems(Cauldron.db.realm.shopping) then if Cauldron.db.global.options[AutoOpenShoppingList] then Cauldron:ShowShoppingList(); end end end function Cauldron:OnBankClosed() if not CauldronShopping:ContainsItems(Cauldron.db.realm.shopping) then if Cauldron.db.global.options[AutoOpenShoppingList] then Cauldron:HideShoppingList(); end end end function Cauldron:OnQuestLogChanged() -- TODO end function Cauldron:OnAchievementEarned() -- update the achievement skill map Cauldron:CreateAchievementSkillMap(); Cauldron:UpdateSkillList(); end function Cauldron:OnSpellcastStart(event, unit, spell, rank) self.processing = true; end function Cauldron:OnSpellcastStop(event, unit, spell, rank) self.processing = false; end function Cauldron:OnSpellcastSucceed(event, unit, spell, rank) -- ignore if the unit was not the player if unit ~= "player" then return; end local queue = self.db.realm.userdata[self.vars.playername].queue; if Cauldron.makingItem then CauldronQueue:AdjustItemCount(queue, self.makingItem, -1); local count = Cauldron.makingItemCount - 1; if count < 1 then Cauldron.makingItem = nil; Cauldron.makingItemCount = nil; else Cauldron.makingItemCount = count; end end Cauldron:UpdateSkillItemCounts(); Cauldron:UpdateSkillList(); --[[ -- adjust queue, but only if window is open if CauldronFrame:IsShown() then self:debug("makingItemSpell: "..tostring(self.makingItemSpell)); if self.makingItemSpell == spell then self.processing = false; CauldronQueue:AdjustItemCount(queue, self.makingItem, -1); end end --]] end function Cauldron:OnSpellcastInterrupt(event, unit, spell, rank) Cauldron.makingItem = nil; Cauldron.makingItemCount = nil; self.processing = false; end function Cauldron:OnError() -- TODO end function Cauldron:TradeSkillFrame_SetSelection(id) -- TODO end function Cauldron:GetSelectedSkill() local skillName = CURRENT_TRADESKILL; if IsTradeSkillLinked() then skillName = "Linked-"..skillName; end if IsTradeSkillGuild() then skillName = "Guild-"..skillName; end if (not self.db.realm.userdata[self.vars.playername]) or (not self.db.realm.userdata[self.vars.playername].skills[skillName]) then return; end local selected = self.db.realm.userdata[self.vars.playername].skills[skillName].window.selected; -- local selected = GetTradeSkillSelectionIndex(); if not selected then return nil; end for name, info in pairs(self.db.realm.userdata[self.vars.playername].skills[skillName].recipes) do if selected == info.index then return info; end end return nil; end function Cauldron:QueueAllTradeSkillItem() local skillInfo = Cauldron:GetSelectedSkill(); if skillInfo then local amount = skillInfo.available; local potential = Cauldron:GetPotentialCraftCount(skillInfo); local queueAmount = 0; -- if regular amount > 0 ... if amount > 0 then if (potential > 0) and IsShiftKeyDown() then queueAmount = potential; else queueAmount = amount; end else queueAmount = potential; end if queueAmount > 0 then CauldronQueue:AddItem(self.db.realm.userdata[self.vars.playername].queue, skillInfo, queueAmount); Cauldron:UpdateQueue(); -- update the shopping list Cauldron:UpdateShoppingListFromQueue(); else Cauldron:info("No amount to queue for "..skillInfo.name.."."); end end end function Cauldron:QueueTradeSkillItem() local skillInfo = Cauldron:GetSelectedSkill(); if skillInfo then local amount = CauldronInputBox:GetNumber(); if not amount or amount < 1 then amount = 1; end CauldronQueue:AddItem(self.db.realm.userdata[self.vars.playername].queue, skillInfo, amount); Cauldron:UpdateQueue(); -- update the shopping list Cauldron:UpdateShoppingListFromQueue(); end end function Cauldron:CreateAllTradeSkillItem() if (not PartialPlayTime()) and (not NoPlayTime()) then CauldronInputBox:ClearFocus(); local skillInfo = Cauldron:GetSelectedSkill(); CauldronInputBox:SetNumber(skillInfo.available); DoTradeSkill(skillInfo.index, skillInfo.available); Cauldron:UpdateSkillItemCounts(); Cauldron:UpdateSkillList(); end end function Cauldron:CreateTradeSkillItem() if ((not PartialPlayTime()) and (not NoPlayTime())) then CauldronInputBox:ClearFocus(); local skillInfo = Cauldron:GetSelectedSkill(); local amount = CauldronInputBox:GetNumber(); DoTradeSkill(skillInfo.index, amount); Cauldron:UpdateSkillItemCounts(); Cauldron:UpdateSkillList(); end end function Cauldron:ProcessQueue() if IsTradeSkillLinked() then self:error("Can't process queue for linked tradeskill!"); return; end --[[ TODO: update queue logic -- look at first item, if it can be made, make it (lower of queued quantity vs. quantity able to make) -- if items other than first can be made and first can't, ask user if they want to make that instead --]] local queue = CauldronQueue:GetItems(self.db.realm.userdata[self.vars.playername].queue); local queueInfo = nil; local skillInfo = nil; if #queue > 0 then -- see if first item can be made queueInfo = queue[1]; -- self:debug("ProcessQueue: queueInfo="..queueInfo.name); skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name); -- self:debug("ProcessQueue: skillInfo="..tostring(skillInfo)); if skillInfo.available > 0 then -- self:debug("First item in main queue can be made "..skillInfo.available.." times"); self:SubmitItemToProcess(queueInfo, skillInfo, math.min(skillInfo.available, queueInfo.amount)); return; --[[ else -- see if queue contains other items that can be made if the first can't be if #queue > 1 then for i=2,#queue do queueInfo = queue[i]; skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name); self:debug("ProcessQueue: skillInfo="..tostring(skillInfo)); if skillInfo.available > 0 then -- present dialog to user to move item to top of queue Cauldron:ConfirmDialog(L["Confirm"], L["message"], L["Okay"], function() Cauldron:info("Okay"); -- TODO end, L["Cancel"], function() Cauldron:info("Cancel"); -- TODO end); return; end end end --]] end end -- find intermediate items that need to be crafted local intQueue = CauldronQueue:GetIntermediates(self.db.realm.userdata[self.vars.playername].queue); if #intQueue > 0 then queueInfo = intQueue[1]; skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name); else if #queue > 0 then queueInfo = queue[1]; skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name); end end self:SubmitItemToProcess(queueInfo, skillInfo); end function Cauldron:SubmitItemToProcess(queueInfo, skillInfo, amount) if queueInfo and skillInfo then if queueInfo.tradeskill ~= CURRENT_TRADESKILL then local msg = string.format(L["Crafting %1$s requires the %2$s skill."], queueInfo.name, queueInfo.tradeskill); UIErrorsFrame:AddMessage(msg, 1.0, 0.0, 0.0); return; end Cauldron:ProcessItem(skillInfo, queueInfo, amount or queueInfo.amount); else if not queueInfo then self:error("Missing queue info!"); end if not skillInfo then self:error("Missing skill info!"); end end end function Cauldron:ProcessItem(skillInfo, queueInfo, amount) if not skillInfo then self:error("ProcessItem: Missing skill info!"); return; end if amount < 1 then self:error("ProcessItem: Invalid amount specified: "..tostring(amount).."!"); return; end if ((not PartialPlayTime()) and (not NoPlayTime())) then -- record the item we're making self.makingItem = Cauldron:GetNameFromLink(queueInfo.link); self.itemCurrentCount = GetItemCount(skillInfo.itemLink); self.makingItemSpell = queueInfo.spell or Cauldron:GetNameFromLink(queueInfo.link); self.makingItemCount = amount; self.queueInfo = queueInfo; -- tell the user what we're doing self:Print(string.format(L["Crafting %1$d of %2$s..."], amount, queueInfo.link)); -- do it self.processing = true; DoTradeSkill(skillInfo.index, amount); else -- TODO: notify player? Cauldron:warn(L["Unable to process item due to play time limitation."]); end end function Cauldron:RemoveQueueItem(name) CauldronQueue:RemoveItem(Cauldron:GetQueue(), name); end function Cauldron:IncreaseItemPriority(name, top) CauldronQueue:IncreasePriority(Cauldron:GetQueue(), name, top); end function Cauldron:DecreaseItemPriority(name, bottom) CauldronQueue:DecreasePriority(Cauldron:GetQueue(), name, bottom); end function Cauldron:DecreaseItemCount(name) CauldronQueue:AdjustItemCount(Cauldron:GetQueue(), name, -1); end function Cauldron:IncreaseItemCount(name) CauldronQueue:AdjustItemCount(Cauldron:GetQueue(), name, 1); end function Cauldron:FirstPage() local skillName = CURRENT_TRADESKILL; if skillName == "UNKNOWN" then return; end if IsTradeSkillLinked() then skillName = "Linked-"..skillName; end if IsTradeSkillGuild() then skillName = "Guild-"..skillName; end self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset = 0; CauldronSkillListScrollFrame:SetVerticalScroll(0); end function Cauldron:PrevPage() local skillName = CURRENT_TRADESKILL; if skillName == "UNKNOWN" then return; end if IsTradeSkillLinked() then skillName = "Linked-"..skillName; end if IsTradeSkillGuild() then skillName = "Guild-"..skillName; end local offset = self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset; if not offset then self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset = 0; else self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset = math.max(0, offset - CAULDRON_SKILL_LIST_MAX); end CauldronSkillListScrollFrame:SetVerticalScroll(0); end function Cauldron:NextPage() local skillName = CURRENT_TRADESKILL; if skillName == "UNKNOWN" then return; end if IsTradeSkillLinked() then skillName = "Linked-"..skillName; end if IsTradeSkillGuild() then skillName = "Guild-"..skillName; end local offset = self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset; local numSkills = table.getn(Cauldron:GetSkillList(self.vars.playername, skillName)); -- local numSkills = self.db.realm.userdata[self.vars.playername].skills[skillName].skillCount or 0; if not offset then self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset = CAULDRON_SKILL_LIST_MAX; else if (offset + CAULDRON_SKILL_LIST_MAX) <= numSkills then self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset = offset + CAULDRON_SKILL_LIST_MAX; end end CauldronSkillListScrollFrame:SetVerticalScroll(0); end function Cauldron:LastPage() local skillName = CURRENT_TRADESKILL; if skillName == "UNKNOWN" then return; end if IsTradeSkillLinked() then skillName = "Linked-"..skillName; end if IsTradeSkillGuild() then skillName = "Guild-"..skillName; end local numSkills = table.getn(Cauldron:GetSkillList(self.vars.playername, skillName)); self.db.realm.userdata[self.vars.playername].skills[skillName].window.offset = math.floor(numSkills / CAULDRON_SKILL_LIST_MAX) * CAULDRON_SKILL_LIST_MAX; CauldronSkillListScrollFrame:SetVerticalScroll(0); end function Cauldron:GetQueue(player) if not player then player = self.vars.playername; end local queue = self.db.realm.userdata[player].queue; if not queue then queue = CauldronQueue:NewQueue(); self.db.realm.userdata[player].queue = queue; end return queue; end function Cauldron:AddItemToShoppingList(itemName, amount, replace) if replace then CauldronShopping:RemoveFromList(self.db.realm.shopping, self.vars.playername, itemName); end CauldronShopping:AddToList(self.db.realm.shopping, self.vars.playername, itemName, amount); Cauldron:ShowShoppingList(); end function Cauldron:RemoveShoppingListItem(requestor, itemName) CauldronShopping:RemoveFromList(self.db.realm.shopping, requestor, itemName, nil); -- if not CauldronShopping:ContainsItems(self.db.realm.shopping) then -- Cauldron:HideShoppingList(); -- end end function Cauldron:UpdateShoppingListFromQueue() local items = CauldronQueue:GetReagents(self.db.realm.userdata[self.vars.playername].queue); if items then for i,item in ipairs(items) do local id = Cauldron:GetIdFromLink(item.link); local have = GetItemCount(item.link); local need = math.max(0, item.amount - have); if (need > 0) and Cauldron:IsVendorItem(id) then Cauldron:AddItemToShoppingList(item.name, need, true); end end end end function Cauldron:SetWindowOffset(offset) local skillName = CURRENT_TRADESKILL; if skillName == "UNKNOWN" then return; end if IsTradeSkillLinked() then skillName = "Linked-"..skillName; end if IsTradeSkillGuild() then skillName = "Guild-"..skillName; end Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName].window.offset = offset; end function Cauldron:LocaleString(str) return L[str]; end function Cauldron:DisplayVersion() self:Print(L["Version "],Cauldron.version); self:Print(L["Date: "],Cauldron.date); self:Print(L["Oh. Smells like barbecued dog hair."]); end ---------------------------------------------------------------- -- Tooltip Functions ---------------------------------------------------------------- function Cauldron:HookTooltips() self:SecureHook(GameTooltip, "SetBagItem"); self:SecureHook(GameTooltip, "SetInventoryItem"); self:SecureHook(GameTooltip, "SetLootItem"); self:SecureHook(GameTooltip, "SetHyperlink"); self:SecureHook(GameTooltip, "SetTradeSkillItem"); self:SecureHook(GameTooltip, "SetMerchantItem"); self:SecureHook(GameTooltip, "SetAuctionItem"); -- self:SecureHook(GameTooltip, "SetTrainerService"); self:SecureHook(GameTooltip, "SetGuildBankItem"); self:SecureHook("SetItemRef"); end function Cauldron:AddToTooltip(tooltip, id) if not Cauldron.db.global.options.ModifyTooltip then return; end if not id then return; end local skillList = Cauldron:GetSkillsForReagent(id); -- add favorite info local favSkills = filterFavoriteSkills(skillList); if favSkills and #favSkills > 0 then if #favSkills > 3 then -- if the skill list has more than 3 items, summarize local favInfo = string.format(L["Needed for %1$d favorite skills"], #favSkills); tooltip:AddLine("|r"..favInfo.."|r"); else -- if the skill list is 3 or less, list all for i,skill in ipairs(favSkills) do local skillName,skillLink = string.split(";", skill, 2); local favInfo = L["Needed for favorites:"]; if i > 1 then favInfo = " "; end local skillInfo = Cauldron:GetSkillInfoForLink(skillLink); local color; if TradeSkillTypeColor then color = TradeSkillTypeColor[skillInfo.difficulty]; else local colorMap = { optimal = {r=1.0, g=0.5, b=0.25}, medium = {r=1.0, g=1.0, b=0.0}, easy = {r=0.25, g=0.75, b=0.25}, }; color = colorMap[skillInfo.difficulty]; end local colorStr = "|r"; if color then colorStr = string.format("|cff%02x%02x%02x", (color.r*255), (color.g*255), (color.b*255)); end tooltip:AddDoubleLine(favInfo, colorStr..skillInfo.name.."|r"); end end end -- add skill-up info local skillups = filterSkillups(skillList); if skillups and #skillups > 0 then if #skillups > 3 then -- if the skill list has more than 3 items, summarize local levelInfo = string.format(L["Needed for %1$d skills for leveling"], #skillups); tooltip:AddLine("|r"..levelInfo.."|r"); else -- if the skill list is 3 or less, list all for i,skill in ipairs(skillups) do local skillName,skillLink = string.split(";", skill, 2); local levelInfo = L["Needed for leveling:"]; if i > 1 then levelInfo = " "; end local skillInfo = Cauldron:GetSkillInfoForLink(skillLink); local color; if TradeSkillTypeColor then color = TradeSkillTypeColor[skillInfo.difficulty]; -- else -- color = {r=1.0, g=1.0, b=1.0}; end local colorStr = "|r"; if color then colorStr = string.format("|cff%02x%02x%02x", (color.r*255), (color.g*255), (color.b*255)); end tooltip:AddDoubleLine(levelInfo, colorStr..skillInfo.name.."|r"); end end end -- force resize of the tooltip tooltip:Show(); end function filterFavoriteSkills(skillList) if not skillList then return nil; end local faves = {}; for i,skill in ipairs(skillList) do local skillName, skillLink = string.split(";", skill, 2); local skillInfo = Cauldron:GetSkillInfoForLink(skillLink); if skillInfo and skillName then if Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName] then if Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName].window.skills[skillInfo.name] then if Cauldron.db.realm.userdata[Cauldron.vars.playername].skills[skillName].window.skills[skillInfo.name].favorite then table.insert(faves, skill); end end end end end return faves; end function filterSkillups(skillList) if not skillList then return nil; end local skillups = {}; local difficulty = { optimal = 4, medium = 3, easy = 2, trivial = 1, }; for i,skill in ipairs(skillList) do local skillName, skillLink = string.split(";", skill, 2); local skillInfo = Cauldron:GetSkillInfoForLink(skillLink); if skillInfo then if difficulty[skillInfo.difficulty] > 1 then table.insert(skillups, skill); end end end return skillups; end function Cauldron:SetTradeSkillItem(tooltip, itemIndex, reagentIndex) --[[ local link; local name; if reagentIndex then local skillInfo = Cauldron:GetSkillInfoByIndex(itemIndex); local reagentInfo = Cauldron:GetReagentInfoByIndex(itemIndex, reagentIndex); if reagentInfo then local id = Cauldron:GetIdFromLink(reagentInfo.link); self:AddToTooltip(tooltip, id); -- let the user know if the reagent is a "non-key" reagent if not reagentInfo.key then tooltip:AddLine("|cff666666"..L["Available at vendor"].."|r"); end end else -- link = GetTradeSkillItemLink(itemIndex); -- name = Cauldron:GetIdFromLink(link); end --]] tooltip:Show(); end function Cauldron:SetHyperlink(tooltip, link) -- local name = Cauldron:GetNameFromLink(link); -- local skillInfo = Cauldron: local id = Cauldron:GetIdFromLink(link); end function Cauldron:SetItemRef(link, text, button) -- if IsControlKeyDown() or IsShiftKeyDown() then return end if ( IsModifiedClick() ) then return end local id = Cauldron:GetIdFromLink(link); if id then -- self:AddToTooltip(ItemRefTooltip,id); end end function Cauldron:SetBagItem(tooltip, bag, slot) local link = GetContainerItemLink(bag, slot); local id = Cauldron:GetIdFromLink(link); self:AddToTooltip(tooltip, id); end function Cauldron:SetGuildBankItem(tooltip, tab, slot) local link = GetGuildBankItemLink(tab, slot); local id = Cauldron:GetIdFromLink(link); self:AddToTooltip(tooltip, id); end function Cauldron:SetInventoryItem(tooltip, unit, slot, nameOnly) if slot > 39 and slot < 68 then local link = GetContainerItemLink(BANK_CONTAINER, slot-39) local id = Cauldron:GetIdFromLink(link); self:AddToTooltip(tooltip, id); end end function Cauldron:SetLootItem(tooltip, index) local link = GetLootSlotLink(index); local id = Cauldron:GetIdFromLink(link); self:AddToTooltip(tooltip, id); end function Cauldron:SetMerchantItem(tooltip, index) local link = GetMerchantItemLink(index); local id = Cauldron:GetIdFromLink(link); self:AddToTooltip(tooltip, id); end function Cauldron:SetAuctionItem(tooltip, type, index) local link = GetAuctionItemLink(type, index); local id = Cauldron:GetIdFromLink(link); self:AddToTooltip(tooltip, id); end ---------------------------------------------------------------------- -- Property functions ---------------------------------------------------------------------- --[[ Databroker Stuff --]] local ldb = LibStub:GetLibrary("LibDataBroker-1.1", true) if ldb then ldb:NewDataObject("Cauldron", { type = "launcher", text = "Cauldron shopping list", icon = "Interface\\Icons\\INV_Elemental_Mote_Nether", OnClick = function(frame, button) if button == "LeftButton" then Cauldron:ShowShoppingList(); elseif button == "RightButton" then Cauldron:ShoppingList_Toggle(); end end, OnTooltipShow = function(tooltip) tooltip:AddLine("Cauldron " .. Cauldron.version) end, }) end --[[ Database table structure: ["userdata"] = { ["<character-name>"] = { ["queue"] = { ["intermediate"] = { ["<skill>"] = { ["tradeskill"] = "<tradeskill>", -- human-readable name of the tradeskill that contains this skill ["name"] = "<skill>", -- human-readable name of the skill ["amount"] = <amount>, -- amount of this skill that must be executed ["priority"] = <priority>, -- priority of the skill, for ordering in the queue ["icon"] = "<icon>", -- the icon path of the skill, for display ["link"] = "<link>", }, }, ["main"] = { ["<skill>"] = { ["tradeskill"] = "<tradeskill>", ["name"] = "<skill>", ["amount"] = <amount>, ["priority"] = <priority>, ["icon"] = "<icon>", ["link"] = "<link>", }, }, ["reagents"] = { ["<reagent>"] = { ["tradeskill"] = "<tradeskill>", ["name"] = "<reagent>", ["amount"] = <amount>, ["icon"] = "<icon>", ["link"] = "<link>", }, }, }, ["skills"] = { ["<tradeskill>"] = { ["skillLevel"] = <level>, ["skillCount"] = <count>, ["lastScanDate"] = <millis>, ["window"] = { -- window metadata ["categories"] = { -- category cache of the tradeskill ["<category>"] = { -- name of the category ["shown"] = true, -- whether the category is being shown in the skill list }, ... }, ["search"] = "", -- search text the user typed in the search box ["filter"] = { -- flags for the filter dropdown ["sortAlpha"] = false, -- should the list be sorted alphabetically (mutually-exclusive to "sortDifficulty" and "sortBenefit") ["sortDifficulty"] = true, -- should the list be sorted by difficulty (mutually-exclusive to "sortAlpha" and "sortBenefit") ["sortBenefit"] = false, -- should the list be sorted by potentcy of the benefit the crafted item/enchantment grants (mutually-exclusive to "sortDifficulty" and "sortAlpha") UNUSED ["haveAllReagents"] = false, -- should only items with all required reagents be shown in the list? ["haveKeyReagents"] = false, -- should only items with all key reagents be shown in the list? ["haveAnyReagents"] = false, -- should only items with any reagents be shown in the list? ["optimal"] = true, -- should optimal skills be shown? ["medium"] = true, -- should medium skills be shown? ["easy"] = true, -- should easy skills be shown? ["trivial"] = false, -- should trivial skills be shown? ["favorites"] = false, -- should only favorite skills be shown? }, ["skills"] = { ["<skill>"] = { ["expanded"] = false, -- should the skill's reagents be displayed? ["favorite"] = false, -- is the skill a favorite? }, ... }, ["selected"] = 1, -- skill index of the selected skill (gets reset on skill update events, like skill level increases and training) ["slots"] = { -- slot cache ["INVTYPE_FEET"] = true, ["INVTYPE_BAG"] = true, ["INVTYPE_CLOAK"] = true, ["(none)"] = true, -- special slot for items that don't have an assigned slot ["INVTYPE_WRIST"] = true, ["INVTYPE_WAIST"] = true, ["INVTYPE_CHEST"] = true, ["INVTYPE_LEGS"] = true, ["INVTYPE_HAND"] = true, }, }, ["recipes"] = { ["<skill>"] = { ["index"] = <skill index>, ["name"] = "<skill>", ["link"] = "<link>", ["icon"] = "<icon>", ["verb"] = "<verb>", -- this is present for skills that cast, like enchantments ["tradeskill"] = "<tradeskill>", ["difficulty"] = "<difficulty>", ["available"] = <available>, ["minMade"] = <min>, ["maxMade"] = <max>, ["slot"] = "INVTYPE_LEGS", ["defaultCategory"] = "<category>", ["reagents"] = { { ["name"] = "<reagent name>", ["numRequired"] = <required>, -- the number required of this reagent to complete one execution of the skill ["index"] = <reagent index>, -- the index of the reagent, for API call usage ["skillIndex"] = <skill index>, -- the index of the skill, for API call usage ["icon"] = "<icon>", ["key"] = <true-or-false>, }, -- [1] ... }, ["keywords"] = "<skill>,<reagent>,...", }, }, }, }, }, }, ["shopping"] = { ["<character-name>"] = { ["<reagent name>"] = <amount>, }, }, --]] function Cauldron:Enable() Cauldron.vars.enabled = true; self:Print(L["enabled"]); end function Cauldron:Disable() Cauldron.vars.enabled = false; self:Print(L["disabled"]); Cauldron:Frame_Hide(); end --[==[ function Cauldron:Forget(param) if not name then return; end self:info("param: "..tostring(param)); for k,v in pairs(name) do self:info("k: "..k.."; v: "..tostring(v)); --[[ if type(v) == "table" then for k2,v2 in pairs(v) do self:info("k2: "..k2.."; v2: "..tostring(v2)); end end --]] end end --]==] function Cauldron:Reset() self:Print("Starting reset."); -- close the window self:Print("Closing windows..."); Cauldron:Frame_Hide(); HideUIPanel(TradeSkillFrame); Cauldron:HideShoppingList(); -- reset some internal variables self:Print("Resetting internal variables..."); self.vars.enabled = true; -- reset the shopping list self:Print("Clearing shopping list..."); self.db.realm.shopping = CauldronShopping:NewList(); -- reset the skills for each character self:Print("Purging data structures..."); for toon, info in pairs(self.db.realm.userdata) do self.db.realm.userdata[toon] = {}; self.db.realm.userdata[toon].skills = {}; self.db.realm.userdata[toon].queue = CauldronQueue:NewQueue(); self.db.realm.userdata[toon].options = { autoBuy = false, compactView = false, }; end self:Print("Reset complete."); end