From 73d6e16472f8eaa335776e1de49a2b8f8038dfda Mon Sep 17 00:00:00 2001 From: "James D. Callahan III" Date: Fri, 9 Jul 2010 18:14:43 -0400 Subject: [PATCH] Renamed ARL.lua to core.lua --- ARL.lua | 2188 --------------------------------------------------- AckisRecipeList.toc | 8 +- core.lua | 2188 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2194 insertions(+), 2190 deletions(-) delete mode 100644 ARL.lua create mode 100644 core.lua diff --git a/ARL.lua b/ARL.lua deleted file mode 100644 index 8522c39..0000000 --- a/ARL.lua +++ /dev/null @@ -1,2188 +0,0 @@ ---[[ -************************************************************************ -ARL.lua -************************************************************************ -File date: @file-date-iso@ -File hash: @file-abbreviated-hash@ -Project hash: @project-abbreviated-hash@ -Project version: @project-version@ -************************************************************************ -Authors: Ackis, Zhinjio, Jim-Bim, Torhal, Pompachomp -************************************************************************ -Please see http://www.wowace.com/addons/arl/ for more information. -************************************************************************ -This source code is released under All Rights Reserved. -************************************************************************ -**AckisRecipeList** provides an interface for scanning professions for missing recipes. -There are a set of functions which allow you make use of the ARL database outside of ARL. -ARL supports all professions currently in World of Warcraft 3.3.2 -@class file -@name ARL.lua -@release 1.0 -************************************************************************ -]] - -------------------------------------------------------------------------------- --- Localized Lua globals. -------------------------------------------------------------------------------- -local _G = getfenv(0) - -local tostring = _G.tostring -local tonumber = _G.tonumber - -local pairs, ipairs = _G.pairs, _G.ipairs -local select = _G.select - -local table = _G.table -local twipe = table.wipe -local tconcat = table.concat -local tinsert = table.insert - -local bit = _G.bit - -local string = _G.string -local strformat = string.format -local strfind = string.find -local strmatch = string.match -local strlower = string.lower - -------------------------------------------------------------------------------- --- Localized Blizzard API. -------------------------------------------------------------------------------- -local GetNumTradeSkills = _G.GetNumTradeSkills -local GetSpellInfo = _G.GetSpellInfo - -------------------------------------------------------------------------------- --- AddOn namespace. -------------------------------------------------------------------------------- -local LibStub = _G.LibStub -local MODNAME = "Ackis Recipe List" -local addon = LibStub("AceAddon-3.0"):NewAddon(MODNAME, "AceConsole-3.0", "AceEvent-3.0") -_G.AckisRecipeList = addon - ---@alpha@ -_G.ARL = addon ---@end-alpha@ - -local L = LibStub("AceLocale-3.0"):GetLocale(MODNAME) -local BFAC = LibStub("LibBabble-Faction-3.0"):GetLookupTable() -local BZ = LibStub("LibBabble-Zone-3.0"):GetLookupTable() - -local debugger = _G.tekDebug and _G.tekDebug:GetFrame(MODNAME) - ------------------------------------------------------------------------------- --- Constants. ------------------------------------------------------------------------------- -local PROFESSION_INITS = {} -- Professions initialization functions. - ------------------------------------------------------------------------------- --- Database tables ------------------------------------------------------------------------------- -local AllSpecialtiesTable = {} -local SpecialtyTable - --- Set up the private intra-file namespace. -local private = select(2, ...) - -private.build_num = select(2, GetBuildInfo()) - -private.custom_list = {} -private.mob_list = {} -private.quest_list = {} -private.recipe_list = {} -private.reputation_list = {} -private.trainer_list = {} -private.seasonal_list = {} -private.vendor_list = {} -private.location_list = {} -private.acquire_list = {} - --- Filter flags and acquire types - defined in Constants.lua -local F = private.filter_flags -local A = private.acquire_types - ------------------------------------------------------------------------------- --- Data which is stored regarding a players statistics (luadoc copied from Collectinator, needs updating) ------------------------------------------------------------------------------- --- @class table --- @name Player --- @field known_filtered Total number of items known filtered during the scan. --- @field Faction Player's faction --- @field Class Player's class --- @field ["Reputation"] Listing of players reputation levels -local Player = {} -private.Player = Player - --- Global Frame Variables -addon.optionsFrame = {} - -------------------------------------------------------------------------------- --- Check to see if we have mandatory libraries loaded. If not, notify the user --- which are missing and return. -------------------------------------------------------------------------------- -local MissingLibraries -do - local REQUIRED_LIBS = { - "AceLocale-3.0", - "LibBabble-Boss-3.0", - "LibBabble-Faction-3.0", - "LibBabble-Zone-3.0", - } - function MissingLibraries() - local missing = false - - for idx, lib in ipairs(REQUIRED_LIBS) do - if not LibStub:GetLibrary(lib, true) then - missing = true - addon:Print(strformat(L["MISSING_LIBRARY"], lib)) - end - end - return missing - end -end -- do - -if MissingLibraries() then - --@debug@ - addon:Print("You are using a development version of ARL. As per WowAce standards, externals are not set up. You will have to install all necessary libraries in order for the addon to function correctly.") - --@end-debug@ - _G.AckisRecipeList = nil - return -end - -function addon:Debug(...) - if debugger then - debugger:AddMessage(string.format(...)) - else - --@debug@ - self:Printf(...) - --@end-debug@ - end -end - -do - local output = {} - - function addon:DumpMembers(match) - twipe(output) - tinsert(output, "Addon Object members.\n") - - local count = 0 - - for key, value in pairs(self) do - local val_type = type(value) - - if not match or val_type == match then - tinsert(output, key.. " ("..val_type..")") - count = count + 1 - end - end - tinsert(output, string.format("\n%d found\n", count)) - self:DisplayTextDump(nil, nil, tconcat(output, "\n")) - end -end -- do - -------------------------------------------------------------------------------- --- Initialization functions -------------------------------------------------------------------------------- -function addon:OnInitialize() - -- Set default options, which are to include everything in the scan - local defaults = { - global = { - -- Saving alts tradeskills (needs to be global so all profiles can access it) - tradeskill = {}, - }, - profile = { - ------------------------------------------------------------------------------- - -- Frame options - ------------------------------------------------------------------------------- - frameopts = { - offsetx = 0, - offsety = 0, - anchorTo = "", - anchorFrom = "", - uiscale = 1, - small_list_font = true, - }, - - ------------------------------------------------------------------------------- - -- Tooltip Options - ------------------------------------------------------------------------------- - tooltip = { - scale = 1, - acquire_fontsize = 11, - }, - ------------------------------------------------------------------------------- - -- Sorting Options - ------------------------------------------------------------------------------- - sorting = "Ascending", - current_tab = 3, -- Name tab - skill_view = false, -- Sort the recipes by skill level instead of name? - - ------------------------------------------------------------------------------- - -- Display Options - ------------------------------------------------------------------------------- - includefiltered = false, - includeexcluded = false, - closeguionskillclose = false, - ignoreexclusionlist = false, - scanbuttonlocation = "TR", - spelltooltiplocation = "Right", - acquiretooltiplocation = "Right", - recipes_in_tooltips = true, - max_recipes_in_tooltips = 10, - hide_tooltip_hint = false, - hidepopup = false, - minimap = true, - worldmap = true, - autoscanmap = false, - scantrainers = false, - scanvendors = false, - autoloaddb = false, - maptrainer = false, - mapvendor = true, - mapmob = true, - mapquest = true, - - ------------------------------------------------------------------------------- - -- Text Dump Options - ------------------------------------------------------------------------------- - textdumpformat = "BBCode", - - ------------------------------------------------------------------------------- - -- Recipe Exclusion - ------------------------------------------------------------------------------- - exclusionlist = {}, - - ------------------------------------------------------------------------------- - -- Filter Options - ------------------------------------------------------------------------------- - filters = { - ------------------------------------------------------------------------------- - -- General Filters - ------------------------------------------------------------------------------- - general = { - faction = true, - specialty = false, - skill = true, - known = false, - unknown = true, - retired = false, - }, - ------------------------------------------------------------------------------- - -- Obtain Filters - ------------------------------------------------------------------------------- - obtain = { - trainer = true, - vendor = true, - instance = true, - raid = true, - seasonal = true, - quest = true, - pvp = true, - discovery = true, - worlddrop = true, - mobdrop = true, - expansion0 = true, - expansion1 = true, - expansion2 = true, - }, - ------------------------------------------------------------------------------- - -- Item Filters (Armor/Weapon) - ------------------------------------------------------------------------------- - item = { - armor = { - cloth = true, - leather = true, - mail = true, - plate = true, - trinket = true, - cloak = true, - ring = true, - necklace = true, - shield = true, - }, - weapon = { - onehand = true, - twohand = true, - axe = true, - sword = true, - mace = true, - polearm = true, - dagger = true, - fist = true, - staff = true, - wand = true, - thrown = true, - bow = true, - crossbow = true, - ammo = true, - gun = true, - }, - }, - ------------------------------------------------------------------------------- - -- Quality Filters - ------------------------------------------------------------------------------- - quality = { - common = true, - uncommon = true, - rare = true, - epic = true, - }, - ------------------------------------------------------------------------------- - -- Binding Filters - ------------------------------------------------------------------------------- - binding = { - itemboe = true, - itembop = true, - recipebop = true, - recipeboe = true, - }, - ------------------------------------------------------------------------------- - -- Player Role Filters - ------------------------------------------------------------------------------- - player = { - melee = true, - tank = true, - healer = true, - caster = true, - }, - ------------------------------------------------------------------------------- - -- Reputation Filters - ------------------------------------------------------------------------------- - rep = { - aldor = true, - scryer = true, - argentdawn = true, - ashtonguedeathsworn = true, - cenarioncircle = true, - cenarionexpedition = true, - consortium = true, - hellfire = true, - keepersoftime = true, - nagrand = true, - lowercity = true, - scaleofthesands = true, - shatar = true, - shatteredsun = true, - sporeggar = true, - thoriumbrotherhood = true, - timbermaw = true, - violeteye = true, - zandalar = true, - argentcrusade = true, - frenzyheart = true, - ebonblade = true, - kirintor = true, - sonsofhodir = true, - kaluak = true, - oracles = true, - wyrmrest = true, - wrathcommon1 = true, - wrathcommon2 = true, - wrathcommon3 = true, - wrathcommon4 = true, - wrathcommon5 = true, - ashenverdict = true, - }, - ------------------------------------------------------------------------------- - -- Class Filters - ------------------------------------------------------------------------------- - classes = { - deathknight = true, - druid = true, - hunter = true, - mage = true, - paladin = true, - priest = true, - rogue = true, - shaman = true, - warlock = true, - warrior = true, - }, - } - } - } - self.db = LibStub("AceDB-3.0"):New("ARLDB2", defaults) - - if not self.db then - self:Print("Error: Database not loaded correctly. Please exit out of WoW and delete the ARL database file (AckisRecipeList.lua) found in: \\World of Warcraft\\WTF\\Account\\>\\SavedVariables\\") - return - end - local version = GetAddOnMetadata("AckisRecipeList", "Version") - self.version = (version == "@project-version@") and "Devel" or version - - self:SetupOptions() - - -- Register slash commands - self:RegisterChatCommand("arl", "ChatCommand") - self:RegisterChatCommand("ackisrecipelist", "ChatCommand") - - ------------------------------------------------------------------------------- - -- Create the scan button - ------------------------------------------------------------------------------- - local scan_button = CreateFrame("Button", nil, UIParent, "UIPanelButtonTemplate") - scan_button:SetHeight(20) - - scan_button:RegisterForClicks("LeftButtonUp") - scan_button:SetScript("OnClick", - function(self, button, down) - local cur_profession = GetTradeSkillLine() - local prev_profession = addon.Frame.prof_name or private.ordered_professions[addon.Frame.profession] - - local shift_key = _G.IsShiftKeyDown() - local alt_key = _G.IsAltKeyDown() - local ctrl_key = _G.IsControlKeyDown() - - if shift_key and not alt_key and not ctrl_key then - addon:Scan(true) - elseif not shift_key and alt_key and not ctrl_key then - addon:ClearWaypoints() - elseif not shift_key and not alt_key and not ctrl_key then - if addon.Frame:IsVisible() and prev_profession == cur_profession then - addon.Frame:Hide() - else - addon:Scan(false) - addon:AddWaypoint() - end - end - end) - - scan_button:SetScript("OnEnter", - function(this) - GameTooltip_SetDefaultAnchor(GameTooltip, this) - GameTooltip:SetText(L["SCAN_RECIPES_DESC"]) - GameTooltip:Show() - end) - scan_button:SetScript("OnLeave", function() GameTooltip:Hide() end) - scan_button:SetText(L["Scan"]) - - self.scan_button = scan_button - - ------------------------------------------------------------------------------- - -- Populate the profession initialization functions. - ------------------------------------------------------------------------------- - PROFESSION_INITS[GetSpellInfo(51304)] = addon.InitAlchemy - PROFESSION_INITS[GetSpellInfo(51300)] = addon.InitBlacksmithing - PROFESSION_INITS[GetSpellInfo(51296)] = addon.InitCooking - PROFESSION_INITS[GetSpellInfo(51313)] = addon.InitEnchanting - PROFESSION_INITS[GetSpellInfo(51306)] = addon.InitEngineering - PROFESSION_INITS[GetSpellInfo(45542)] = addon.InitFirstAid - PROFESSION_INITS[GetSpellInfo(51302)] = addon.InitLeatherworking - PROFESSION_INITS[GetSpellInfo(32606)] = addon.InitSmelting - PROFESSION_INITS[GetSpellInfo(51309)] = addon.InitTailoring - PROFESSION_INITS[GetSpellInfo(51311)] = addon.InitJewelcrafting - PROFESSION_INITS[GetSpellInfo(45363)] = addon.InitInscription - PROFESSION_INITS[private.runeforging_name] = addon.InitRuneforging - - ------------------------------------------------------------------------------- - -- Hook GameTooltip so we can show information on mobs that drop/sell/train - ------------------------------------------------------------------------------- - GameTooltip:HookScript("OnTooltipSetUnit", - function(self) - if not addon.db.profile.recipes_in_tooltips then - return - end - local name, unit = self:GetUnit() - - if not unit then - return - end - local guid = UnitGUID(unit) - - if not guid then - return - end - local GUID = tonumber(string.sub(guid, 8, 12), 16) - local unit = private.mob_list[GUID] or private.vendor_list[GUID] or private.trainer_list[GUID] - - if not unit or not unit.item_list then - return - end - local recipe_list = private.recipe_list - local shifted = _G.IsShiftKeyDown() - local count = 0 - - for spell_id in pairs(unit.item_list) do - local recipe = recipe_list[spell_id] - local recipe_prof = GetSpellInfo(recipe.profession) - local scanned = Player.has_scanned[recipe_prof] - - if scanned then - local skill_level = Player.professions[recipe_prof] - local has_level = skill_level and (type(skill_level) == "boolean" and true or skill_level >= recipe.skill_level) - - if ((not recipe:HasState("KNOWN") and has_level) or shifted) and Player:HasRecipeFaction(recipe) then - local _, _, _, hex = GetItemQualityColor(recipe.quality) - - self:AddLine(string.format("%s: %s%s|r (%d)", recipe.profession, hex, recipe.name, recipe.skill_level)) - count = count + 1 - end - end - - if count >= addon.db.profile.max_recipes_in_tooltips then - break - end - end - end) -end - ----Function run when the addon is enabled. Registers events and pre-loads certain variables. -function addon:OnEnable() - self:RegisterEvent("TRADE_SKILL_SHOW") -- Make addon respond to the tradeskill windows being shown - self:RegisterEvent("TRADE_SKILL_CLOSE") -- Addon responds to tradeskill windows being closed. - self:RegisterEvent("TRADE_SKILL_UPDATE") - - if addon.db.profile.scantrainers then - self:RegisterEvent("TRAINER_SHOW") - end - - if addon.db.profile.scanvendors then - self:RegisterEvent("MERCHANT_SHOW") - end - - ------------------------------------------------------------------------------- - -- Set the parent and scripts for addon.scan_button. - ------------------------------------------------------------------------------- - local scan_button = self.scan_button - - if Skillet and Skillet:IsActive() then - scan_button:SetParent(SkilletFrame) - Skillet:AddButtonToTradeskillWindow(scan_button) - scan_button:SetWidth(80) - elseif MRTUIUtils_RegisterWindowOnShow then - MRTUIUtils_RegisterWindowOnShow(function() - scan_button:SetParent(MRTSkillFrame) - scan_button:ClearAllPoints() - scan_button:SetPoint("RIGHT", MRTSkillFrameCloseButton, "LEFT", 4, 0) - scan_button:SetWidth(scan_button:GetTextWidth() + 10) - scan_button:Show() - end) - elseif ATSWFrame then - scan_button:SetParent(ATSWFrame) - scan_button:ClearAllPoints() - - if TradeJunkieMain and TJ_OpenButtonATSW then - scan_button:SetPoint("RIGHT", TJ_OpenButtonATSW, "LEFT", 0, 0) - else - scan_button:SetPoint("RIGHT", ATSWOptionsButton, "LEFT", 0, 0) - end - scan_button:SetHeight(ATSWOptionsButton:GetHeight()) - scan_button:SetWidth(ATSWOptionsButton:GetWidth()) - elseif CauldronFrame then - scan_button:SetParent(CauldronFrame) - scan_button:ClearAllPoints() - scan_button:SetPoint("TOP", CauldronFrame, "TOPRIGHT", -58, -52) - scan_button:SetWidth(90) - end - - local buttonparent = scan_button:GetParent() - local framelevel = buttonparent:GetFrameLevel() - local framestrata = buttonparent:GetFrameStrata() - - -- Set the frame level of the button to be 1 deeper than its parent - scan_button:SetFrameLevel(framelevel + 1) - scan_button:SetFrameStrata(framestrata) - scan_button:Enable() - - -- Add an option so that ARL will work with Manufac - if Manufac then - Manufac.options.args.ARLScan = { - type = 'execute', - name = L["Scan"], - desc = L["SCAN_RECIPES_DESC"], - func = function() addon:Scan(false) end, - order = 550, - } - end - ---[[ - -- If we're using Skillet, use Skillet's API to work with getting tradeskills - if (Skillet) and (Skillet.GetNumTradeSkills) and - (Skillet.GetTradeSkillLine) and (Skillet.GetTradeSkillInfo) and - (Skillet.GetTradeSkillRecipeLink) and (Skillet.ExpandTradeSkillSubClass) then - self:Print("Enabling Skillet advanced features.") - GetNumTradeSkills = function(...) return Skillet:GetNumTradeSkills(...) end - GetTradeSkillLine = function(...) return Skillet:GetTradeSkillLine(...) end - GetTradeSkillInfo = function(...) return Skillet:GetTradeSkillInfo(...) end - GetTradeSkillRecipeLink = function(...) return Skillet:GetTradeSkillRecipeLink(...) end - ExpandTradeSkillSubClass = function(...) return Skillet:ExpandTradeSkillSubClass(...) end - end -]]-- - ------------------------------------------------------------------------------- - -- Initialize the player's data. - ------------------------------------------------------------------------------- - do - Player.faction = UnitFactionGroup("player") - Player["Class"] = select(2, UnitClass("player")) - - ------------------------------------------------------------------------------- - -- Get the player's professions. - ------------------------------------------------------------------------------- - Player.professions = { - [GetSpellInfo(51304)] = false, -- Alchemy - [GetSpellInfo(51300)] = false, -- Blacksmithing - [GetSpellInfo(51296)] = false, -- Cooking - [GetSpellInfo(51313)] = false, -- Enchanting - [GetSpellInfo(51306)] = false, -- Engineering - [GetSpellInfo(45542)] = false, -- First Aid - [GetSpellInfo(51302)] = false, -- Leatherworking - [GetSpellInfo(2656)] = false, -- Smelting - [GetSpellInfo(51309)] = false, -- Tailoring - [GetSpellInfo(51311)] = false, -- Jewelcrafting - [GetSpellInfo(45363)] = false, -- Inscription - [private.runeforging_name] = false, -- Runeforging - } - Player:SetProfessions() - - ------------------------------------------------------------------------------- - -- Set the scanned state for all professions to false. - ------------------------------------------------------------------------------- - Player.has_scanned = {} - - for profession in pairs(Player.professions) do - Player.has_scanned[profession] = false - end - - end -- do - - ------------------------------------------------------------------------------- - -- Initialize the SpecialtyTable and AllSpecialtiesTable. - ------------------------------------------------------------------------------- - do - local AlchemySpec = { - [GetSpellInfo(28674)] = 28674, - [GetSpellInfo(28678)] = 28678, - [GetSpellInfo(28676)] = 28676, - } - - local BlacksmithSpec = { - [GetSpellInfo(9788)] = 9788, -- Armorsmith - [GetSpellInfo(17041)] = 17041, -- Master Axesmith - [GetSpellInfo(17040)] = 17040, -- Master Hammersmith - [GetSpellInfo(17039)] = 17039, -- Master Swordsmith - [GetSpellInfo(9787)] = 9787, -- Weaponsmith - } - - local EngineeringSpec = { - [GetSpellInfo(20219)] = 20219, -- Gnomish - [GetSpellInfo(20222)] = 20222, -- Goblin - } - - local LeatherworkSpec = { - [GetSpellInfo(10657)] = 10657, -- Dragonscale - [GetSpellInfo(10659)] = 10659, -- Elemental - [GetSpellInfo(10661)] = 10661, -- Tribal - } - - local TailorSpec = { - [GetSpellInfo(26797)] = 26797, -- Spellfire - [GetSpellInfo(26801)] = 26801, -- Shadoweave - [GetSpellInfo(26798)] = 26798, -- Primal Mooncloth - } - - SpecialtyTable = { - [GetSpellInfo(51304)] = AlchemySpec, - [GetSpellInfo(51300)] = BlacksmithSpec, - [GetSpellInfo(51306)] = EngineeringSpec, - [GetSpellInfo(51302)] = LeatherworkSpec, - [GetSpellInfo(51309)] = TailorSpec, - } - - -- Populate the Specialty table with all Specialties, adding alchemy even though no recipes have alchemy filters - for i in pairs(AlchemySpec) do AllSpecialtiesTable[i] = true end - for i in pairs(BlacksmithSpec) do AllSpecialtiesTable[i] = true end - for i in pairs(EngineeringSpec) do AllSpecialtiesTable[i] = true end - for i in pairs(LeatherworkSpec) do AllSpecialtiesTable[i] = true end - for i in pairs(TailorSpec) do AllSpecialtiesTable[i] = true end - end -- do -end - ----Run when the addon is disabled. Ace3 takes care of unregistering events, etc. -function addon:OnDisable() - addon.Frame:Hide() - - -- Remove the option from Manufac - if Manufac then - Manufac.options.args.ARLScan = nil - end -end - -------------------------------------------------------------------------------- --- Event handling functions -------------------------------------------------------------------------------- - ----Event used for datamining when a trainer is shown. -function addon:TRAINER_SHOW() - self:ScanSkillLevelData(true) - self:ScanTrainerData(true) -end - ----Event used for datamining when a vendor is shown. -function addon:MERCHANT_SHOW() - addon:ScanVendor() -end - -do - local GetTradeSkillListLink = _G.GetTradeSkillListLink - local UnitName = _G.UnitName - local GetRealmName = _G.GetRealmName - local IsTradeSkillLinked = _G.IsTradeSkillLinked - - function addon:TRADE_SKILL_SHOW() - -- If this is our own skill, save it, if not don't save it - if not IsTradeSkillLinked() then - local tradelink = GetTradeSkillListLink() - - if tradelink then - local pname = UnitName("player") - local prealm = GetRealmName() - local tradename = GetTradeSkillLine() - - -- Actual alt information saved here. -Torhal - addon.db.global.tradeskill = addon.db.global.tradeskill or {} - addon.db.global.tradeskill[prealm] = addon.db.global.tradeskill[prealm] or {} - addon.db.global.tradeskill[prealm][pname] = addon.db.global.tradeskill[prealm][pname] or {} - - addon.db.global.tradeskill[prealm][pname][tradename] = tradelink - end - end - local scan_button = self.scan_button - local scan_parent = scan_button:GetParent() - - if not scan_parent or scan_parent == UIParent then - scan_button:SetParent(TradeSkillFrame) - scan_parent = scan_button:GetParent() - end - - if scan_parent == TradeSkillFrame then - scan_button:ClearAllPoints() - - local loc = addon.db.profile.scanbuttonlocation - - if loc == "TR" then - scan_button:SetPoint("RIGHT", TradeSkillFrameCloseButton, "LEFT",4,0) - elseif loc == "TL" then - scan_button:SetPoint("LEFT", TradeSkillFramePortrait, "RIGHT",2,12) - elseif loc == "BR" then - scan_button:SetPoint("TOP", TradeSkillCancelButton, "BOTTOM",0,-5) - elseif loc == "BL" then - scan_button:SetPoint("TOP", TradeSkillCreateAllButton, "BOTTOM",0,-5) - end - scan_button:SetWidth(scan_button:GetTextWidth() + 10) - end - scan_button:Show() - end -end - -function addon:TRADE_SKILL_CLOSE() - if addon.db.profile.closeguionskillclose then - self.Frame:Hide() - end - - if not Skillet then - addon.scan_button:Hide() - end -end - -do - local last_update = 0 - local updater = CreateFrame("Frame", nil, UIParent) - - updater:Hide() - updater:SetScript("OnUpdate", - function(self, elapsed) - last_update = last_update + elapsed - - if last_update >= 0.5 then - local profession = GetTradeSkillLine() - - if profession ~= "UNKNOWN" then - addon:Scan(false, true) - end - self:Hide() - end - end) - - function addon:TRADE_SKILL_UPDATE(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) - if not self.Frame:IsVisible() then - return - end - - if not updater:IsVisible() then - last_update = 0 - updater:Show() - end - end -end - -------------------------------------------------------------------------------- --- Tradeskill functions --- Recipe DB Structures are defined in Documentation.lua -------------------------------------------------------------------------------- - -do - local SF = private.recipe_state_flags - - ------------------------------------------------------------------------------- - -- Recipe member functions for bit flags. - ------------------------------------------------------------------------------- - local function Recipe_HasState(self, state_name) - return self.state and (bit.band(self.state, SF[state_name]) == SF[state_name]) or false - end - - local function Recipe_AddState(self, state_name) - if not self.state then - self.state = 0 - end - - if bit.band(self.state, SF[state_name]) == SF[state_name] then - return - end - self.state = bit.bxor(self.state, SF[state_name]) - end - - local function Recipe_RemoveState(self, state_name) - if not self.state then - return - end - - if bit.band(self.state, SF[state_name]) ~= SF[state_name] then - return - end - self.state = bit.bxor(self.state, SF[state_name]) - - if self.state == 0 then - self.state = nil - end - end - - local BITFIELD_MAP = { - ["common1"] = private.common_flags_word1, - ["class1"] = private.class_flags_word1, - ["reputation1"] = private.rep_flags_word1, - ["reputation2"] = private.rep_flags_word2, - ["item1"] = private.item_flags_word1, - } - - local function Recipe_IsFlagged(self, field_name, flag_name) - local bitfield = self.flags[field_name] - local bitset = BITFIELD_MAP[field_name] - local value = bitset[flag_name] - - return bitfield and (bit.band(bitfield, value) == value) or false - end - - --- Adds a tradeskill recipe into the specified recipe database - -- @name AckisRecipeList:AddRecipe - -- @usage AckisRecipeList:AddRecipe(28927, 305, 23109, Q.UNCOMMON, V.TBC, 305, 305, 325, 345) - -- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe being added to the database - -- @param skill_level The skill level at which the recipe can be initially learned - -- @param item_id The [[http://www.wowwiki.com/ItemLink|Item ID]] that is created by the recipe, or nil - -- @param quality The quality/rarity of the recipe - -- @param profession The profession ID that uses the recipe. See [[API/database-documentation]] for a listing of profession IDs - -- @param specialty The specialty that uses the recipe (ie: goblin engineering) or nil or blank - -- @param genesis Game version that the recipe was first introduced in, for example, Original, BC, or WoTLK - -- @param optimal_level Level at which recipe is considered orange - -- @param medium_level Level at which recipe is considered yellow - -- @param easy_level Level at which recipe is considered green - -- @param trivial_level Level at which recipe is considered grey - -- @return None, array is passed as a reference - function addon:AddRecipe(spell_id, skill_level, item_id, quality, profession, specialty, genesis, optimal_level, medium_level, easy_level, trivial_level) - local recipe_list = private.recipe_list - - if recipe_list[spell_id] then - --@alpha@ - self:Print("Duplicate recipe: "..recipe_list[spell_id].profession.." "..tostring(spell_id).." "..recipe_list[spell_id].name) - --@end-alpha@ - return - end - - local recipe = { - ["spell_id"] = spell_id, - ["skill_level"] = skill_level, - ["item_id"] = item_id, - ["quality"] = quality, - ["profession"] = GetSpellInfo(profession), - ["name"] = GetSpellInfo(spell_id), - ["flags"] = {}, - ["acquire_data"] = {}, - ["specialty"] = specialty, -- Assumption: there will only be 1 speciality for a trade skill - ["genesis"] = private.game_version_names[genesis], - ["optimal_level"] = optimal_level or skill_level, - ["medium_level"] = medium_level or skill_level + 10, - ["easy_level"] = easy_level or skill_level + 15, - ["trivial_level"] = trivial_level or skill_level + 20, - - -- Function members - ["HasState"] = Recipe_HasState, - ["AddState"] = Recipe_AddState, - ["RemoveState"] = Recipe_RemoveState, - ["IsFlagged"] = Recipe_IsFlagged, - } - - if not recipe.name then - self:Print(strformat(L["SpellIDCache"], spell_id)) - end - recipe_list[spell_id] = recipe - end -end -- do - --- Public API function for retrieving specific information about a recipe. --- @name AckisRecipeList:GetRecipeData --- @usage AckisRecipeList:GetRecipeData(28972, "profession") --- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe being queried. --- @param data Which member of the recipe table is being queried. --- @return Variable, depending upon which member of the recipe table is queried. -function addon:GetRecipeData(spell_id, data) - local recipe = private.recipe_list[spell_id] - return recipe and recipe[data] or nil -end - ---- Adds filtering flags to a specific tradeskill. --- @name AckisRecipeList:AddRecipeFlags --- @usage AckisRecipeList:AddRecipeFlags(28927, F.ALLIANCE, F.VENDOR, F.IBOE, F.RBOP, F.HEALER, F.CASTER, F.ALDOR) --- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe which the filter flags are being added to --- @param ... A listing of filtering flags. See [[API/database-documentation]] for a listing of filter flags --- @return None, array is passed as a reference. -function addon:AddRecipeFlags(spell_id, ...) - local num_flags = select('#',...) - local recipe = private.recipe_list[spell_id] - - for index = 1, num_flags, 1 do - local flag = select(index, ...) - local flag_name = private.filter_strings[flag] - - local bitfield - local member_name - - for table_index, bits in ipairs(private.bit_flags) do - if bits[flag_name] then - bitfield = bits - member_name = private.flag_members[table_index] - end - end - - if not bitfield or not member_name then - return - end - - if not recipe.flags[member_name] then - recipe.flags[member_name] = 0 - end - recipe.flags[member_name] = bit.bxor(recipe.flags[member_name], bitfield[flag_name]) - end -end - ---- Adds acquire methods to a specific tradeskill. --- @name AckisRecipeList:AddRecipeAcquire --- @usage AckisRecipeList:AddRecipeAcquire(28927, A.REPUTATION, FAC.ALDOR, REP.HONORED, 19321) --- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe which acquire methods are being added to --- @param ... A listing of acquire methods. See [[API/database-documentation]] for a listing of acquire methods and how they work --- @return None, array is passed as a reference. -do - local location_list = private.location_list - local acquire_list = private.acquire_list - - function addon:AddRecipeAcquire(spell_id, ...) - local numvars = select('#', ...) -- Find out how many flags we're adding - local i = 1 -- Index for which variables we're parsing through - local recipe_list = private.recipe_list - local acquire_data = recipe_list[spell_id].acquire_data - - while i <= numvars do - local location, affiliation - - local acquire_type, acquire_id = select(i, ...) - i = i + 2 - - if not acquire_type then - self:Debug("Spell ID: %d has no acquire type.", spell_id) - else - acquire_data[acquire_type] = acquire_data[acquire_type] or {} - - local acquire = acquire_data[acquire_type] - - if not acquire_id then - self:Debug("Spell ID %d: %s ID is nil.", spell_id, private.acquire_strings[acquire_type]) - else - if acquire_type == A.TRAINER then - local trainer_list = private.trainer_list - local trainer = trainer_list[acquire_id] - - if not trainer then - self:Debug("Spell ID "..spell_id..": TrainerID "..acquire_id.." does not exist in the database.") - else - acquire[acquire_id] = true - - affiliation = trainer.faction - location = trainer.location - - trainer.item_list = trainer.item_list or {} - trainer.item_list[spell_id] = true - end - elseif acquire_type == A.VENDOR then - local vendor_list = private.vendor_list - local vendor = vendor_list[acquire_id] - - if not vendor then - self:Debug("Spell ID "..spell_id..": VendorID "..acquire_id.." does not exist in the database.") - else - acquire[acquire_id] = true - - affiliation = vendor.faction - location = vendor.location - - vendor.item_list = vendor.item_list or {} - vendor.item_list[spell_id] = true - end - elseif acquire_type == A.MOB_DROP then - local mob_list = private.mob_list - local mob = mob_list[acquire_id] - - if not mob then - self:Debug("Spell ID "..spell_id..": Mob ID "..acquire_id.." does not exist in the database.") - else - acquire[acquire_id] = true - - affiliation = mob.faction - location = mob.location - - mob_list[acquire_id].item_list = mob_list[acquire_id].item_list or {} - mob_list[acquire_id].item_list[spell_id] = true - end - elseif acquire_type == A.QUEST then - local quest_list = private.quest_list - local quest = quest_list[acquire_id] - - if not quest then - self:Debug("Spell ID "..spell_id..": Quest ID "..acquire_id.." does not exist in the database.") - else - acquire[acquire_id] = true - - affiliation = quest.faction - location = quest.location - end - elseif acquire_type == A.REPUTATION then - local vendor_list = private.vendor_list - local rep_level, vendor_id = select(i, ...) - i = i + 2 - - if not private.reputation_list[acquire_id] then - self:Debug("Spell ID "..spell_id..": ReputationID "..acquire_id.." does not exist in the database.") - else - if not vendor_id then - self:Debug("Spell ID "..spell_id..": Reputation Vendor ID is nil.") - elseif not vendor_list[vendor_id] then - self:Debug("Spell ID "..spell_id..": Reputation Vendor ID "..vendor_id.." does not exist in the database.") - else - acquire[acquire_id] = acquire[acquire_id] or {} - - local faction = acquire[acquire_id] - faction[rep_level] = faction[rep_level] or {} - faction[rep_level][vendor_id] = true - - local rep_vendor = vendor_list[vendor_id] - - affiliation = rep_vendor.faction - location = rep_vendor.location - - rep_vendor.item_list = rep_vendor.item_list or {} - rep_vendor.item_list[spell_id] = true - end - end - elseif acquire_type == A.WORLD_DROP then - acquire[acquire_id] = true - location = type(acquire_id) == "string" and BZ[acquire_id] or nil - - if location then - affiliation = "world_drop" - else - addon:Debug("WORLD_DROP with no location: %d %s", spell_id, recipe_list[spell_id].name) - end - elseif acquire_type == A.SEASONAL then - acquire[acquire_id] = true - elseif acquire_type == A.CUSTOM then - acquire[acquire_id] = true - location = private.custom_list[acquire_id].location - else - -- Unhandled acquire_type - acquire[acquire_id] = true - location = private.acquire_strings[acquire_type] or _G.UNKNOWN - end - end -- acquire_id - acquire_list[acquire_type] = acquire_list[acquire_type] or {} - acquire_list[acquire_type].recipes = acquire_list[acquire_type].recipes or {} - - acquire_list[acquire_type].name = private.acquire_names[acquire_type] - acquire_list[acquire_type].recipes[spell_id] = affiliation or true - end -- acquire_type - - if location then - location_list[location] = location_list[location] or {} - location_list[location].recipes = location_list[location].recipes or {} - - location_list[location].name = location - location_list[location].recipes[spell_id] = affiliation or true - end - end -- while - end - - local function GenericAddRecipeAcquire(spell_id, acquire_type, type_string, unit_list, ...) - local num_vars = select('#', ...) - local cur_var = 1 - local recipe = private.recipe_list[spell_id] - - local acquire_data = recipe.acquire_data - acquire_data[acquire_type] = acquire_data[acquire_type] or {} - - local acquire = acquire_data[acquire_type] - - while cur_var <= num_vars do - local location, affiliation - local id_num = select(cur_var, ...) - cur_var = cur_var + 1 - - -- A quantity of true means unlimited - normal vendor item. - local quantity = true - - if type_string == "Limited Vendor" then - quantity = select(cur_var, ...) - cur_var = cur_var + 1 - end - acquire[id_num] = true - - if unit_list and not unit_list[id_num] then - addon:Debug("Spell ID %d: %s ID %d does not exist in the database.", spell_id, type_string, id_num) - else - if not unit_list then - location = type(id_num) == "string" and BZ[id_num] or nil - - if location then - affiliation = "world_drop" - else - addon:Debug("WORLD_DROP with no location: %d %s", spell_id, private.recipe_list[spell_id].name) - end - else - local unit = unit_list[id_num] - - affiliation = unit.faction - location = unit.location - - unit.item_list = unit.item_list or {} - unit.item_list[spell_id] = quantity - end - end - acquire_list[acquire_type] = acquire_list[acquire_type] or {} - acquire_list[acquire_type].recipes = acquire_list[acquire_type].recipes or {} - - acquire_list[acquire_type].name = private.acquire_names[acquire_type] - acquire_list[acquire_type].recipes[spell_id] = affiliation or true - - if location then - location_list[location] = location_list[location] or {} - location_list[location].recipes = location_list[location].recipes or {} - - location_list[location].name = location - location_list[location].recipes[spell_id] = affiliation or true - end - end - end - - function addon:AddRecipeMobDrop(spell_id, ...) - GenericAddRecipeAcquire(spell_id, A.MOB_DROP, "Mob", private.mob_list, ...) - end - - function addon:AddRecipeTrainer(spell_id, ...) - GenericAddRecipeAcquire(spell_id, A.TRAINER, "Trainer", private.trainer_list, ...) - end - - function addon:AddRecipeVendor(spell_id, ...) - GenericAddRecipeAcquire(spell_id, A.VENDOR, "Vendor", private.vendor_list, ...) - end - - function addon:AddRecipeLimitedVendor(spell_id, ...) - GenericAddRecipeAcquire(spell_id, A.VENDOR, "Limited Vendor", private.vendor_list, ...) - end - - function addon:AddRecipeWorldDrop(spell_id, ...) - GenericAddRecipeAcquire(spell_id, A.WORLD_DROP, nil, nil, ...) - end - - function addon:AddRecipeQuest(spell_id, ...) - GenericAddRecipeAcquire(spell_id, A.QUEST, "Quest", private.quest_list, ...) - end - - -- This function can NOT use GenericAddRecipeAcquire() - reputation vendors are more complicated than the other acquire types. - function addon:AddRecipeRepVendor(spell_id, faction_id, rep_level, ...) - local num_vars = select('#', ...) - local cur_var = 1 - - local recipe = private.recipe_list[spell_id] - local vendor_list = private.vendor_list - - local acquire_data = recipe.acquire_data - acquire_data[A.REPUTATION] = acquire_data[A.REPUTATION] or {} - - local acquire = acquire_data[A.REPUTATION] - acquire[faction_id] = acquire[faction_id] or {} - - local faction = acquire[faction_id] - faction[rep_level] = faction[rep_level] or {} - - while cur_var <= num_vars do - local location, affiliation - local vendor_id = select(cur_var, ...) - cur_var = cur_var + 1 - - if not private.reputation_list[faction_id] then - --@alpha@ - self:Printf("Spell ID %d: Faction ID %d does not exist in the database.", spell_id, faction_id) - --@end-alpha@ - else - if not vendor_id then - --@alpha@ - self:Print("Spell ID "..spell_id..": Reputation Vendor ID is nil.") - --@end-alpha@ - elseif not vendor_list[vendor_id] then - --@alpha@ - self:Print("Spell ID "..spell_id..": Reputation Vendor ID "..vendor_id.." does not exist in the database.") - --@end-alpha@ - else - faction[rep_level][vendor_id] = true - - local rep_vendor = vendor_list[vendor_id] - - affiliation = rep_vendor.faction - location = rep_vendor.location - - rep_vendor.item_list = rep_vendor.item_list or {} - rep_vendor.item_list[spell_id] = true - end - end - acquire_list[A.REPUTATION] = acquire_list[A.REPUTATION] or {} - acquire_list[A.REPUTATION].recipes = acquire_list[A.REPUTATION].recipes or {} - - acquire_list[A.REPUTATION].name = private.acquire_names[A.REPUTATION] - acquire_list[A.REPUTATION].recipes[spell_id] = affiliation or true - - if location then - location_list[location] = location_list[location] or {} - location_list[location].recipes = location_list[location].recipes or {} - - location_list[location].name = location - location_list[location].recipes[spell_id] = affiliation or true - end - end - end -end -- do block - ---- Adds an item to a specific database listing (ie: vendor, mob, etc) --- @name AckisRecipeList:addLookupList --- @usage AckisRecipeList:addLookupList(DB,NPC ID, NPC Name, NPC Location, X Coord, Y Coord, Faction) --- @param DB Database which the entry will be stored --- @param ID Unique identified for the entry --- @param name Name of the entry --- @param location Location of the entry in the world --- @param coord_x X coordinate of where the entry is found --- @param coord_y Y coordinate of where the entry is found --- @param faction Faction identifier for the entry --- @return None, array is passed as a reference ---For individual database structures, see Documentation.lua -do - local FACTION_NAMES = { - [1] = BFAC["Neutral"], - [2] = BFAC["Alliance"], - [3] = BFAC["Horde"] - } - function addon:addLookupList(DB, ID, name, location, coord_x, coord_y, faction) - if DB[ID] then - self:Debug("Duplicate lookup: %d - %s.", ID, name) - return - end - - DB[ID] = { - ["name"] = name, - ["location"] = location, - ["faction"] = faction and FACTION_NAMES[faction + 1] or FACTION_NAMES[1] - } - - if coord_x and coord_y then - DB[ID]["coord_x"] = coord_x - DB[ID]["coord_y"] = coord_y - end - - --@alpha@ - if not location and DB ~= private.custom_list then - self:Debug("Lookup ID: %d (%s) has an unknown location.", ID, DB[ID].name or _G.UNKNOWN) - end - - if faction and DB == private.mob_list then - self:Debug("Mob %d (%s) has been assigned to faction %s.", ID, name, DB[ID].faction) - end - --@end-alpha@ - end -end -- do - -------------------------------------------------------------------------------- --- Filter flag functions -------------------------------------------------------------------------------- -do - local COMMON1 = private.common_flags_word1 - local CLASS1 = private.class_flags_word1 - local REP1 = private.rep_flags_word1 - local REP2 = private.rep_flags_word2 - local ITEM1 = private.item_flags_word1 - - -- HardFilterFlags and SoftFilterFlags are used to determine if a recipe should be shown based on the value of the key compared to the value of its saved_var. - -- Its keys and values are populated the first time CanDisplayRecipe() is called. - local HardFilterFlags, SoftFilterFlags, RepFilterFlags, RepFilterFlags2 - - local ClassFilterFlags = { - ["deathknight"] = CLASS1.DK, - ["druid"] = CLASS1.DRUID, - ["hunter"] = CLASS1.HUNTER, - ["mage"] = CLASS1.MAGE, - ["paladin"] = CLASS1.PALADIN, - ["priest"] = CLASS1.PRIEST, - ["shaman"] = CLASS1.SHAMAN, - ["rogue"] = CLASS1.ROGUE, - ["warlock"] = CLASS1.WARLOCK, - ["warrior"] = CLASS1.WARRIOR, - } - - ---Scans a specific recipe to determine if it is to be displayed or not. - -- For flag info see comments at start of file in comments - local function CanDisplayRecipe(recipe) - if addon.db.profile.exclusionlist[recipe.spell_id] and not addon.db.profile.ignoreexclusionlist then - return false - end - local filter_db = addon.db.profile.filters - local general_filters = filter_db.general - - -- See Documentation file for logic explanation - ------------------------------------------------------------------------------- - -- Stage 1 - -- Loop through exclusive flags (hard filters) - -- If one of these does not pass we do not display the recipe - -- So to be more efficient we'll just leave this function if there's a false - ------------------------------------------------------------------------------- - - -- Display both horde and alliance factions? - if not general_filters.faction and not Player:HasRecipeFaction(recipe) then - return false - end - - -- Display all skill levels? - if not general_filters.skill and recipe.skill_level > Player["ProfessionLevel"] then - return false - end - - -- Display all specialities? - if not general_filters.specialty then - local specialty = recipe.specialty - - if specialty and specialty ~= Player["Specialty"] then - return false - end - end - - -- Display retired recipes? - if not general_filters.retired and bit.band(recipe.flags.common1, COMMON1.RETIRED) == COMMON1.RETIRED then - return false - end - local obtain_filters = filter_db.obtain - local game_version = private.game_versions[recipe.genesis] - local V = private.game_versions - - -- Filter out game recipes - if not obtain_filters.expansion0 and game_version == V.ORIG then - return false - end - - if not obtain_filters.expansion1 and game_version == V.TBC then - return false - end - - if not obtain_filters.expansion2 and game_version == V.WOTLK then - return false - end - local quality_filters = filter_db.quality - local recipe_quality = recipe.quality - local Q = private.item_qualities - - -- Filter out certain recipe quality types. - if not quality_filters.common and recipe_quality == Q.COMMON then - return false - end - - if not quality_filters.uncommon and recipe_quality == Q.UNCOMMON then - return false - end - - if not quality_filters.rare and recipe_quality == Q.RARE then - return false - end - - if not quality_filters.epic and recipe_quality == Q.EPIC then - return false - end - - ------------------------------------------------------------------------------- - -- Check the hard filter flags - ------------------------------------------------------------------------------- - if not HardFilterFlags then - local binding_filters = filter_db.binding - local player_filters = filter_db.player - local armor_filters = filter_db.item.armor - local weapon_filters = filter_db.item.weapon - - HardFilterFlags = { - ------------------------------------------------------------------------------------------------ - -- Binding flags. - ------------------------------------------------------------------------------------------------ - ["itemboe"] = { flag = COMMON1.IBOE, index = 1, sv_root = binding_filters }, - ["itembop"] = { flag = COMMON1.IBOP, index = 1, sv_root = binding_filters }, - ["itemboa"] = { flag = COMMON1.IBOA, index = 1, sv_root = binding_filters }, - ["recipeboe"] = { flag = COMMON1.RBOE, index = 1, sv_root = binding_filters }, - ["recipebop"] = { flag = COMMON1.RBOP, index = 1, sv_root = binding_filters }, - ["recipeboa"] = { flag = COMMON1.RBOA, index = 1, sv_root = binding_filters }, - ------------------------------------------------------------------------------------------------ - -- Player Type flags. - ------------------------------------------------------------------------------------------------ - ["melee"] = { flag = COMMON1.DPS, index = 1, sv_root = player_filters }, - ["tank"] = { flag = COMMON1.TANK, index = 1, sv_root = player_filters }, - ["healer"] = { flag = COMMON1.HEALER, index = 1, sv_root = player_filters }, - ["caster"] = { flag = COMMON1.CASTER, index = 1, sv_root = player_filters }, - ------------------------------------------------------------------------------------------------ - -- Armor flags. - ------------------------------------------------------------------------------------------------ - ["cloth"] = { flag = ITEM1.CLOTH, index = 5, sv_root = armor_filters }, - ["leather"] = { flag = ITEM1.LEATHER, index = 5, sv_root = armor_filters }, - ["mail"] = { flag = ITEM1.MAIL, index = 5, sv_root = armor_filters }, - ["plate"] = { flag = ITEM1.PLATE, index = 5, sv_root = armor_filters }, - ["trinket"] = { flag = ITEM1.TRINKET, index = 5, sv_root = armor_filters }, - ["cloak"] = { flag = ITEM1.CLOAK, index = 5, sv_root = armor_filters }, - ["ring"] = { flag = ITEM1.RING, index = 5, sv_root = armor_filters }, - ["necklace"] = { flag = ITEM1.NECK, index = 5, sv_root = armor_filters }, - ["shield"] = { flag = ITEM1.SHIELD, index = 5, sv_root = armor_filters }, - ------------------------------------------------------------------------------------------------ - -- Weapon flags. - ------------------------------------------------------------------------------------------------ - ["onehand"] = { flag = ITEM1.ONE_HAND, index = 5, sv_root = weapon_filters }, - ["twohand"] = { flag = ITEM1.TWO_HAND, index = 5, sv_root = weapon_filters }, - ["axe"] = { flag = ITEM1.AXE, index = 5, sv_root = weapon_filters }, - ["sword"] = { flag = ITEM1.SWORD, index = 5, sv_root = weapon_filters }, - ["mace"] = { flag = ITEM1.MACE, index = 5, sv_root = weapon_filters }, - ["polearm"] = { flag = ITEM1.POLEARM, index = 5, sv_root = weapon_filters }, - ["dagger"] = { flag = ITEM1.DAGGER, index = 5, sv_root = weapon_filters }, - ["fist"] = { flag = ITEM1.FIST, index = 5, sv_root = weapon_filters }, - ["gun"] = { flag = ITEM1.GUN, index = 5, sv_root = weapon_filters }, - ["staff"] = { flag = ITEM1.STAFF, index = 5, sv_root = weapon_filters }, - ["wand"] = { flag = ITEM1.WAND, index = 5, sv_root = weapon_filters }, - ["thrown"] = { flag = ITEM1.THROWN, index = 5, sv_root = weapon_filters }, - ["bow"] = { flag = ITEM1.BOW, index = 5, sv_root = weapon_filters }, - ["crossbow"] = { flag = ITEM1.XBOW, index = 5, sv_root = weapon_filters }, - ["ammo"] = { flag = ITEM1.AMMO, index = 5, sv_root = weapon_filters }, - } - end - - for filter, data in pairs(HardFilterFlags) do - local bitfield = recipe.flags[private.flag_members[data.index]] - - if bitfield and bit.band(bitfield, data.flag) == data.flag and not data.sv_root[filter] then - return false - end - end - - ------------------------------------------------------------------------------- - -- Check the reputation filter flags - ------------------------------------------------------------------------------- - if not RepFilterFlags then - RepFilterFlags = { - [REP1.ARGENTDAWN] = "argentdawn", - [REP1.CENARION_CIRCLE] = "cenarioncircle", - [REP1.THORIUM_BROTHERHOOD] = "thoriumbrotherhood", - [REP1.TIMBERMAW_HOLD] = "timbermaw", - [REP1.ZANDALAR] = "zandalar", - [REP1.ALDOR] = "aldor", - [REP1.ASHTONGUE] = "ashtonguedeathsworn", - [REP1.CENARION_EXPEDITION] = "cenarionexpedition", - [REP1.HELLFIRE] = "hellfire", - [REP1.CONSORTIUM] = "consortium", - [REP1.KOT] = "keepersoftime", - [REP1.LOWERCITY] = "lowercity", - [REP1.NAGRAND] = "nagrand", - [REP1.SCALE_SANDS] = "scaleofthesands", - [REP1.SCRYER] = "scryer", - [REP1.SHATAR] = "shatar", - [REP1.SHATTEREDSUN] = "shatteredsun", - [REP1.SPOREGGAR] = "sporeggar", - [REP1.VIOLETEYE] = "violeteye", - [REP1.ARGENTCRUSADE] = "argentcrusade", - [REP1.FRENZYHEART] = "frenzyheart", - [REP1.EBONBLADE] = "ebonblade", - [REP1.KIRINTOR] = "kirintor", - [REP1.HODIR] = "sonsofhodir", - [REP1.KALUAK] = "kaluak", - [REP1.ORACLES] = "oracles", - [REP1.WYRMREST] = "wyrmrest", - [REP1.WRATHCOMMON1] = "wrathcommon1", - [REP1.WRATHCOMMON2] = "wrathcommon2", - [REP1.WRATHCOMMON3] = "wrathcommon3", - [REP1.WRATHCOMMON4] = "wrathcommon4", - [REP1.WRATHCOMMON5] = "wrathcommon5", - } - end - - if not RepFilterFlags2 then - RepFilterFlags2 = { - [REP2.ASHEN_VERDICT] = "ashenverdict", - } - end - -- Now we check to see if _all_ of the pertinent reputation or class flags are toggled off. If even one is toggled on, we still show the recipe. - local toggled_off, toggled_on = 0, 0 - - for flag, name in pairs(RepFilterFlags) do - local bitfield = recipe.flags.reputation1 - - if bitfield and bit.band(bitfield, flag) == flag then - if filter_db.rep[name] then - toggled_on = toggled_on + 1 - else - toggled_off = toggled_off + 1 - end - end - end - - if toggled_off > 0 and toggled_on == 0 then - return false - end - - toggled_off, toggled_on = 0, 0 - - for flag, name in pairs(RepFilterFlags2) do - local bitfield = recipe.flags.reputation2 - - if bitfield and bit.band(bitfield, flag) == flag then - if filter_db.rep[name] then - toggled_on = toggled_on + 1 - else - toggled_off = toggled_off + 1 - end - end - end - - if toggled_off > 0 and toggled_on == 0 then - return false - end - - ------------------------------------------------------------------------------- - -- Check the class filter flags - ------------------------------------------------------------------------------- - local class_filters = filter_db.classes - - toggled_off, toggled_on = 0, 0 - - for class, flag in pairs(ClassFilterFlags) do - local bitfield = recipe.flags.class1 - - if bitfield and bit.band(bitfield, flag) == flag then - if class_filters[class] then - toggled_on = toggled_on + 1 - else - toggled_off = toggled_off + 1 - end - end - end - - if toggled_off > 0 and toggled_on == 0 then - return false - end - - ------------------------------------------------------------------------------------------------ - -- Stage 2 - -- loop through nonexclusive (soft filters) flags until one is true - -- If one of these is true (ie: we want to see trainers and there is a trainer flag) we display the recipe - ------------------------------------------------------------------------------------------------ - if not SoftFilterFlags then - SoftFilterFlags = { - ["trainer"] = { flag = COMMON1.TRAINER, index = 1, sv_root = obtain_filters }, - ["vendor"] = { flag = COMMON1.VENDOR, index = 1, sv_root = obtain_filters }, - ["instance"] = { flag = COMMON1.INSTANCE, index = 1, sv_root = obtain_filters }, - ["raid"] = { flag = COMMON1.RAID, index = 1, sv_root = obtain_filters }, - ["seasonal"] = { flag = COMMON1.SEASONAL, index = 1, sv_root = obtain_filters }, - ["quest"] = { flag = COMMON1.QUEST, index = 1, sv_root = obtain_filters }, - ["pvp"] = { flag = COMMON1.PVP, index = 1, sv_root = obtain_filters }, - ["worlddrop"] = { flag = COMMON1.WORLD_DROP, index = 1, sv_root = obtain_filters }, - ["mobdrop"] = { flag = COMMON1.MOB_DROP, index = 1, sv_root = obtain_filters }, - ["discovery"] = { flag = COMMON1.DISC, index = 1, sv_root = obtain_filters }, - } - end - - for filter, data in pairs(SoftFilterFlags) do - local bitfield = recipe.flags[private.flag_members[data.index]] - - if bitfield and bit.band(bitfield, data.flag) == data.flag and data.sv_root[filter] then - return true - end - end - - -- If we get here it means that no flags matched our values - return false - end - - ---Scans the recipe listing and updates the filters according to user preferences - function addon:UpdateFilters(is_linked) - local general_filters = addon.db.profile.filters.general - local recipes_total = 0 - local recipes_known = 0 - local recipes_total_filtered = 0 - local recipes_known_filtered = 0 - local can_display = false - local current_profession = self.Frame.prof_name or private.ordered_professions[self.Frame.profession] - local recipe_list = private.recipe_list - local SF = private.recipe_state_flags - - for recipe_id, recipe in pairs(recipe_list) do - recipe:RemoveState("VISIBLE") - - if recipe.profession == current_profession then - local is_known - - if is_linked then - is_known = recipe:HasState("LINKED") - else - is_known = recipe:HasState("KNOWN") - end - - can_display = CanDisplayRecipe(recipe) - recipes_total = recipes_total + 1 - recipes_known = recipes_known + (is_known and 1 or 0) - - if can_display then - recipes_total_filtered = recipes_total_filtered + 1 - recipes_known_filtered = recipes_known_filtered + (is_known and 1 or 0) - - if not general_filters.known and is_known then - can_display = false - end - - if not general_filters.unknown and not is_known then - can_display = false - end - end - else - can_display = false - end - - if can_display then - recipe:AddState("VISIBLE") - end - end - Player.recipes_total = recipes_total - Player.recipes_known = recipes_known - Player.recipes_total_filtered = recipes_total_filtered - Player.recipes_known_filtered = recipes_known_filtered - end - -end -- do - -------------------------------------------------------------------------------- --- ARL Logic Functions -------------------------------------------------------------------------------- -function addon:InitializeProfession(profession) - if not profession then - --@alpha@ - addon:Print("nil profession passed to InitializeProfession()") - --@end-alpha@ - return - end - - if profession == private.professions["Smelting"] then - profession = private.mining_name - end - local func = PROFESSION_INITS[profession] - - if func then - return func(addon) - else - addon:Print(L["UnknownTradeSkill"]:format(profession)) - return 0 - end -end - --- Determines what to do when the slash command is called. -function addon:ChatCommand(input) - - -- Open About panel if there's no parameters or if we do /arl about - if not input or (input and input:trim() == "") or input == strlower(L["Sorting"]) or input == strlower(L["Sort"]) or input == strlower(_G.DISPLAY) then - InterfaceOptionsFrame_OpenToCategory(self.optionsFrame) - elseif (input == strlower(L["About"])) then - if (self.optionsFrame["About"]) then - InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["About"]) - else - InterfaceOptionsFrame_OpenToCategory(self.optionsFrame) - end - elseif (input == strlower(L["Profile"])) then - InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["Profiles"]) - elseif (input == strlower(_G.FILTER)) then - InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["Filters"]) - elseif (input == strlower(L["Documentation"])) then - InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["Documentation"]) - elseif (input == strlower(L["Scan"])) then - self:Scan(false) - elseif (input == strlower("scandata")) then - self:ScanSkillLevelData() - elseif (input == strlower("scanprof")) then - self:ScanProfession("all") - elseif (input == strlower("tradelinks")) then - self:GenerateLinks() - else - -- What happens when we get here? - LibStub("AceConfigCmd-3.0"):HandleCommand("arl", "Ackis Recipe List", input) - end - -end - --- Public API function to initialize all of the lookup lists - self-nils once run. --- @name AckisRecipeList:InitializeLookups() --- @usage if AckisRecipeList.InitializeLookups then AckisRecipeList:InitializeLookups() end -function addon:InitializeLookups() - self:InitCustom(private.custom_list) - self:InitMob(private.mob_list) - self:InitQuest(private.quest_list) - self:InitReputation(private.reputation_list) - self:InitTrainer(private.trainer_list) - self:InitSeasons(private.seasonal_list) - self:InitVendor(private.vendor_list) - - self.InitializeLookups = nil -end - -------------------------------------------------------------------------------- --- Recipe Scanning Functions -------------------------------------------------------------------------------- -do - -- List of tradeskill headers, used in addon:Scan() - local header_list = {} - - --- Causes a scan of the tradeskill to be conducted. Function called when the scan button is clicked. Parses recipes and displays output - -- @name AckisRecipeList:Scan - -- @usage AckisRecipeList:Scan(true) - -- @param textdump Boolean indicating if we want the output to be a text dump, or if we want to use the ARL GUI - -- @return A frame with either the text dump, or the ARL frame - function addon:Scan(textdump, is_refresh) - local current_prof, prof_level = GetTradeSkillLine() - - -- Bail if we haven't opened a tradeskill frame. - if current_prof == "UNKNOWN" then - self:Print(L["OpenTradeSkillWindow"]) - return - end - - -- Set the current profession level, and update the cached data. - Player["ProfessionLevel"] = prof_level - - -- Make sure we're only updating a profession the character actually knows - this could be a scan from a tradeskill link. - local is_linked = IsTradeSkillLinked() - - if not is_linked and Player.professions[current_prof] then - Player.professions[current_prof] = prof_level - Player.has_scanned[current_prof] = true - end - - -- Get the current profession Specialty - local specialty = SpecialtyTable[current_prof] - - for index = 1, 25, 1 do - local spellName = GetSpellName(index, BOOKTYPE_SPELL) - - if not spellName or index == 25 then - Player["Specialty"] = nil - break - elseif specialty and specialty[spellName] then - Player["Specialty"] = specialty[spellName] - break - end - end - - if self.InitializeLookups then - self:InitializeLookups() - end - -- Add the recipes to the database - -- TODO: Figure out what this variable was supposed to be for - it isn't used anywhere. -Torhal - Player.totalRecipes = addon:InitializeProfession(current_prof) - - ------------------------------------------------------------------------------- - -- Scan all recipes and mark the ones we know - ------------------------------------------------------------------------------- - twipe(header_list) - - -- Save the state of the "Have Materials" checkbox. - local have_materials = TradeSkillFrameAvailableFilterCheckButton:GetChecked() - - if MRTUIUtils_PushFilterSelection then - MRTUIUtils_PushFilterSelection() - else - if not Skillet and TradeSkillFrameAvailableFilterCheckButton:GetChecked() then - TradeSkillFrameAvailableFilterCheckButton:SetChecked(false) - TradeSkillOnlyShowMakeable(false) - end - - -- Clear the inventory slot filter - UIDropDownMenu_Initialize(TradeSkillInvSlotDropDown, TradeSkillInvSlotDropDown_Initialize) - UIDropDownMenu_SetSelectedID(TradeSkillInvSlotDropDown, 1) - SetTradeSkillInvSlotFilter(0, 1, 1) - - -- Clear the sub-classes filters - UIDropDownMenu_Initialize(TradeSkillSubClassDropDown, TradeSkillSubClassDropDown_Initialize) - UIDropDownMenu_SetSelectedID(TradeSkillSubClassDropDown, 1) - SetTradeSkillSubClassFilter(0, 1, 1) - - -- Expand all headers so we can see all the recipes there are - for i = GetNumTradeSkills(), 1, -1 do - local name, tradeType, _, isExpanded = GetTradeSkillInfo(i) - - if tradeType == "header" and not isExpanded then - header_list[name] = true - ExpandTradeSkillSubClass(i) - end - end - end - local recipe_list = private.recipe_list - local recipes_found = 0 - local SF = private.recipe_state_flags - - for i = 1, GetNumTradeSkills() do - local tradeName, tradeType = GetTradeSkillInfo(i) - - if tradeType ~= "header" then - -- Get the trade skill link for the specified recipe - local SpellLink = GetTradeSkillRecipeLink(i) - local SpellString = strmatch(SpellLink, "^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)") - local recipe = recipe_list[tonumber(SpellString)] - - if recipe then - if not is_linked then - recipe:AddState("KNOWN") - recipe:RemoveState("LINKED") - else - recipe:AddState("LINKED") - end - recipes_found = recipes_found + 1 - else - self:Debug(tradeName .. " " .. SpellString .. L["MissingFromDB"]) - end - end - end - - -- Close all the headers we've opened - -- If Mr Trader is installed use that API - if MRTUIUtils_PopFilterSelection then - MRTUIUtils_PopFilterSelection() - else - -- Collapse all headers that were collapsed before - for i = GetNumTradeSkills(), 1, -1 do - local name, tradeType, _, isExpanded = GetTradeSkillInfo(i) - - if header_list[name] then - CollapseTradeSkillSubClass(i) - end - end - -- Restore the state of the "Have Materials" checkbox. - TradeSkillFrameAvailableFilterCheckButton:SetChecked(have_materials) - TradeSkillOnlyShowMakeable(have_materials) - end - Player.prev_count = Player.foundRecipes - Player.foundRecipes = recipes_found - - if is_refresh and Player.prev_count == recipes_found then - return - end - - ------------------------------------------------------------------------------- - -- Update the player's reputation levels. - ------------------------------------------------------------------------------- - Player["Reputation"] = Player["Reputation"] or {} - - table.wipe(header_list) - - -- Number of factions before expansion - local num_factions = GetNumFactions() - - -- Expand all the headers, storing those which were collapsed. - for i = num_factions, 1, -1 do - local name, _, _, _, _, _, _, _, _, isCollapsed = GetFactionInfo(i) - - if isCollapsed then - ExpandFactionHeader(i) - header_list[name] = true - end - end - - -- Number of factions with everything expanded - num_factions = GetNumFactions() - - -- Get the rep levels - for i = 1, num_factions, 1 do - local name, _, replevel = GetFactionInfo(i) - - -- If the rep is greater than neutral - if replevel > 4 then - -- We use levels of 0, 1, 2, 3, 4 internally for reputation levels, make it correspond here - Player["Reputation"][name] = replevel - 4 - end - end - - -- Collapse the headers again - for i = num_factions, 1, -1 do - local name = GetFactionInfo(i) - - if header_list[name] then - CollapseFactionHeader(i) - end - end - - ------------------------------------------------------------------------------- - -- Everything is ready - display the GUI or dump the list to text. - ------------------------------------------------------------------------------- - if textdump then - self:DisplayTextDump(recipe_list, current_prof) - else - self.Frame:Display(current_prof, is_linked) - end - end -end - -------------------------------------------------------------------------------- --- Text dumping functions -------------------------------------------------------------------------------- -do - ------------------------------------------------------------------------------- - -- Provides a string of comma separated values for all recipe information - ------------------------------------------------------------------------------- - local text_table = {} - local acquire_list = {} - local ACQUIRE_NAMES = private.acquire_names - - local GetFilterNames - do - local LC = _G.LOCALIZED_CLASS_NAMES_MALE - local FILTER_NAMES - - function GetFilterNames() - if not FILTER_NAMES then - local is_alliance = (Player.faction == "Alliance") - - FILTER_NAMES = { - [1] = BFAC["Alliance"], - [2] = BFAC["Horde"], - [3] = L["Trainer"], - [4] = L["Vendor"], - [5] = _G.INSTANCE, - [6] = _G.RAID, - [7] = _G.EVENTS_LABEL, - [8] = L["Quest"], - [9] = _G.PVP, - [10] = L["World Drop"], - [11] = L["Mob Drop"], - [12] = L["Discovery"], - [13] = L["Retired"], - [21] = LC["DEATHKNIGHT"], - [22] = LC["DRUID"], - [23] = LC["HUNTER"], - [24] = LC["MAGE"], - [25] = LC["PALADIN"], - [26] = LC["PRIEST"], - [27] = LC["SHAMAN"], - [28] = LC["ROGUE"], - [29] = LC["WARLOCK"], - [30] = LC["WARRIOR"], - [36] = L["BOEFilter"], - [37] = L["BOPFilter"], - [38] = L["BOAFilter"], - [40] = L["RecipeBOEFilter"], - [41] = L["RecipeBOPFilter"], - [42] = L["RecipeBOAFilter"], - [51] = _G.MELEE, - [52] = _G.TANK, - [53] = _G.HEALER, - [54] = _G.DAMAGER, - [56] = L["Cloth"], - [57] = L["Leather"], - [58] = L["Mail"], - [59] = L["Plate"], - [60] = L["Cloak"], - [61] = L["Trinket"], - [62] = L["Ring"], - [63] = L["Necklace"], - [64] = L["Shield"], - [66] = L["One Hand"], - [67] = L["Two Hand"], - [68] = L["Axe"], - [69] = L["Sword"], - [70] = L["Mace"], - [71] = L["Polearm"], - [72] = L["Dagger"], - [73] = L["Staff"], - [74] = L["Wand"], - [75] = L["Thrown"], - [76] = L["Bow"], - [77] = L["Crossbow"], - [78] = L["Ammo"], - [79] = L["Fist"], - [80] = L["Gun"], - [96] = BFAC["Argent Dawn"], - [97] = BFAC["Cenarion Circle"], - [98] = BFAC["Thorium Brotherhood"], - [99] = BFAC["Timbermaw Hold"], - [100] = BFAC["Zandalar Tribe"], - [101] = BFAC["The Aldor"], - [102] = BFAC["Ashtongue Deathsworn"], - [103] = BFAC["Cenarion Expedition"], - [104] = (is_alliance and BFAC["Honor Hold"] or BFAC["Thrallmar"]), - [105] = BFAC["The Consortium"], - [106] = BFAC["Keepers of Time"], - [107] = BFAC["Lower City"], - [108] = (is_alliance and BFAC["Kurenai"] or BFAC["The Mag'har"]), - [109] = BFAC["The Scale of the Sands"], - [110] = BFAC["The Scryers"], - [111] = BFAC["The Sha'tar"], - [112] = BFAC["Shattered Sun Offensive"], - [113] = BFAC["Sporeggar"], - [114] = BFAC["The Violet Eye"], - [115] = BFAC["Argent Crusade"], - [116] = BFAC["Frenzyheart Tribe"], - [117] = BFAC["Knights of the Ebon Blade"], - [118] = BFAC["Kirin Tor"], - [119] = BFAC["The Sons of Hodir"], - [120] = BFAC["The Kalu'ak"], - [121] = BFAC["The Oracles"], - [122] = BFAC["The Wyrmrest Accord"], - [123] = (is_alliance and BFAC["The Silver Covenant"] or BFAC["The Sunreavers"]), - [124] = (is_alliance and BFAC["Explorers' League"] or BFAC["The Hand of Vengeance"]), - [125] = (is_alliance and BFAC["Valiance Expedition"] or BFAC["Warsong Offensive"]), - [126] = (is_alliance and BFAC["The Frostborn"] or BFAC["The Taunka"]), - [127] = (is_alliance and BFAC["Alliance Vanguard"] or BFAC["Horde Expedition"]), - [128] = BFAC["The Ashen Verdict"], - } - end - return FILTER_NAMES - end - end -- do - - ---Dumps the recipe database in a format that is readable to humans. - function addon:GetTextDump(profession) - local output = addon.db.profile.textdumpformat - twipe(text_table) - - if not output or output == "Comma" then - tinsert(text_table, strformat("Ackis Recipe List Text Dump for %s's %s, in the form of Comma Separated Values.\n ", UnitName("player"), profession)) - tinsert(text_table, "Spell ID,Recipe Name,Skill Level,ARL Filter Flags,Acquire Methods,Known\n") - elseif output == "BBCode" then - tinsert(text_table, strformat("Ackis Recipe List Text Dump for %s's %s, in the form of BBCode.\n", UnitName("player"), profession)) - end - local recipe_list = private.recipe_list - local SF = private.recipe_state_flags - - for recipe_id in pairs(recipe_list) do - local recipe = recipe_list[recipe_id] - local recipe_prof = GetSpellInfo(recipe.profession) - local is_known = recipe:HasState("KNOWN") - - if recipe_prof == profession then - -- CSV - if not output or output == "Comma" then - -- Add Spell ID, Name and Skill Level to the list - tinsert(text_table, recipe_id) - tinsert(text_table, ",") - tinsert(text_table, recipe.name) - tinsert(text_table, ",") - tinsert(text_table, recipe.skill_level) - tinsert(text_table, ",\"") - -- BBCode - elseif output == "BBCode" then - -- Make the entry red - if not is_known then - tinsert(text_table, "[color=red]") - end - tinsert(text_table, "\n[b]" .. recipe_id .. "[/b] - " .. recipe.name .. " (" .. recipe.skill_level .. ")\n") - - -- Close Color tag - if not is_known then - tinsert(text_table, "[/color]\nRecipe Flags:\n[list]") - elseif is_known then - tinsert(text_table, "\nRecipe Flags:\n[list]") - end - --Name - elseif output == "Name" then - tinsert(text_table, recipe.name.."\n") - end - - -- Add in all the filter flags - local filter_names = GetFilterNames() - local prev = false - - -- Find out which flags are set - for table_index, bits in ipairs(private.bit_flags) do - for flag_name, flag in pairs(bits) do - local bitfield = recipe.flags[private.flag_members[table_index]] - - if bitfield and bit.band(bitfield, flag) == flag then - if not output or output == "Comma" then - if prev then - tinsert(text_table, ",") - end - tinsert(text_table, filter_names[private.filter_flags[flag_name]]) - prev = true - -- BBCode - elseif output == "BBCode" then - tinsert(text_table, "[*]" .. filter_names[private.filter_flags[flag_name]]) - end - end - end - end - - if not output or output == "Comma" then - tinsert(text_table, "\",\"") - elseif output == "BBCode" then - tinsert(text_table, "[/list]\nAcquire Methods:\n[list]") - end - - -- Find out which unique acquire methods we have - local acquire_data = recipe["acquire_data"] - twipe(acquire_list) - - for acquire_type in pairs(acquire_data) do - acquire_list[ACQUIRE_NAMES[acquire_type]] = true - end - - -- Add all the acquire methods in - prev = false - - for i in pairs(acquire_list) do - if not output or output == "Comma" then - if prev then - tinsert(text_table, ",") - end - tinsert(text_table, i) - prev = true - elseif output == "BBCode" then - tinsert(text_table, "[*] " .. i) - end - end - - if not output or output == "Comma" then - if is_known then - tinsert(text_table, "\",true\n") - else - tinsert(text_table, "\",false\n") - end - elseif output == "BBCode" then - tinsert(text_table, "\n[/list]") - end - end - end -- for - return tconcat(text_table, "") - end -end diff --git a/AckisRecipeList.toc b/AckisRecipeList.toc index 55d10a2..f0376d6 100644 --- a/AckisRecipeList.toc +++ b/AckisRecipeList.toc @@ -62,13 +62,17 @@ embeds.xml # Localization files locale.xml +# Constants should be loaded first - many files use them in their main chunk. Constants.lua -ARL.lua + +core.lua Config.lua Waypoint.lua -Frame.lua Scanner.lua Player.lua +# User Interface files +Frame.lua + # Database files database.xml diff --git a/core.lua b/core.lua new file mode 100644 index 0000000..8522c39 --- /dev/null +++ b/core.lua @@ -0,0 +1,2188 @@ +--[[ +************************************************************************ +ARL.lua +************************************************************************ +File date: @file-date-iso@ +File hash: @file-abbreviated-hash@ +Project hash: @project-abbreviated-hash@ +Project version: @project-version@ +************************************************************************ +Authors: Ackis, Zhinjio, Jim-Bim, Torhal, Pompachomp +************************************************************************ +Please see http://www.wowace.com/addons/arl/ for more information. +************************************************************************ +This source code is released under All Rights Reserved. +************************************************************************ +**AckisRecipeList** provides an interface for scanning professions for missing recipes. +There are a set of functions which allow you make use of the ARL database outside of ARL. +ARL supports all professions currently in World of Warcraft 3.3.2 +@class file +@name ARL.lua +@release 1.0 +************************************************************************ +]] + +------------------------------------------------------------------------------- +-- Localized Lua globals. +------------------------------------------------------------------------------- +local _G = getfenv(0) + +local tostring = _G.tostring +local tonumber = _G.tonumber + +local pairs, ipairs = _G.pairs, _G.ipairs +local select = _G.select + +local table = _G.table +local twipe = table.wipe +local tconcat = table.concat +local tinsert = table.insert + +local bit = _G.bit + +local string = _G.string +local strformat = string.format +local strfind = string.find +local strmatch = string.match +local strlower = string.lower + +------------------------------------------------------------------------------- +-- Localized Blizzard API. +------------------------------------------------------------------------------- +local GetNumTradeSkills = _G.GetNumTradeSkills +local GetSpellInfo = _G.GetSpellInfo + +------------------------------------------------------------------------------- +-- AddOn namespace. +------------------------------------------------------------------------------- +local LibStub = _G.LibStub +local MODNAME = "Ackis Recipe List" +local addon = LibStub("AceAddon-3.0"):NewAddon(MODNAME, "AceConsole-3.0", "AceEvent-3.0") +_G.AckisRecipeList = addon + +--@alpha@ +_G.ARL = addon +--@end-alpha@ + +local L = LibStub("AceLocale-3.0"):GetLocale(MODNAME) +local BFAC = LibStub("LibBabble-Faction-3.0"):GetLookupTable() +local BZ = LibStub("LibBabble-Zone-3.0"):GetLookupTable() + +local debugger = _G.tekDebug and _G.tekDebug:GetFrame(MODNAME) + +------------------------------------------------------------------------------ +-- Constants. +------------------------------------------------------------------------------ +local PROFESSION_INITS = {} -- Professions initialization functions. + +------------------------------------------------------------------------------ +-- Database tables +------------------------------------------------------------------------------ +local AllSpecialtiesTable = {} +local SpecialtyTable + +-- Set up the private intra-file namespace. +local private = select(2, ...) + +private.build_num = select(2, GetBuildInfo()) + +private.custom_list = {} +private.mob_list = {} +private.quest_list = {} +private.recipe_list = {} +private.reputation_list = {} +private.trainer_list = {} +private.seasonal_list = {} +private.vendor_list = {} +private.location_list = {} +private.acquire_list = {} + +-- Filter flags and acquire types - defined in Constants.lua +local F = private.filter_flags +local A = private.acquire_types + +------------------------------------------------------------------------------ +-- Data which is stored regarding a players statistics (luadoc copied from Collectinator, needs updating) +------------------------------------------------------------------------------ +-- @class table +-- @name Player +-- @field known_filtered Total number of items known filtered during the scan. +-- @field Faction Player's faction +-- @field Class Player's class +-- @field ["Reputation"] Listing of players reputation levels +local Player = {} +private.Player = Player + +-- Global Frame Variables +addon.optionsFrame = {} + +------------------------------------------------------------------------------- +-- Check to see if we have mandatory libraries loaded. If not, notify the user +-- which are missing and return. +------------------------------------------------------------------------------- +local MissingLibraries +do + local REQUIRED_LIBS = { + "AceLocale-3.0", + "LibBabble-Boss-3.0", + "LibBabble-Faction-3.0", + "LibBabble-Zone-3.0", + } + function MissingLibraries() + local missing = false + + for idx, lib in ipairs(REQUIRED_LIBS) do + if not LibStub:GetLibrary(lib, true) then + missing = true + addon:Print(strformat(L["MISSING_LIBRARY"], lib)) + end + end + return missing + end +end -- do + +if MissingLibraries() then + --@debug@ + addon:Print("You are using a development version of ARL. As per WowAce standards, externals are not set up. You will have to install all necessary libraries in order for the addon to function correctly.") + --@end-debug@ + _G.AckisRecipeList = nil + return +end + +function addon:Debug(...) + if debugger then + debugger:AddMessage(string.format(...)) + else + --@debug@ + self:Printf(...) + --@end-debug@ + end +end + +do + local output = {} + + function addon:DumpMembers(match) + twipe(output) + tinsert(output, "Addon Object members.\n") + + local count = 0 + + for key, value in pairs(self) do + local val_type = type(value) + + if not match or val_type == match then + tinsert(output, key.. " ("..val_type..")") + count = count + 1 + end + end + tinsert(output, string.format("\n%d found\n", count)) + self:DisplayTextDump(nil, nil, tconcat(output, "\n")) + end +end -- do + +------------------------------------------------------------------------------- +-- Initialization functions +------------------------------------------------------------------------------- +function addon:OnInitialize() + -- Set default options, which are to include everything in the scan + local defaults = { + global = { + -- Saving alts tradeskills (needs to be global so all profiles can access it) + tradeskill = {}, + }, + profile = { + ------------------------------------------------------------------------------- + -- Frame options + ------------------------------------------------------------------------------- + frameopts = { + offsetx = 0, + offsety = 0, + anchorTo = "", + anchorFrom = "", + uiscale = 1, + small_list_font = true, + }, + + ------------------------------------------------------------------------------- + -- Tooltip Options + ------------------------------------------------------------------------------- + tooltip = { + scale = 1, + acquire_fontsize = 11, + }, + ------------------------------------------------------------------------------- + -- Sorting Options + ------------------------------------------------------------------------------- + sorting = "Ascending", + current_tab = 3, -- Name tab + skill_view = false, -- Sort the recipes by skill level instead of name? + + ------------------------------------------------------------------------------- + -- Display Options + ------------------------------------------------------------------------------- + includefiltered = false, + includeexcluded = false, + closeguionskillclose = false, + ignoreexclusionlist = false, + scanbuttonlocation = "TR", + spelltooltiplocation = "Right", + acquiretooltiplocation = "Right", + recipes_in_tooltips = true, + max_recipes_in_tooltips = 10, + hide_tooltip_hint = false, + hidepopup = false, + minimap = true, + worldmap = true, + autoscanmap = false, + scantrainers = false, + scanvendors = false, + autoloaddb = false, + maptrainer = false, + mapvendor = true, + mapmob = true, + mapquest = true, + + ------------------------------------------------------------------------------- + -- Text Dump Options + ------------------------------------------------------------------------------- + textdumpformat = "BBCode", + + ------------------------------------------------------------------------------- + -- Recipe Exclusion + ------------------------------------------------------------------------------- + exclusionlist = {}, + + ------------------------------------------------------------------------------- + -- Filter Options + ------------------------------------------------------------------------------- + filters = { + ------------------------------------------------------------------------------- + -- General Filters + ------------------------------------------------------------------------------- + general = { + faction = true, + specialty = false, + skill = true, + known = false, + unknown = true, + retired = false, + }, + ------------------------------------------------------------------------------- + -- Obtain Filters + ------------------------------------------------------------------------------- + obtain = { + trainer = true, + vendor = true, + instance = true, + raid = true, + seasonal = true, + quest = true, + pvp = true, + discovery = true, + worlddrop = true, + mobdrop = true, + expansion0 = true, + expansion1 = true, + expansion2 = true, + }, + ------------------------------------------------------------------------------- + -- Item Filters (Armor/Weapon) + ------------------------------------------------------------------------------- + item = { + armor = { + cloth = true, + leather = true, + mail = true, + plate = true, + trinket = true, + cloak = true, + ring = true, + necklace = true, + shield = true, + }, + weapon = { + onehand = true, + twohand = true, + axe = true, + sword = true, + mace = true, + polearm = true, + dagger = true, + fist = true, + staff = true, + wand = true, + thrown = true, + bow = true, + crossbow = true, + ammo = true, + gun = true, + }, + }, + ------------------------------------------------------------------------------- + -- Quality Filters + ------------------------------------------------------------------------------- + quality = { + common = true, + uncommon = true, + rare = true, + epic = true, + }, + ------------------------------------------------------------------------------- + -- Binding Filters + ------------------------------------------------------------------------------- + binding = { + itemboe = true, + itembop = true, + recipebop = true, + recipeboe = true, + }, + ------------------------------------------------------------------------------- + -- Player Role Filters + ------------------------------------------------------------------------------- + player = { + melee = true, + tank = true, + healer = true, + caster = true, + }, + ------------------------------------------------------------------------------- + -- Reputation Filters + ------------------------------------------------------------------------------- + rep = { + aldor = true, + scryer = true, + argentdawn = true, + ashtonguedeathsworn = true, + cenarioncircle = true, + cenarionexpedition = true, + consortium = true, + hellfire = true, + keepersoftime = true, + nagrand = true, + lowercity = true, + scaleofthesands = true, + shatar = true, + shatteredsun = true, + sporeggar = true, + thoriumbrotherhood = true, + timbermaw = true, + violeteye = true, + zandalar = true, + argentcrusade = true, + frenzyheart = true, + ebonblade = true, + kirintor = true, + sonsofhodir = true, + kaluak = true, + oracles = true, + wyrmrest = true, + wrathcommon1 = true, + wrathcommon2 = true, + wrathcommon3 = true, + wrathcommon4 = true, + wrathcommon5 = true, + ashenverdict = true, + }, + ------------------------------------------------------------------------------- + -- Class Filters + ------------------------------------------------------------------------------- + classes = { + deathknight = true, + druid = true, + hunter = true, + mage = true, + paladin = true, + priest = true, + rogue = true, + shaman = true, + warlock = true, + warrior = true, + }, + } + } + } + self.db = LibStub("AceDB-3.0"):New("ARLDB2", defaults) + + if not self.db then + self:Print("Error: Database not loaded correctly. Please exit out of WoW and delete the ARL database file (AckisRecipeList.lua) found in: \\World of Warcraft\\WTF\\Account\\>\\SavedVariables\\") + return + end + local version = GetAddOnMetadata("AckisRecipeList", "Version") + self.version = (version == "@project-version@") and "Devel" or version + + self:SetupOptions() + + -- Register slash commands + self:RegisterChatCommand("arl", "ChatCommand") + self:RegisterChatCommand("ackisrecipelist", "ChatCommand") + + ------------------------------------------------------------------------------- + -- Create the scan button + ------------------------------------------------------------------------------- + local scan_button = CreateFrame("Button", nil, UIParent, "UIPanelButtonTemplate") + scan_button:SetHeight(20) + + scan_button:RegisterForClicks("LeftButtonUp") + scan_button:SetScript("OnClick", + function(self, button, down) + local cur_profession = GetTradeSkillLine() + local prev_profession = addon.Frame.prof_name or private.ordered_professions[addon.Frame.profession] + + local shift_key = _G.IsShiftKeyDown() + local alt_key = _G.IsAltKeyDown() + local ctrl_key = _G.IsControlKeyDown() + + if shift_key and not alt_key and not ctrl_key then + addon:Scan(true) + elseif not shift_key and alt_key and not ctrl_key then + addon:ClearWaypoints() + elseif not shift_key and not alt_key and not ctrl_key then + if addon.Frame:IsVisible() and prev_profession == cur_profession then + addon.Frame:Hide() + else + addon:Scan(false) + addon:AddWaypoint() + end + end + end) + + scan_button:SetScript("OnEnter", + function(this) + GameTooltip_SetDefaultAnchor(GameTooltip, this) + GameTooltip:SetText(L["SCAN_RECIPES_DESC"]) + GameTooltip:Show() + end) + scan_button:SetScript("OnLeave", function() GameTooltip:Hide() end) + scan_button:SetText(L["Scan"]) + + self.scan_button = scan_button + + ------------------------------------------------------------------------------- + -- Populate the profession initialization functions. + ------------------------------------------------------------------------------- + PROFESSION_INITS[GetSpellInfo(51304)] = addon.InitAlchemy + PROFESSION_INITS[GetSpellInfo(51300)] = addon.InitBlacksmithing + PROFESSION_INITS[GetSpellInfo(51296)] = addon.InitCooking + PROFESSION_INITS[GetSpellInfo(51313)] = addon.InitEnchanting + PROFESSION_INITS[GetSpellInfo(51306)] = addon.InitEngineering + PROFESSION_INITS[GetSpellInfo(45542)] = addon.InitFirstAid + PROFESSION_INITS[GetSpellInfo(51302)] = addon.InitLeatherworking + PROFESSION_INITS[GetSpellInfo(32606)] = addon.InitSmelting + PROFESSION_INITS[GetSpellInfo(51309)] = addon.InitTailoring + PROFESSION_INITS[GetSpellInfo(51311)] = addon.InitJewelcrafting + PROFESSION_INITS[GetSpellInfo(45363)] = addon.InitInscription + PROFESSION_INITS[private.runeforging_name] = addon.InitRuneforging + + ------------------------------------------------------------------------------- + -- Hook GameTooltip so we can show information on mobs that drop/sell/train + ------------------------------------------------------------------------------- + GameTooltip:HookScript("OnTooltipSetUnit", + function(self) + if not addon.db.profile.recipes_in_tooltips then + return + end + local name, unit = self:GetUnit() + + if not unit then + return + end + local guid = UnitGUID(unit) + + if not guid then + return + end + local GUID = tonumber(string.sub(guid, 8, 12), 16) + local unit = private.mob_list[GUID] or private.vendor_list[GUID] or private.trainer_list[GUID] + + if not unit or not unit.item_list then + return + end + local recipe_list = private.recipe_list + local shifted = _G.IsShiftKeyDown() + local count = 0 + + for spell_id in pairs(unit.item_list) do + local recipe = recipe_list[spell_id] + local recipe_prof = GetSpellInfo(recipe.profession) + local scanned = Player.has_scanned[recipe_prof] + + if scanned then + local skill_level = Player.professions[recipe_prof] + local has_level = skill_level and (type(skill_level) == "boolean" and true or skill_level >= recipe.skill_level) + + if ((not recipe:HasState("KNOWN") and has_level) or shifted) and Player:HasRecipeFaction(recipe) then + local _, _, _, hex = GetItemQualityColor(recipe.quality) + + self:AddLine(string.format("%s: %s%s|r (%d)", recipe.profession, hex, recipe.name, recipe.skill_level)) + count = count + 1 + end + end + + if count >= addon.db.profile.max_recipes_in_tooltips then + break + end + end + end) +end + +---Function run when the addon is enabled. Registers events and pre-loads certain variables. +function addon:OnEnable() + self:RegisterEvent("TRADE_SKILL_SHOW") -- Make addon respond to the tradeskill windows being shown + self:RegisterEvent("TRADE_SKILL_CLOSE") -- Addon responds to tradeskill windows being closed. + self:RegisterEvent("TRADE_SKILL_UPDATE") + + if addon.db.profile.scantrainers then + self:RegisterEvent("TRAINER_SHOW") + end + + if addon.db.profile.scanvendors then + self:RegisterEvent("MERCHANT_SHOW") + end + + ------------------------------------------------------------------------------- + -- Set the parent and scripts for addon.scan_button. + ------------------------------------------------------------------------------- + local scan_button = self.scan_button + + if Skillet and Skillet:IsActive() then + scan_button:SetParent(SkilletFrame) + Skillet:AddButtonToTradeskillWindow(scan_button) + scan_button:SetWidth(80) + elseif MRTUIUtils_RegisterWindowOnShow then + MRTUIUtils_RegisterWindowOnShow(function() + scan_button:SetParent(MRTSkillFrame) + scan_button:ClearAllPoints() + scan_button:SetPoint("RIGHT", MRTSkillFrameCloseButton, "LEFT", 4, 0) + scan_button:SetWidth(scan_button:GetTextWidth() + 10) + scan_button:Show() + end) + elseif ATSWFrame then + scan_button:SetParent(ATSWFrame) + scan_button:ClearAllPoints() + + if TradeJunkieMain and TJ_OpenButtonATSW then + scan_button:SetPoint("RIGHT", TJ_OpenButtonATSW, "LEFT", 0, 0) + else + scan_button:SetPoint("RIGHT", ATSWOptionsButton, "LEFT", 0, 0) + end + scan_button:SetHeight(ATSWOptionsButton:GetHeight()) + scan_button:SetWidth(ATSWOptionsButton:GetWidth()) + elseif CauldronFrame then + scan_button:SetParent(CauldronFrame) + scan_button:ClearAllPoints() + scan_button:SetPoint("TOP", CauldronFrame, "TOPRIGHT", -58, -52) + scan_button:SetWidth(90) + end + + local buttonparent = scan_button:GetParent() + local framelevel = buttonparent:GetFrameLevel() + local framestrata = buttonparent:GetFrameStrata() + + -- Set the frame level of the button to be 1 deeper than its parent + scan_button:SetFrameLevel(framelevel + 1) + scan_button:SetFrameStrata(framestrata) + scan_button:Enable() + + -- Add an option so that ARL will work with Manufac + if Manufac then + Manufac.options.args.ARLScan = { + type = 'execute', + name = L["Scan"], + desc = L["SCAN_RECIPES_DESC"], + func = function() addon:Scan(false) end, + order = 550, + } + end + +--[[ + -- If we're using Skillet, use Skillet's API to work with getting tradeskills + if (Skillet) and (Skillet.GetNumTradeSkills) and + (Skillet.GetTradeSkillLine) and (Skillet.GetTradeSkillInfo) and + (Skillet.GetTradeSkillRecipeLink) and (Skillet.ExpandTradeSkillSubClass) then + self:Print("Enabling Skillet advanced features.") + GetNumTradeSkills = function(...) return Skillet:GetNumTradeSkills(...) end + GetTradeSkillLine = function(...) return Skillet:GetTradeSkillLine(...) end + GetTradeSkillInfo = function(...) return Skillet:GetTradeSkillInfo(...) end + GetTradeSkillRecipeLink = function(...) return Skillet:GetTradeSkillRecipeLink(...) end + ExpandTradeSkillSubClass = function(...) return Skillet:ExpandTradeSkillSubClass(...) end + end +]]-- + ------------------------------------------------------------------------------- + -- Initialize the player's data. + ------------------------------------------------------------------------------- + do + Player.faction = UnitFactionGroup("player") + Player["Class"] = select(2, UnitClass("player")) + + ------------------------------------------------------------------------------- + -- Get the player's professions. + ------------------------------------------------------------------------------- + Player.professions = { + [GetSpellInfo(51304)] = false, -- Alchemy + [GetSpellInfo(51300)] = false, -- Blacksmithing + [GetSpellInfo(51296)] = false, -- Cooking + [GetSpellInfo(51313)] = false, -- Enchanting + [GetSpellInfo(51306)] = false, -- Engineering + [GetSpellInfo(45542)] = false, -- First Aid + [GetSpellInfo(51302)] = false, -- Leatherworking + [GetSpellInfo(2656)] = false, -- Smelting + [GetSpellInfo(51309)] = false, -- Tailoring + [GetSpellInfo(51311)] = false, -- Jewelcrafting + [GetSpellInfo(45363)] = false, -- Inscription + [private.runeforging_name] = false, -- Runeforging + } + Player:SetProfessions() + + ------------------------------------------------------------------------------- + -- Set the scanned state for all professions to false. + ------------------------------------------------------------------------------- + Player.has_scanned = {} + + for profession in pairs(Player.professions) do + Player.has_scanned[profession] = false + end + + end -- do + + ------------------------------------------------------------------------------- + -- Initialize the SpecialtyTable and AllSpecialtiesTable. + ------------------------------------------------------------------------------- + do + local AlchemySpec = { + [GetSpellInfo(28674)] = 28674, + [GetSpellInfo(28678)] = 28678, + [GetSpellInfo(28676)] = 28676, + } + + local BlacksmithSpec = { + [GetSpellInfo(9788)] = 9788, -- Armorsmith + [GetSpellInfo(17041)] = 17041, -- Master Axesmith + [GetSpellInfo(17040)] = 17040, -- Master Hammersmith + [GetSpellInfo(17039)] = 17039, -- Master Swordsmith + [GetSpellInfo(9787)] = 9787, -- Weaponsmith + } + + local EngineeringSpec = { + [GetSpellInfo(20219)] = 20219, -- Gnomish + [GetSpellInfo(20222)] = 20222, -- Goblin + } + + local LeatherworkSpec = { + [GetSpellInfo(10657)] = 10657, -- Dragonscale + [GetSpellInfo(10659)] = 10659, -- Elemental + [GetSpellInfo(10661)] = 10661, -- Tribal + } + + local TailorSpec = { + [GetSpellInfo(26797)] = 26797, -- Spellfire + [GetSpellInfo(26801)] = 26801, -- Shadoweave + [GetSpellInfo(26798)] = 26798, -- Primal Mooncloth + } + + SpecialtyTable = { + [GetSpellInfo(51304)] = AlchemySpec, + [GetSpellInfo(51300)] = BlacksmithSpec, + [GetSpellInfo(51306)] = EngineeringSpec, + [GetSpellInfo(51302)] = LeatherworkSpec, + [GetSpellInfo(51309)] = TailorSpec, + } + + -- Populate the Specialty table with all Specialties, adding alchemy even though no recipes have alchemy filters + for i in pairs(AlchemySpec) do AllSpecialtiesTable[i] = true end + for i in pairs(BlacksmithSpec) do AllSpecialtiesTable[i] = true end + for i in pairs(EngineeringSpec) do AllSpecialtiesTable[i] = true end + for i in pairs(LeatherworkSpec) do AllSpecialtiesTable[i] = true end + for i in pairs(TailorSpec) do AllSpecialtiesTable[i] = true end + end -- do +end + +---Run when the addon is disabled. Ace3 takes care of unregistering events, etc. +function addon:OnDisable() + addon.Frame:Hide() + + -- Remove the option from Manufac + if Manufac then + Manufac.options.args.ARLScan = nil + end +end + +------------------------------------------------------------------------------- +-- Event handling functions +------------------------------------------------------------------------------- + +---Event used for datamining when a trainer is shown. +function addon:TRAINER_SHOW() + self:ScanSkillLevelData(true) + self:ScanTrainerData(true) +end + +---Event used for datamining when a vendor is shown. +function addon:MERCHANT_SHOW() + addon:ScanVendor() +end + +do + local GetTradeSkillListLink = _G.GetTradeSkillListLink + local UnitName = _G.UnitName + local GetRealmName = _G.GetRealmName + local IsTradeSkillLinked = _G.IsTradeSkillLinked + + function addon:TRADE_SKILL_SHOW() + -- If this is our own skill, save it, if not don't save it + if not IsTradeSkillLinked() then + local tradelink = GetTradeSkillListLink() + + if tradelink then + local pname = UnitName("player") + local prealm = GetRealmName() + local tradename = GetTradeSkillLine() + + -- Actual alt information saved here. -Torhal + addon.db.global.tradeskill = addon.db.global.tradeskill or {} + addon.db.global.tradeskill[prealm] = addon.db.global.tradeskill[prealm] or {} + addon.db.global.tradeskill[prealm][pname] = addon.db.global.tradeskill[prealm][pname] or {} + + addon.db.global.tradeskill[prealm][pname][tradename] = tradelink + end + end + local scan_button = self.scan_button + local scan_parent = scan_button:GetParent() + + if not scan_parent or scan_parent == UIParent then + scan_button:SetParent(TradeSkillFrame) + scan_parent = scan_button:GetParent() + end + + if scan_parent == TradeSkillFrame then + scan_button:ClearAllPoints() + + local loc = addon.db.profile.scanbuttonlocation + + if loc == "TR" then + scan_button:SetPoint("RIGHT", TradeSkillFrameCloseButton, "LEFT",4,0) + elseif loc == "TL" then + scan_button:SetPoint("LEFT", TradeSkillFramePortrait, "RIGHT",2,12) + elseif loc == "BR" then + scan_button:SetPoint("TOP", TradeSkillCancelButton, "BOTTOM",0,-5) + elseif loc == "BL" then + scan_button:SetPoint("TOP", TradeSkillCreateAllButton, "BOTTOM",0,-5) + end + scan_button:SetWidth(scan_button:GetTextWidth() + 10) + end + scan_button:Show() + end +end + +function addon:TRADE_SKILL_CLOSE() + if addon.db.profile.closeguionskillclose then + self.Frame:Hide() + end + + if not Skillet then + addon.scan_button:Hide() + end +end + +do + local last_update = 0 + local updater = CreateFrame("Frame", nil, UIParent) + + updater:Hide() + updater:SetScript("OnUpdate", + function(self, elapsed) + last_update = last_update + elapsed + + if last_update >= 0.5 then + local profession = GetTradeSkillLine() + + if profession ~= "UNKNOWN" then + addon:Scan(false, true) + end + self:Hide() + end + end) + + function addon:TRADE_SKILL_UPDATE(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) + if not self.Frame:IsVisible() then + return + end + + if not updater:IsVisible() then + last_update = 0 + updater:Show() + end + end +end + +------------------------------------------------------------------------------- +-- Tradeskill functions +-- Recipe DB Structures are defined in Documentation.lua +------------------------------------------------------------------------------- + +do + local SF = private.recipe_state_flags + + ------------------------------------------------------------------------------- + -- Recipe member functions for bit flags. + ------------------------------------------------------------------------------- + local function Recipe_HasState(self, state_name) + return self.state and (bit.band(self.state, SF[state_name]) == SF[state_name]) or false + end + + local function Recipe_AddState(self, state_name) + if not self.state then + self.state = 0 + end + + if bit.band(self.state, SF[state_name]) == SF[state_name] then + return + end + self.state = bit.bxor(self.state, SF[state_name]) + end + + local function Recipe_RemoveState(self, state_name) + if not self.state then + return + end + + if bit.band(self.state, SF[state_name]) ~= SF[state_name] then + return + end + self.state = bit.bxor(self.state, SF[state_name]) + + if self.state == 0 then + self.state = nil + end + end + + local BITFIELD_MAP = { + ["common1"] = private.common_flags_word1, + ["class1"] = private.class_flags_word1, + ["reputation1"] = private.rep_flags_word1, + ["reputation2"] = private.rep_flags_word2, + ["item1"] = private.item_flags_word1, + } + + local function Recipe_IsFlagged(self, field_name, flag_name) + local bitfield = self.flags[field_name] + local bitset = BITFIELD_MAP[field_name] + local value = bitset[flag_name] + + return bitfield and (bit.band(bitfield, value) == value) or false + end + + --- Adds a tradeskill recipe into the specified recipe database + -- @name AckisRecipeList:AddRecipe + -- @usage AckisRecipeList:AddRecipe(28927, 305, 23109, Q.UNCOMMON, V.TBC, 305, 305, 325, 345) + -- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe being added to the database + -- @param skill_level The skill level at which the recipe can be initially learned + -- @param item_id The [[http://www.wowwiki.com/ItemLink|Item ID]] that is created by the recipe, or nil + -- @param quality The quality/rarity of the recipe + -- @param profession The profession ID that uses the recipe. See [[API/database-documentation]] for a listing of profession IDs + -- @param specialty The specialty that uses the recipe (ie: goblin engineering) or nil or blank + -- @param genesis Game version that the recipe was first introduced in, for example, Original, BC, or WoTLK + -- @param optimal_level Level at which recipe is considered orange + -- @param medium_level Level at which recipe is considered yellow + -- @param easy_level Level at which recipe is considered green + -- @param trivial_level Level at which recipe is considered grey + -- @return None, array is passed as a reference + function addon:AddRecipe(spell_id, skill_level, item_id, quality, profession, specialty, genesis, optimal_level, medium_level, easy_level, trivial_level) + local recipe_list = private.recipe_list + + if recipe_list[spell_id] then + --@alpha@ + self:Print("Duplicate recipe: "..recipe_list[spell_id].profession.." "..tostring(spell_id).." "..recipe_list[spell_id].name) + --@end-alpha@ + return + end + + local recipe = { + ["spell_id"] = spell_id, + ["skill_level"] = skill_level, + ["item_id"] = item_id, + ["quality"] = quality, + ["profession"] = GetSpellInfo(profession), + ["name"] = GetSpellInfo(spell_id), + ["flags"] = {}, + ["acquire_data"] = {}, + ["specialty"] = specialty, -- Assumption: there will only be 1 speciality for a trade skill + ["genesis"] = private.game_version_names[genesis], + ["optimal_level"] = optimal_level or skill_level, + ["medium_level"] = medium_level or skill_level + 10, + ["easy_level"] = easy_level or skill_level + 15, + ["trivial_level"] = trivial_level or skill_level + 20, + + -- Function members + ["HasState"] = Recipe_HasState, + ["AddState"] = Recipe_AddState, + ["RemoveState"] = Recipe_RemoveState, + ["IsFlagged"] = Recipe_IsFlagged, + } + + if not recipe.name then + self:Print(strformat(L["SpellIDCache"], spell_id)) + end + recipe_list[spell_id] = recipe + end +end -- do + +-- Public API function for retrieving specific information about a recipe. +-- @name AckisRecipeList:GetRecipeData +-- @usage AckisRecipeList:GetRecipeData(28972, "profession") +-- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe being queried. +-- @param data Which member of the recipe table is being queried. +-- @return Variable, depending upon which member of the recipe table is queried. +function addon:GetRecipeData(spell_id, data) + local recipe = private.recipe_list[spell_id] + return recipe and recipe[data] or nil +end + +--- Adds filtering flags to a specific tradeskill. +-- @name AckisRecipeList:AddRecipeFlags +-- @usage AckisRecipeList:AddRecipeFlags(28927, F.ALLIANCE, F.VENDOR, F.IBOE, F.RBOP, F.HEALER, F.CASTER, F.ALDOR) +-- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe which the filter flags are being added to +-- @param ... A listing of filtering flags. See [[API/database-documentation]] for a listing of filter flags +-- @return None, array is passed as a reference. +function addon:AddRecipeFlags(spell_id, ...) + local num_flags = select('#',...) + local recipe = private.recipe_list[spell_id] + + for index = 1, num_flags, 1 do + local flag = select(index, ...) + local flag_name = private.filter_strings[flag] + + local bitfield + local member_name + + for table_index, bits in ipairs(private.bit_flags) do + if bits[flag_name] then + bitfield = bits + member_name = private.flag_members[table_index] + end + end + + if not bitfield or not member_name then + return + end + + if not recipe.flags[member_name] then + recipe.flags[member_name] = 0 + end + recipe.flags[member_name] = bit.bxor(recipe.flags[member_name], bitfield[flag_name]) + end +end + +--- Adds acquire methods to a specific tradeskill. +-- @name AckisRecipeList:AddRecipeAcquire +-- @usage AckisRecipeList:AddRecipeAcquire(28927, A.REPUTATION, FAC.ALDOR, REP.HONORED, 19321) +-- @param spell_id The [[http://www.wowwiki.com/SpellLink|Spell ID]] of the recipe which acquire methods are being added to +-- @param ... A listing of acquire methods. See [[API/database-documentation]] for a listing of acquire methods and how they work +-- @return None, array is passed as a reference. +do + local location_list = private.location_list + local acquire_list = private.acquire_list + + function addon:AddRecipeAcquire(spell_id, ...) + local numvars = select('#', ...) -- Find out how many flags we're adding + local i = 1 -- Index for which variables we're parsing through + local recipe_list = private.recipe_list + local acquire_data = recipe_list[spell_id].acquire_data + + while i <= numvars do + local location, affiliation + + local acquire_type, acquire_id = select(i, ...) + i = i + 2 + + if not acquire_type then + self:Debug("Spell ID: %d has no acquire type.", spell_id) + else + acquire_data[acquire_type] = acquire_data[acquire_type] or {} + + local acquire = acquire_data[acquire_type] + + if not acquire_id then + self:Debug("Spell ID %d: %s ID is nil.", spell_id, private.acquire_strings[acquire_type]) + else + if acquire_type == A.TRAINER then + local trainer_list = private.trainer_list + local trainer = trainer_list[acquire_id] + + if not trainer then + self:Debug("Spell ID "..spell_id..": TrainerID "..acquire_id.." does not exist in the database.") + else + acquire[acquire_id] = true + + affiliation = trainer.faction + location = trainer.location + + trainer.item_list = trainer.item_list or {} + trainer.item_list[spell_id] = true + end + elseif acquire_type == A.VENDOR then + local vendor_list = private.vendor_list + local vendor = vendor_list[acquire_id] + + if not vendor then + self:Debug("Spell ID "..spell_id..": VendorID "..acquire_id.." does not exist in the database.") + else + acquire[acquire_id] = true + + affiliation = vendor.faction + location = vendor.location + + vendor.item_list = vendor.item_list or {} + vendor.item_list[spell_id] = true + end + elseif acquire_type == A.MOB_DROP then + local mob_list = private.mob_list + local mob = mob_list[acquire_id] + + if not mob then + self:Debug("Spell ID "..spell_id..": Mob ID "..acquire_id.." does not exist in the database.") + else + acquire[acquire_id] = true + + affiliation = mob.faction + location = mob.location + + mob_list[acquire_id].item_list = mob_list[acquire_id].item_list or {} + mob_list[acquire_id].item_list[spell_id] = true + end + elseif acquire_type == A.QUEST then + local quest_list = private.quest_list + local quest = quest_list[acquire_id] + + if not quest then + self:Debug("Spell ID "..spell_id..": Quest ID "..acquire_id.." does not exist in the database.") + else + acquire[acquire_id] = true + + affiliation = quest.faction + location = quest.location + end + elseif acquire_type == A.REPUTATION then + local vendor_list = private.vendor_list + local rep_level, vendor_id = select(i, ...) + i = i + 2 + + if not private.reputation_list[acquire_id] then + self:Debug("Spell ID "..spell_id..": ReputationID "..acquire_id.." does not exist in the database.") + else + if not vendor_id then + self:Debug("Spell ID "..spell_id..": Reputation Vendor ID is nil.") + elseif not vendor_list[vendor_id] then + self:Debug("Spell ID "..spell_id..": Reputation Vendor ID "..vendor_id.." does not exist in the database.") + else + acquire[acquire_id] = acquire[acquire_id] or {} + + local faction = acquire[acquire_id] + faction[rep_level] = faction[rep_level] or {} + faction[rep_level][vendor_id] = true + + local rep_vendor = vendor_list[vendor_id] + + affiliation = rep_vendor.faction + location = rep_vendor.location + + rep_vendor.item_list = rep_vendor.item_list or {} + rep_vendor.item_list[spell_id] = true + end + end + elseif acquire_type == A.WORLD_DROP then + acquire[acquire_id] = true + location = type(acquire_id) == "string" and BZ[acquire_id] or nil + + if location then + affiliation = "world_drop" + else + addon:Debug("WORLD_DROP with no location: %d %s", spell_id, recipe_list[spell_id].name) + end + elseif acquire_type == A.SEASONAL then + acquire[acquire_id] = true + elseif acquire_type == A.CUSTOM then + acquire[acquire_id] = true + location = private.custom_list[acquire_id].location + else + -- Unhandled acquire_type + acquire[acquire_id] = true + location = private.acquire_strings[acquire_type] or _G.UNKNOWN + end + end -- acquire_id + acquire_list[acquire_type] = acquire_list[acquire_type] or {} + acquire_list[acquire_type].recipes = acquire_list[acquire_type].recipes or {} + + acquire_list[acquire_type].name = private.acquire_names[acquire_type] + acquire_list[acquire_type].recipes[spell_id] = affiliation or true + end -- acquire_type + + if location then + location_list[location] = location_list[location] or {} + location_list[location].recipes = location_list[location].recipes or {} + + location_list[location].name = location + location_list[location].recipes[spell_id] = affiliation or true + end + end -- while + end + + local function GenericAddRecipeAcquire(spell_id, acquire_type, type_string, unit_list, ...) + local num_vars = select('#', ...) + local cur_var = 1 + local recipe = private.recipe_list[spell_id] + + local acquire_data = recipe.acquire_data + acquire_data[acquire_type] = acquire_data[acquire_type] or {} + + local acquire = acquire_data[acquire_type] + + while cur_var <= num_vars do + local location, affiliation + local id_num = select(cur_var, ...) + cur_var = cur_var + 1 + + -- A quantity of true means unlimited - normal vendor item. + local quantity = true + + if type_string == "Limited Vendor" then + quantity = select(cur_var, ...) + cur_var = cur_var + 1 + end + acquire[id_num] = true + + if unit_list and not unit_list[id_num] then + addon:Debug("Spell ID %d: %s ID %d does not exist in the database.", spell_id, type_string, id_num) + else + if not unit_list then + location = type(id_num) == "string" and BZ[id_num] or nil + + if location then + affiliation = "world_drop" + else + addon:Debug("WORLD_DROP with no location: %d %s", spell_id, private.recipe_list[spell_id].name) + end + else + local unit = unit_list[id_num] + + affiliation = unit.faction + location = unit.location + + unit.item_list = unit.item_list or {} + unit.item_list[spell_id] = quantity + end + end + acquire_list[acquire_type] = acquire_list[acquire_type] or {} + acquire_list[acquire_type].recipes = acquire_list[acquire_type].recipes or {} + + acquire_list[acquire_type].name = private.acquire_names[acquire_type] + acquire_list[acquire_type].recipes[spell_id] = affiliation or true + + if location then + location_list[location] = location_list[location] or {} + location_list[location].recipes = location_list[location].recipes or {} + + location_list[location].name = location + location_list[location].recipes[spell_id] = affiliation or true + end + end + end + + function addon:AddRecipeMobDrop(spell_id, ...) + GenericAddRecipeAcquire(spell_id, A.MOB_DROP, "Mob", private.mob_list, ...) + end + + function addon:AddRecipeTrainer(spell_id, ...) + GenericAddRecipeAcquire(spell_id, A.TRAINER, "Trainer", private.trainer_list, ...) + end + + function addon:AddRecipeVendor(spell_id, ...) + GenericAddRecipeAcquire(spell_id, A.VENDOR, "Vendor", private.vendor_list, ...) + end + + function addon:AddRecipeLimitedVendor(spell_id, ...) + GenericAddRecipeAcquire(spell_id, A.VENDOR, "Limited Vendor", private.vendor_list, ...) + end + + function addon:AddRecipeWorldDrop(spell_id, ...) + GenericAddRecipeAcquire(spell_id, A.WORLD_DROP, nil, nil, ...) + end + + function addon:AddRecipeQuest(spell_id, ...) + GenericAddRecipeAcquire(spell_id, A.QUEST, "Quest", private.quest_list, ...) + end + + -- This function can NOT use GenericAddRecipeAcquire() - reputation vendors are more complicated than the other acquire types. + function addon:AddRecipeRepVendor(spell_id, faction_id, rep_level, ...) + local num_vars = select('#', ...) + local cur_var = 1 + + local recipe = private.recipe_list[spell_id] + local vendor_list = private.vendor_list + + local acquire_data = recipe.acquire_data + acquire_data[A.REPUTATION] = acquire_data[A.REPUTATION] or {} + + local acquire = acquire_data[A.REPUTATION] + acquire[faction_id] = acquire[faction_id] or {} + + local faction = acquire[faction_id] + faction[rep_level] = faction[rep_level] or {} + + while cur_var <= num_vars do + local location, affiliation + local vendor_id = select(cur_var, ...) + cur_var = cur_var + 1 + + if not private.reputation_list[faction_id] then + --@alpha@ + self:Printf("Spell ID %d: Faction ID %d does not exist in the database.", spell_id, faction_id) + --@end-alpha@ + else + if not vendor_id then + --@alpha@ + self:Print("Spell ID "..spell_id..": Reputation Vendor ID is nil.") + --@end-alpha@ + elseif not vendor_list[vendor_id] then + --@alpha@ + self:Print("Spell ID "..spell_id..": Reputation Vendor ID "..vendor_id.." does not exist in the database.") + --@end-alpha@ + else + faction[rep_level][vendor_id] = true + + local rep_vendor = vendor_list[vendor_id] + + affiliation = rep_vendor.faction + location = rep_vendor.location + + rep_vendor.item_list = rep_vendor.item_list or {} + rep_vendor.item_list[spell_id] = true + end + end + acquire_list[A.REPUTATION] = acquire_list[A.REPUTATION] or {} + acquire_list[A.REPUTATION].recipes = acquire_list[A.REPUTATION].recipes or {} + + acquire_list[A.REPUTATION].name = private.acquire_names[A.REPUTATION] + acquire_list[A.REPUTATION].recipes[spell_id] = affiliation or true + + if location then + location_list[location] = location_list[location] or {} + location_list[location].recipes = location_list[location].recipes or {} + + location_list[location].name = location + location_list[location].recipes[spell_id] = affiliation or true + end + end + end +end -- do block + +--- Adds an item to a specific database listing (ie: vendor, mob, etc) +-- @name AckisRecipeList:addLookupList +-- @usage AckisRecipeList:addLookupList(DB,NPC ID, NPC Name, NPC Location, X Coord, Y Coord, Faction) +-- @param DB Database which the entry will be stored +-- @param ID Unique identified for the entry +-- @param name Name of the entry +-- @param location Location of the entry in the world +-- @param coord_x X coordinate of where the entry is found +-- @param coord_y Y coordinate of where the entry is found +-- @param faction Faction identifier for the entry +-- @return None, array is passed as a reference +--For individual database structures, see Documentation.lua +do + local FACTION_NAMES = { + [1] = BFAC["Neutral"], + [2] = BFAC["Alliance"], + [3] = BFAC["Horde"] + } + function addon:addLookupList(DB, ID, name, location, coord_x, coord_y, faction) + if DB[ID] then + self:Debug("Duplicate lookup: %d - %s.", ID, name) + return + end + + DB[ID] = { + ["name"] = name, + ["location"] = location, + ["faction"] = faction and FACTION_NAMES[faction + 1] or FACTION_NAMES[1] + } + + if coord_x and coord_y then + DB[ID]["coord_x"] = coord_x + DB[ID]["coord_y"] = coord_y + end + + --@alpha@ + if not location and DB ~= private.custom_list then + self:Debug("Lookup ID: %d (%s) has an unknown location.", ID, DB[ID].name or _G.UNKNOWN) + end + + if faction and DB == private.mob_list then + self:Debug("Mob %d (%s) has been assigned to faction %s.", ID, name, DB[ID].faction) + end + --@end-alpha@ + end +end -- do + +------------------------------------------------------------------------------- +-- Filter flag functions +------------------------------------------------------------------------------- +do + local COMMON1 = private.common_flags_word1 + local CLASS1 = private.class_flags_word1 + local REP1 = private.rep_flags_word1 + local REP2 = private.rep_flags_word2 + local ITEM1 = private.item_flags_word1 + + -- HardFilterFlags and SoftFilterFlags are used to determine if a recipe should be shown based on the value of the key compared to the value of its saved_var. + -- Its keys and values are populated the first time CanDisplayRecipe() is called. + local HardFilterFlags, SoftFilterFlags, RepFilterFlags, RepFilterFlags2 + + local ClassFilterFlags = { + ["deathknight"] = CLASS1.DK, + ["druid"] = CLASS1.DRUID, + ["hunter"] = CLASS1.HUNTER, + ["mage"] = CLASS1.MAGE, + ["paladin"] = CLASS1.PALADIN, + ["priest"] = CLASS1.PRIEST, + ["shaman"] = CLASS1.SHAMAN, + ["rogue"] = CLASS1.ROGUE, + ["warlock"] = CLASS1.WARLOCK, + ["warrior"] = CLASS1.WARRIOR, + } + + ---Scans a specific recipe to determine if it is to be displayed or not. + -- For flag info see comments at start of file in comments + local function CanDisplayRecipe(recipe) + if addon.db.profile.exclusionlist[recipe.spell_id] and not addon.db.profile.ignoreexclusionlist then + return false + end + local filter_db = addon.db.profile.filters + local general_filters = filter_db.general + + -- See Documentation file for logic explanation + ------------------------------------------------------------------------------- + -- Stage 1 + -- Loop through exclusive flags (hard filters) + -- If one of these does not pass we do not display the recipe + -- So to be more efficient we'll just leave this function if there's a false + ------------------------------------------------------------------------------- + + -- Display both horde and alliance factions? + if not general_filters.faction and not Player:HasRecipeFaction(recipe) then + return false + end + + -- Display all skill levels? + if not general_filters.skill and recipe.skill_level > Player["ProfessionLevel"] then + return false + end + + -- Display all specialities? + if not general_filters.specialty then + local specialty = recipe.specialty + + if specialty and specialty ~= Player["Specialty"] then + return false + end + end + + -- Display retired recipes? + if not general_filters.retired and bit.band(recipe.flags.common1, COMMON1.RETIRED) == COMMON1.RETIRED then + return false + end + local obtain_filters = filter_db.obtain + local game_version = private.game_versions[recipe.genesis] + local V = private.game_versions + + -- Filter out game recipes + if not obtain_filters.expansion0 and game_version == V.ORIG then + return false + end + + if not obtain_filters.expansion1 and game_version == V.TBC then + return false + end + + if not obtain_filters.expansion2 and game_version == V.WOTLK then + return false + end + local quality_filters = filter_db.quality + local recipe_quality = recipe.quality + local Q = private.item_qualities + + -- Filter out certain recipe quality types. + if not quality_filters.common and recipe_quality == Q.COMMON then + return false + end + + if not quality_filters.uncommon and recipe_quality == Q.UNCOMMON then + return false + end + + if not quality_filters.rare and recipe_quality == Q.RARE then + return false + end + + if not quality_filters.epic and recipe_quality == Q.EPIC then + return false + end + + ------------------------------------------------------------------------------- + -- Check the hard filter flags + ------------------------------------------------------------------------------- + if not HardFilterFlags then + local binding_filters = filter_db.binding + local player_filters = filter_db.player + local armor_filters = filter_db.item.armor + local weapon_filters = filter_db.item.weapon + + HardFilterFlags = { + ------------------------------------------------------------------------------------------------ + -- Binding flags. + ------------------------------------------------------------------------------------------------ + ["itemboe"] = { flag = COMMON1.IBOE, index = 1, sv_root = binding_filters }, + ["itembop"] = { flag = COMMON1.IBOP, index = 1, sv_root = binding_filters }, + ["itemboa"] = { flag = COMMON1.IBOA, index = 1, sv_root = binding_filters }, + ["recipeboe"] = { flag = COMMON1.RBOE, index = 1, sv_root = binding_filters }, + ["recipebop"] = { flag = COMMON1.RBOP, index = 1, sv_root = binding_filters }, + ["recipeboa"] = { flag = COMMON1.RBOA, index = 1, sv_root = binding_filters }, + ------------------------------------------------------------------------------------------------ + -- Player Type flags. + ------------------------------------------------------------------------------------------------ + ["melee"] = { flag = COMMON1.DPS, index = 1, sv_root = player_filters }, + ["tank"] = { flag = COMMON1.TANK, index = 1, sv_root = player_filters }, + ["healer"] = { flag = COMMON1.HEALER, index = 1, sv_root = player_filters }, + ["caster"] = { flag = COMMON1.CASTER, index = 1, sv_root = player_filters }, + ------------------------------------------------------------------------------------------------ + -- Armor flags. + ------------------------------------------------------------------------------------------------ + ["cloth"] = { flag = ITEM1.CLOTH, index = 5, sv_root = armor_filters }, + ["leather"] = { flag = ITEM1.LEATHER, index = 5, sv_root = armor_filters }, + ["mail"] = { flag = ITEM1.MAIL, index = 5, sv_root = armor_filters }, + ["plate"] = { flag = ITEM1.PLATE, index = 5, sv_root = armor_filters }, + ["trinket"] = { flag = ITEM1.TRINKET, index = 5, sv_root = armor_filters }, + ["cloak"] = { flag = ITEM1.CLOAK, index = 5, sv_root = armor_filters }, + ["ring"] = { flag = ITEM1.RING, index = 5, sv_root = armor_filters }, + ["necklace"] = { flag = ITEM1.NECK, index = 5, sv_root = armor_filters }, + ["shield"] = { flag = ITEM1.SHIELD, index = 5, sv_root = armor_filters }, + ------------------------------------------------------------------------------------------------ + -- Weapon flags. + ------------------------------------------------------------------------------------------------ + ["onehand"] = { flag = ITEM1.ONE_HAND, index = 5, sv_root = weapon_filters }, + ["twohand"] = { flag = ITEM1.TWO_HAND, index = 5, sv_root = weapon_filters }, + ["axe"] = { flag = ITEM1.AXE, index = 5, sv_root = weapon_filters }, + ["sword"] = { flag = ITEM1.SWORD, index = 5, sv_root = weapon_filters }, + ["mace"] = { flag = ITEM1.MACE, index = 5, sv_root = weapon_filters }, + ["polearm"] = { flag = ITEM1.POLEARM, index = 5, sv_root = weapon_filters }, + ["dagger"] = { flag = ITEM1.DAGGER, index = 5, sv_root = weapon_filters }, + ["fist"] = { flag = ITEM1.FIST, index = 5, sv_root = weapon_filters }, + ["gun"] = { flag = ITEM1.GUN, index = 5, sv_root = weapon_filters }, + ["staff"] = { flag = ITEM1.STAFF, index = 5, sv_root = weapon_filters }, + ["wand"] = { flag = ITEM1.WAND, index = 5, sv_root = weapon_filters }, + ["thrown"] = { flag = ITEM1.THROWN, index = 5, sv_root = weapon_filters }, + ["bow"] = { flag = ITEM1.BOW, index = 5, sv_root = weapon_filters }, + ["crossbow"] = { flag = ITEM1.XBOW, index = 5, sv_root = weapon_filters }, + ["ammo"] = { flag = ITEM1.AMMO, index = 5, sv_root = weapon_filters }, + } + end + + for filter, data in pairs(HardFilterFlags) do + local bitfield = recipe.flags[private.flag_members[data.index]] + + if bitfield and bit.band(bitfield, data.flag) == data.flag and not data.sv_root[filter] then + return false + end + end + + ------------------------------------------------------------------------------- + -- Check the reputation filter flags + ------------------------------------------------------------------------------- + if not RepFilterFlags then + RepFilterFlags = { + [REP1.ARGENTDAWN] = "argentdawn", + [REP1.CENARION_CIRCLE] = "cenarioncircle", + [REP1.THORIUM_BROTHERHOOD] = "thoriumbrotherhood", + [REP1.TIMBERMAW_HOLD] = "timbermaw", + [REP1.ZANDALAR] = "zandalar", + [REP1.ALDOR] = "aldor", + [REP1.ASHTONGUE] = "ashtonguedeathsworn", + [REP1.CENARION_EXPEDITION] = "cenarionexpedition", + [REP1.HELLFIRE] = "hellfire", + [REP1.CONSORTIUM] = "consortium", + [REP1.KOT] = "keepersoftime", + [REP1.LOWERCITY] = "lowercity", + [REP1.NAGRAND] = "nagrand", + [REP1.SCALE_SANDS] = "scaleofthesands", + [REP1.SCRYER] = "scryer", + [REP1.SHATAR] = "shatar", + [REP1.SHATTEREDSUN] = "shatteredsun", + [REP1.SPOREGGAR] = "sporeggar", + [REP1.VIOLETEYE] = "violeteye", + [REP1.ARGENTCRUSADE] = "argentcrusade", + [REP1.FRENZYHEART] = "frenzyheart", + [REP1.EBONBLADE] = "ebonblade", + [REP1.KIRINTOR] = "kirintor", + [REP1.HODIR] = "sonsofhodir", + [REP1.KALUAK] = "kaluak", + [REP1.ORACLES] = "oracles", + [REP1.WYRMREST] = "wyrmrest", + [REP1.WRATHCOMMON1] = "wrathcommon1", + [REP1.WRATHCOMMON2] = "wrathcommon2", + [REP1.WRATHCOMMON3] = "wrathcommon3", + [REP1.WRATHCOMMON4] = "wrathcommon4", + [REP1.WRATHCOMMON5] = "wrathcommon5", + } + end + + if not RepFilterFlags2 then + RepFilterFlags2 = { + [REP2.ASHEN_VERDICT] = "ashenverdict", + } + end + -- Now we check to see if _all_ of the pertinent reputation or class flags are toggled off. If even one is toggled on, we still show the recipe. + local toggled_off, toggled_on = 0, 0 + + for flag, name in pairs(RepFilterFlags) do + local bitfield = recipe.flags.reputation1 + + if bitfield and bit.band(bitfield, flag) == flag then + if filter_db.rep[name] then + toggled_on = toggled_on + 1 + else + toggled_off = toggled_off + 1 + end + end + end + + if toggled_off > 0 and toggled_on == 0 then + return false + end + + toggled_off, toggled_on = 0, 0 + + for flag, name in pairs(RepFilterFlags2) do + local bitfield = recipe.flags.reputation2 + + if bitfield and bit.band(bitfield, flag) == flag then + if filter_db.rep[name] then + toggled_on = toggled_on + 1 + else + toggled_off = toggled_off + 1 + end + end + end + + if toggled_off > 0 and toggled_on == 0 then + return false + end + + ------------------------------------------------------------------------------- + -- Check the class filter flags + ------------------------------------------------------------------------------- + local class_filters = filter_db.classes + + toggled_off, toggled_on = 0, 0 + + for class, flag in pairs(ClassFilterFlags) do + local bitfield = recipe.flags.class1 + + if bitfield and bit.band(bitfield, flag) == flag then + if class_filters[class] then + toggled_on = toggled_on + 1 + else + toggled_off = toggled_off + 1 + end + end + end + + if toggled_off > 0 and toggled_on == 0 then + return false + end + + ------------------------------------------------------------------------------------------------ + -- Stage 2 + -- loop through nonexclusive (soft filters) flags until one is true + -- If one of these is true (ie: we want to see trainers and there is a trainer flag) we display the recipe + ------------------------------------------------------------------------------------------------ + if not SoftFilterFlags then + SoftFilterFlags = { + ["trainer"] = { flag = COMMON1.TRAINER, index = 1, sv_root = obtain_filters }, + ["vendor"] = { flag = COMMON1.VENDOR, index = 1, sv_root = obtain_filters }, + ["instance"] = { flag = COMMON1.INSTANCE, index = 1, sv_root = obtain_filters }, + ["raid"] = { flag = COMMON1.RAID, index = 1, sv_root = obtain_filters }, + ["seasonal"] = { flag = COMMON1.SEASONAL, index = 1, sv_root = obtain_filters }, + ["quest"] = { flag = COMMON1.QUEST, index = 1, sv_root = obtain_filters }, + ["pvp"] = { flag = COMMON1.PVP, index = 1, sv_root = obtain_filters }, + ["worlddrop"] = { flag = COMMON1.WORLD_DROP, index = 1, sv_root = obtain_filters }, + ["mobdrop"] = { flag = COMMON1.MOB_DROP, index = 1, sv_root = obtain_filters }, + ["discovery"] = { flag = COMMON1.DISC, index = 1, sv_root = obtain_filters }, + } + end + + for filter, data in pairs(SoftFilterFlags) do + local bitfield = recipe.flags[private.flag_members[data.index]] + + if bitfield and bit.band(bitfield, data.flag) == data.flag and data.sv_root[filter] then + return true + end + end + + -- If we get here it means that no flags matched our values + return false + end + + ---Scans the recipe listing and updates the filters according to user preferences + function addon:UpdateFilters(is_linked) + local general_filters = addon.db.profile.filters.general + local recipes_total = 0 + local recipes_known = 0 + local recipes_total_filtered = 0 + local recipes_known_filtered = 0 + local can_display = false + local current_profession = self.Frame.prof_name or private.ordered_professions[self.Frame.profession] + local recipe_list = private.recipe_list + local SF = private.recipe_state_flags + + for recipe_id, recipe in pairs(recipe_list) do + recipe:RemoveState("VISIBLE") + + if recipe.profession == current_profession then + local is_known + + if is_linked then + is_known = recipe:HasState("LINKED") + else + is_known = recipe:HasState("KNOWN") + end + + can_display = CanDisplayRecipe(recipe) + recipes_total = recipes_total + 1 + recipes_known = recipes_known + (is_known and 1 or 0) + + if can_display then + recipes_total_filtered = recipes_total_filtered + 1 + recipes_known_filtered = recipes_known_filtered + (is_known and 1 or 0) + + if not general_filters.known and is_known then + can_display = false + end + + if not general_filters.unknown and not is_known then + can_display = false + end + end + else + can_display = false + end + + if can_display then + recipe:AddState("VISIBLE") + end + end + Player.recipes_total = recipes_total + Player.recipes_known = recipes_known + Player.recipes_total_filtered = recipes_total_filtered + Player.recipes_known_filtered = recipes_known_filtered + end + +end -- do + +------------------------------------------------------------------------------- +-- ARL Logic Functions +------------------------------------------------------------------------------- +function addon:InitializeProfession(profession) + if not profession then + --@alpha@ + addon:Print("nil profession passed to InitializeProfession()") + --@end-alpha@ + return + end + + if profession == private.professions["Smelting"] then + profession = private.mining_name + end + local func = PROFESSION_INITS[profession] + + if func then + return func(addon) + else + addon:Print(L["UnknownTradeSkill"]:format(profession)) + return 0 + end +end + +-- Determines what to do when the slash command is called. +function addon:ChatCommand(input) + + -- Open About panel if there's no parameters or if we do /arl about + if not input or (input and input:trim() == "") or input == strlower(L["Sorting"]) or input == strlower(L["Sort"]) or input == strlower(_G.DISPLAY) then + InterfaceOptionsFrame_OpenToCategory(self.optionsFrame) + elseif (input == strlower(L["About"])) then + if (self.optionsFrame["About"]) then + InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["About"]) + else + InterfaceOptionsFrame_OpenToCategory(self.optionsFrame) + end + elseif (input == strlower(L["Profile"])) then + InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["Profiles"]) + elseif (input == strlower(_G.FILTER)) then + InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["Filters"]) + elseif (input == strlower(L["Documentation"])) then + InterfaceOptionsFrame_OpenToCategory(self.optionsFrame["Documentation"]) + elseif (input == strlower(L["Scan"])) then + self:Scan(false) + elseif (input == strlower("scandata")) then + self:ScanSkillLevelData() + elseif (input == strlower("scanprof")) then + self:ScanProfession("all") + elseif (input == strlower("tradelinks")) then + self:GenerateLinks() + else + -- What happens when we get here? + LibStub("AceConfigCmd-3.0"):HandleCommand("arl", "Ackis Recipe List", input) + end + +end + +-- Public API function to initialize all of the lookup lists - self-nils once run. +-- @name AckisRecipeList:InitializeLookups() +-- @usage if AckisRecipeList.InitializeLookups then AckisRecipeList:InitializeLookups() end +function addon:InitializeLookups() + self:InitCustom(private.custom_list) + self:InitMob(private.mob_list) + self:InitQuest(private.quest_list) + self:InitReputation(private.reputation_list) + self:InitTrainer(private.trainer_list) + self:InitSeasons(private.seasonal_list) + self:InitVendor(private.vendor_list) + + self.InitializeLookups = nil +end + +------------------------------------------------------------------------------- +-- Recipe Scanning Functions +------------------------------------------------------------------------------- +do + -- List of tradeskill headers, used in addon:Scan() + local header_list = {} + + --- Causes a scan of the tradeskill to be conducted. Function called when the scan button is clicked. Parses recipes and displays output + -- @name AckisRecipeList:Scan + -- @usage AckisRecipeList:Scan(true) + -- @param textdump Boolean indicating if we want the output to be a text dump, or if we want to use the ARL GUI + -- @return A frame with either the text dump, or the ARL frame + function addon:Scan(textdump, is_refresh) + local current_prof, prof_level = GetTradeSkillLine() + + -- Bail if we haven't opened a tradeskill frame. + if current_prof == "UNKNOWN" then + self:Print(L["OpenTradeSkillWindow"]) + return + end + + -- Set the current profession level, and update the cached data. + Player["ProfessionLevel"] = prof_level + + -- Make sure we're only updating a profession the character actually knows - this could be a scan from a tradeskill link. + local is_linked = IsTradeSkillLinked() + + if not is_linked and Player.professions[current_prof] then + Player.professions[current_prof] = prof_level + Player.has_scanned[current_prof] = true + end + + -- Get the current profession Specialty + local specialty = SpecialtyTable[current_prof] + + for index = 1, 25, 1 do + local spellName = GetSpellName(index, BOOKTYPE_SPELL) + + if not spellName or index == 25 then + Player["Specialty"] = nil + break + elseif specialty and specialty[spellName] then + Player["Specialty"] = specialty[spellName] + break + end + end + + if self.InitializeLookups then + self:InitializeLookups() + end + -- Add the recipes to the database + -- TODO: Figure out what this variable was supposed to be for - it isn't used anywhere. -Torhal + Player.totalRecipes = addon:InitializeProfession(current_prof) + + ------------------------------------------------------------------------------- + -- Scan all recipes and mark the ones we know + ------------------------------------------------------------------------------- + twipe(header_list) + + -- Save the state of the "Have Materials" checkbox. + local have_materials = TradeSkillFrameAvailableFilterCheckButton:GetChecked() + + if MRTUIUtils_PushFilterSelection then + MRTUIUtils_PushFilterSelection() + else + if not Skillet and TradeSkillFrameAvailableFilterCheckButton:GetChecked() then + TradeSkillFrameAvailableFilterCheckButton:SetChecked(false) + TradeSkillOnlyShowMakeable(false) + end + + -- Clear the inventory slot filter + UIDropDownMenu_Initialize(TradeSkillInvSlotDropDown, TradeSkillInvSlotDropDown_Initialize) + UIDropDownMenu_SetSelectedID(TradeSkillInvSlotDropDown, 1) + SetTradeSkillInvSlotFilter(0, 1, 1) + + -- Clear the sub-classes filters + UIDropDownMenu_Initialize(TradeSkillSubClassDropDown, TradeSkillSubClassDropDown_Initialize) + UIDropDownMenu_SetSelectedID(TradeSkillSubClassDropDown, 1) + SetTradeSkillSubClassFilter(0, 1, 1) + + -- Expand all headers so we can see all the recipes there are + for i = GetNumTradeSkills(), 1, -1 do + local name, tradeType, _, isExpanded = GetTradeSkillInfo(i) + + if tradeType == "header" and not isExpanded then + header_list[name] = true + ExpandTradeSkillSubClass(i) + end + end + end + local recipe_list = private.recipe_list + local recipes_found = 0 + local SF = private.recipe_state_flags + + for i = 1, GetNumTradeSkills() do + local tradeName, tradeType = GetTradeSkillInfo(i) + + if tradeType ~= "header" then + -- Get the trade skill link for the specified recipe + local SpellLink = GetTradeSkillRecipeLink(i) + local SpellString = strmatch(SpellLink, "^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)") + local recipe = recipe_list[tonumber(SpellString)] + + if recipe then + if not is_linked then + recipe:AddState("KNOWN") + recipe:RemoveState("LINKED") + else + recipe:AddState("LINKED") + end + recipes_found = recipes_found + 1 + else + self:Debug(tradeName .. " " .. SpellString .. L["MissingFromDB"]) + end + end + end + + -- Close all the headers we've opened + -- If Mr Trader is installed use that API + if MRTUIUtils_PopFilterSelection then + MRTUIUtils_PopFilterSelection() + else + -- Collapse all headers that were collapsed before + for i = GetNumTradeSkills(), 1, -1 do + local name, tradeType, _, isExpanded = GetTradeSkillInfo(i) + + if header_list[name] then + CollapseTradeSkillSubClass(i) + end + end + -- Restore the state of the "Have Materials" checkbox. + TradeSkillFrameAvailableFilterCheckButton:SetChecked(have_materials) + TradeSkillOnlyShowMakeable(have_materials) + end + Player.prev_count = Player.foundRecipes + Player.foundRecipes = recipes_found + + if is_refresh and Player.prev_count == recipes_found then + return + end + + ------------------------------------------------------------------------------- + -- Update the player's reputation levels. + ------------------------------------------------------------------------------- + Player["Reputation"] = Player["Reputation"] or {} + + table.wipe(header_list) + + -- Number of factions before expansion + local num_factions = GetNumFactions() + + -- Expand all the headers, storing those which were collapsed. + for i = num_factions, 1, -1 do + local name, _, _, _, _, _, _, _, _, isCollapsed = GetFactionInfo(i) + + if isCollapsed then + ExpandFactionHeader(i) + header_list[name] = true + end + end + + -- Number of factions with everything expanded + num_factions = GetNumFactions() + + -- Get the rep levels + for i = 1, num_factions, 1 do + local name, _, replevel = GetFactionInfo(i) + + -- If the rep is greater than neutral + if replevel > 4 then + -- We use levels of 0, 1, 2, 3, 4 internally for reputation levels, make it correspond here + Player["Reputation"][name] = replevel - 4 + end + end + + -- Collapse the headers again + for i = num_factions, 1, -1 do + local name = GetFactionInfo(i) + + if header_list[name] then + CollapseFactionHeader(i) + end + end + + ------------------------------------------------------------------------------- + -- Everything is ready - display the GUI or dump the list to text. + ------------------------------------------------------------------------------- + if textdump then + self:DisplayTextDump(recipe_list, current_prof) + else + self.Frame:Display(current_prof, is_linked) + end + end +end + +------------------------------------------------------------------------------- +-- Text dumping functions +------------------------------------------------------------------------------- +do + ------------------------------------------------------------------------------- + -- Provides a string of comma separated values for all recipe information + ------------------------------------------------------------------------------- + local text_table = {} + local acquire_list = {} + local ACQUIRE_NAMES = private.acquire_names + + local GetFilterNames + do + local LC = _G.LOCALIZED_CLASS_NAMES_MALE + local FILTER_NAMES + + function GetFilterNames() + if not FILTER_NAMES then + local is_alliance = (Player.faction == "Alliance") + + FILTER_NAMES = { + [1] = BFAC["Alliance"], + [2] = BFAC["Horde"], + [3] = L["Trainer"], + [4] = L["Vendor"], + [5] = _G.INSTANCE, + [6] = _G.RAID, + [7] = _G.EVENTS_LABEL, + [8] = L["Quest"], + [9] = _G.PVP, + [10] = L["World Drop"], + [11] = L["Mob Drop"], + [12] = L["Discovery"], + [13] = L["Retired"], + [21] = LC["DEATHKNIGHT"], + [22] = LC["DRUID"], + [23] = LC["HUNTER"], + [24] = LC["MAGE"], + [25] = LC["PALADIN"], + [26] = LC["PRIEST"], + [27] = LC["SHAMAN"], + [28] = LC["ROGUE"], + [29] = LC["WARLOCK"], + [30] = LC["WARRIOR"], + [36] = L["BOEFilter"], + [37] = L["BOPFilter"], + [38] = L["BOAFilter"], + [40] = L["RecipeBOEFilter"], + [41] = L["RecipeBOPFilter"], + [42] = L["RecipeBOAFilter"], + [51] = _G.MELEE, + [52] = _G.TANK, + [53] = _G.HEALER, + [54] = _G.DAMAGER, + [56] = L["Cloth"], + [57] = L["Leather"], + [58] = L["Mail"], + [59] = L["Plate"], + [60] = L["Cloak"], + [61] = L["Trinket"], + [62] = L["Ring"], + [63] = L["Necklace"], + [64] = L["Shield"], + [66] = L["One Hand"], + [67] = L["Two Hand"], + [68] = L["Axe"], + [69] = L["Sword"], + [70] = L["Mace"], + [71] = L["Polearm"], + [72] = L["Dagger"], + [73] = L["Staff"], + [74] = L["Wand"], + [75] = L["Thrown"], + [76] = L["Bow"], + [77] = L["Crossbow"], + [78] = L["Ammo"], + [79] = L["Fist"], + [80] = L["Gun"], + [96] = BFAC["Argent Dawn"], + [97] = BFAC["Cenarion Circle"], + [98] = BFAC["Thorium Brotherhood"], + [99] = BFAC["Timbermaw Hold"], + [100] = BFAC["Zandalar Tribe"], + [101] = BFAC["The Aldor"], + [102] = BFAC["Ashtongue Deathsworn"], + [103] = BFAC["Cenarion Expedition"], + [104] = (is_alliance and BFAC["Honor Hold"] or BFAC["Thrallmar"]), + [105] = BFAC["The Consortium"], + [106] = BFAC["Keepers of Time"], + [107] = BFAC["Lower City"], + [108] = (is_alliance and BFAC["Kurenai"] or BFAC["The Mag'har"]), + [109] = BFAC["The Scale of the Sands"], + [110] = BFAC["The Scryers"], + [111] = BFAC["The Sha'tar"], + [112] = BFAC["Shattered Sun Offensive"], + [113] = BFAC["Sporeggar"], + [114] = BFAC["The Violet Eye"], + [115] = BFAC["Argent Crusade"], + [116] = BFAC["Frenzyheart Tribe"], + [117] = BFAC["Knights of the Ebon Blade"], + [118] = BFAC["Kirin Tor"], + [119] = BFAC["The Sons of Hodir"], + [120] = BFAC["The Kalu'ak"], + [121] = BFAC["The Oracles"], + [122] = BFAC["The Wyrmrest Accord"], + [123] = (is_alliance and BFAC["The Silver Covenant"] or BFAC["The Sunreavers"]), + [124] = (is_alliance and BFAC["Explorers' League"] or BFAC["The Hand of Vengeance"]), + [125] = (is_alliance and BFAC["Valiance Expedition"] or BFAC["Warsong Offensive"]), + [126] = (is_alliance and BFAC["The Frostborn"] or BFAC["The Taunka"]), + [127] = (is_alliance and BFAC["Alliance Vanguard"] or BFAC["Horde Expedition"]), + [128] = BFAC["The Ashen Verdict"], + } + end + return FILTER_NAMES + end + end -- do + + ---Dumps the recipe database in a format that is readable to humans. + function addon:GetTextDump(profession) + local output = addon.db.profile.textdumpformat + twipe(text_table) + + if not output or output == "Comma" then + tinsert(text_table, strformat("Ackis Recipe List Text Dump for %s's %s, in the form of Comma Separated Values.\n ", UnitName("player"), profession)) + tinsert(text_table, "Spell ID,Recipe Name,Skill Level,ARL Filter Flags,Acquire Methods,Known\n") + elseif output == "BBCode" then + tinsert(text_table, strformat("Ackis Recipe List Text Dump for %s's %s, in the form of BBCode.\n", UnitName("player"), profession)) + end + local recipe_list = private.recipe_list + local SF = private.recipe_state_flags + + for recipe_id in pairs(recipe_list) do + local recipe = recipe_list[recipe_id] + local recipe_prof = GetSpellInfo(recipe.profession) + local is_known = recipe:HasState("KNOWN") + + if recipe_prof == profession then + -- CSV + if not output or output == "Comma" then + -- Add Spell ID, Name and Skill Level to the list + tinsert(text_table, recipe_id) + tinsert(text_table, ",") + tinsert(text_table, recipe.name) + tinsert(text_table, ",") + tinsert(text_table, recipe.skill_level) + tinsert(text_table, ",\"") + -- BBCode + elseif output == "BBCode" then + -- Make the entry red + if not is_known then + tinsert(text_table, "[color=red]") + end + tinsert(text_table, "\n[b]" .. recipe_id .. "[/b] - " .. recipe.name .. " (" .. recipe.skill_level .. ")\n") + + -- Close Color tag + if not is_known then + tinsert(text_table, "[/color]\nRecipe Flags:\n[list]") + elseif is_known then + tinsert(text_table, "\nRecipe Flags:\n[list]") + end + --Name + elseif output == "Name" then + tinsert(text_table, recipe.name.."\n") + end + + -- Add in all the filter flags + local filter_names = GetFilterNames() + local prev = false + + -- Find out which flags are set + for table_index, bits in ipairs(private.bit_flags) do + for flag_name, flag in pairs(bits) do + local bitfield = recipe.flags[private.flag_members[table_index]] + + if bitfield and bit.band(bitfield, flag) == flag then + if not output or output == "Comma" then + if prev then + tinsert(text_table, ",") + end + tinsert(text_table, filter_names[private.filter_flags[flag_name]]) + prev = true + -- BBCode + elseif output == "BBCode" then + tinsert(text_table, "[*]" .. filter_names[private.filter_flags[flag_name]]) + end + end + end + end + + if not output or output == "Comma" then + tinsert(text_table, "\",\"") + elseif output == "BBCode" then + tinsert(text_table, "[/list]\nAcquire Methods:\n[list]") + end + + -- Find out which unique acquire methods we have + local acquire_data = recipe["acquire_data"] + twipe(acquire_list) + + for acquire_type in pairs(acquire_data) do + acquire_list[ACQUIRE_NAMES[acquire_type]] = true + end + + -- Add all the acquire methods in + prev = false + + for i in pairs(acquire_list) do + if not output or output == "Comma" then + if prev then + tinsert(text_table, ",") + end + tinsert(text_table, i) + prev = true + elseif output == "BBCode" then + tinsert(text_table, "[*] " .. i) + end + end + + if not output or output == "Comma" then + if is_known then + tinsert(text_table, "\",true\n") + else + tinsert(text_table, "\",false\n") + end + elseif output == "BBCode" then + tinsert(text_table, "\n[/list]") + end + end + end -- for + return tconcat(text_table, "") + end +end -- 1.7.9.5