-- $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 = 300, -- TODO: @project-revision@,
};
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_UPDATE", "OnSkillUpdate");
self:RegisterEvent("TRADE_SKILL_CLOSE", "OnTradeClose");
-- self:RegisterEvent("TRADE_TARGET_ITEM_CHANGED", "OnTradeItemChanged");
self:RegisterEvent("SKILL_LINES_CHANGED", "OnSkillUpdate");
self:RegisterEvent("ADDON_LOADED", "OnAddonLoaded");
self:RegisterEvent("UNIT_PORTRAIT_UPDATE", "OnEvent");
self:RegisterEvent("BANKFRAME_OPENED", "OnBankOpened");
self:RegisterEvent("BANKFRAME_CLOSED", "OnBankClosed");
-- self:RegisterEvent("PLAYERBANKSLOTS_CHANGED");
-- self:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED");
self:RegisterEvent("MERCHANT_SHOW", "OnMerchantShow");
-- self:RegisterEvent("MERCHANT_UPDATE");
self:RegisterEvent("MERCHANT_CLOSED", "OnMerchantClose");
self:RegisterEvent("BAG_UPDATE", "OnBagUpdate");
-- self:RegisterEvent("TRAINER_CLOSED");
-- self:RegisterEvent("PLAYER_REGEN_DISABLED");
-- self:RegisterEvent("PLAYER_REGEN_ENABLED", "PlayerRegenEnabled");
-- self:RegisterEvent("AUCTION_HOUSE_CLOSED");
-- self:RegisterEvent("AUCTION_HOUSE_SHOW");
self:RegisterEvent("CRAFT_SHOW", "OnCraftShow");
self:RegisterEvent("CRAFT_CLOSE", "OnCraftClose");
-- self:RegisterEvent("PLAYER_LOGOUT");
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()
self:debug("OnTradeShow enter");
TradeSkillFrame_Update(); -- seems to fix the early bailout of trade skill iterations
-- update our known skills
self:debug("OnTradeShow: update known skills");
if not Cauldron.updatingSkills then
-- Cauldron:info("Requesting skill update on trade show");
Cauldron.updatingSkills = Cauldron:NeedsSkillUpdate();
Cauldron:UpdateSkills();
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
self:debug("OnTradeShow: show the UI");
-- Cauldron.needsRedraw = true;
self:Frame_Show();
self:debug("OnTradeShow exit");
end
function Cauldron:OnTradeUpdate()
self:debug("OnTradeUpdate enter");
-- TODO
self:debug("OnTradeUpdate exit");
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:OnTradeItemChanged()
Cauldron:info("trade item changed");
-- Cauldron:UpdateSkillList();
end
--]]
function Cauldron:OnSkillUpdate(event)
Cauldron:info("skill update: event="..tostring(event));
--[[
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();
-- CauldronAmountInputBox:SetNumber(GetTradeskillRepeatCount());
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;
--[==[
-- check if the item acquired is in the intermediate list or shopping list
local items = Cauldron:GetItemDeltas(bagid);
self:debug("items="..tostring(items));
local recalc = false;
for item, itemCount in pairs(items) do
if itemCount > 0 then
-- adjust shopping list
local amtRemoved = CauldronShopping:RemoveFromList(self.db.realm.shopping, self.vars.playername, item, itemCount);
if amtRemoved > 0 then
local str = string.format("%s: %s: %d", L["Shopping list"], item, amtRemoved);
UIErrorsFrame:AddMessage(str, 1.0, 0.5, 0.25, 86, 3);
end
-- adjust intermediate list
self:debug("OnBagUpdate: adjust intermediate list");
local intItem = CauldronQueue:GetIntermediateItem(queue, item);
if intItem then
-- the item is found in the intermediate list, so recalculate the queue
self:debug("OnBagUpdate: set recalc flag");
recalc = true;
local counts = Cauldron:ReagentCount(item);
local amount = math.min(counts.has, intItem.amount);
local str = string.format("%s: %s: %d, %d %s", L["Intermediate"], item, amount, intItem.amount, L["remaining"]);
if (counts.has - itemCount) < intItem.amount then
UIErrorsFrame:AddMessage(str, 0.0, 0.9, 0.4, 86, 3);
end
else
local rItem = CauldronQueue:GetReagentItem(queue, item);
if rItem then
local counts = Cauldron:ReagentCount(item);
local amount = math.min(counts.has, rItem.amount);
local str = string.format("%s: %s: %d/%d", L["Reagent"], item, amount, rItem.amount);
if amount <= rItem.amount then
UIErrorsFrame:AddMessage(str, 0.0, 0.9, 0.0, 86, 3);
end
end
end
end
end
if recalc then
self:debug("OnBagUpdate: recalculating queue");
CauldronQueue:CalculateAllRequiredItems(queue);
Cauldron:UpdateQueue();
end
-- check if we were making something, and then update the queue
--[[
if self.makingItem then
--@alpha@
self:debug("OnBagUpdate: self.makingItem="..self.makingItem);
--@end-alpha@
local count = GetItemCount(self.makingItem);
--@alpha@
self:debug("OnBagUpdate: count="..count);
self:debug("OnBagUpdate: self.itemCurrentCount="..self.itemCurrentCount);
--@end-alpha@
if count ~= self.itemCurrentCount then
local delta = self.itemCurrentCount - count; -- TODO: is this necessary?
--@alpha@
self:debug("OnBagUpdate: delta="..delta);
--@end-alpha@
CauldronQueue:AdjustItemCount(Cauldron:GetQueue(), self.queueInfo.name, -1);
self.itemCurrentCount = count;
end
else
--
end
--]]
-- Cauldron:UpdateSkills();
self:Frame_Update();
Cauldron:UpdateShoppingList();
-- update the bags
Cauldron:ScanBags();
--]==]
Cauldron:UpdateSkillList();
end
function Cauldron:OnCraftShow()
-- TODO
end
function Cauldron:OnCraftClose()
-- TODO
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:info("spell start - unit: "..tostring(unit).."; spell: "..tostring(spell).."; rank: "..tostring(rank));
self.processing = true;
end
function Cauldron:OnSpellcastStop(event, unit, spell, rank)
-- self:info("spell stop - unit: "..tostring(unit).."; spell: "..tostring(spell).."; rank: "..tostring(rank));
self.processing = false;
end
function Cauldron:OnSpellcastSucceed(event, unit, spell, rank)
self:debug("spell succeed - unit: "..tostring(unit).."; spell: "..tostring(spell).."; rank: "..tostring(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
--[[
-- 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)
-- self:info("spell interrupt - unit: "..tostring(unit).."; spell: "..tostring(spell).."; rank: "..tostring(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
-- self:info("No amount to queue for "..skillInfo.name..".");
end
end
end
function Cauldron:QueueTradeSkillItem()
local skillInfo = Cauldron:GetSelectedSkill();
if skillInfo then
local amount = TradeSkillInputBox: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
CauldronAmountInputBox:ClearFocus();
local skillInfo = Cauldron:GetSelectedSkill();
CauldronAmountInputBox:SetNumber(skillInfo.available);
DoTradeSkill(skillInfo.index, skillInfo.available);
end
end
function Cauldron:CreateTradeSkillItem()
if ( (not PartialPlayTime()) and (not NoPlayTime()) ) then
CauldronAmountInputBox:ClearFocus();
local skillInfo = Cauldron:GetSelectedSkill();
local amount = CauldronAmountInputBox:GetNumber();
DoTradeSkill(skillInfo.index, amount);
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);
self:debug("ProcessQueue: queue="..#queue);
local queueInfo = nil;
local skillInfo = nil;
if #queue > 0 then
self:debug("ProcessQueue: checking first main queue item to see if it can be made now");
-- 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);
self:debug("ProcessQueue: intQueue="..#intQueue);
if #intQueue > 0 then
self:debug("ProcessQueue: processing intermediate queue items");
queueInfo = intQueue[1];
self:debug("ProcessQueue: queueInfo="..queueInfo.name);
skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name);
self:debug("ProcessQueue: skillInfo="..tostring(skillInfo));
else
if #queue > 0 then
self:debug("ProcessQueue: processing main queue items");
queueInfo = queue[1];
self:debug("ProcessQueue: queueInfo="..queueInfo.name);
skillInfo = Cauldron:GetSkillInfo(queueInfo.tradeskill, queueInfo.name);
self:debug("ProcessQueue: skillInfo="..tostring(skillInfo));
end
end
self:SubmitItemToProcess(queueInfo, skillInfo);
end
function Cauldron:SubmitItemToProcess(queueInfo, skillInfo, amount)
if queueInfo and skillInfo then
self:debug("ProcessQueue: queueInfo="..queueInfo.name);
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
self:debug("ProcessQueue: process item: "..queueInfo.name);
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;
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
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
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;
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