--[[ ************************************************************************ core.lua ************************************************************************ File date: @file-date-iso@ File hash: @file-abbreviated-hash@ Project hash: @project-abbreviated-hash@ Project version: @project-version@ ************************************************************************ Please see http://www.wowace.com/addons/arl/ for more information. ************************************************************************ This source code is released under All Rights Reserved. ************************************************************************ @class file @name core.lua @release 2.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 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 ------------------------------------------------------------------------------- -- Define the static popups we're going to call when people haven't scanned or -- when current filters are blocking all recipes from being displayed. ------------------------------------------------------------------------------- StaticPopupDialogs["ARL_NOTSCANNED"] = { text = L["NOTSCANNED"], button1 = _G.OKAY, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1 } StaticPopupDialogs["ARL_ALLFILTERED"] = { text = L["ALL_FILTERED"], button1 = _G.OKAY, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1 } StaticPopupDialogs["ARL_ALLKNOWN"] = { text = L["ARL_ALLKNOWN"], button1 = _G.OKAY, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1 } StaticPopupDialogs["ARL_ALLEXCLUDED"] = { text = L["ARL_ALLEXCLUDED"], button1 = _G.OKAY, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1 } StaticPopupDialogs["ARL_SEARCHFILTERED"] = { text = L["ARL_SEARCHFILTERED"], button1 = _G.OKAY, timeout = 0, exclusive = 1, whileDead = 1, hideOnEscape = 1 } ------------------------------------------------------------------------------- -- Close all possible pop-up windows ------------------------------------------------------------------------------- function addon:ClosePopups() _G.StaticPopup_Hide("ARL_NOTSCANNED") _G.StaticPopup_Hide("ARL_ALLFILTERED") _G.StaticPopup_Hide("ARL_ALLKNOWN") _G.StaticPopup_Hide("ARL_ALLEXCLUDED") _G.StaticPopup_Hide("ARL_SEARCHFILTERED") end ------------------------------------------------------------------------------- -- Functions common to most files in the AddOn. ------------------------------------------------------------------------------- function private.SetTextColor(color_code, text) return string.format("|cff%s%s|r", color_code or "ffffff", text) end ------------------------------------------------------------------------------- -- Debugger. ------------------------------------------------------------------------------- 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) table.wipe(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\\<Account Name>>\\SavedVariables\\") return end local version = GetAddOnMetadata("AckisRecipeList", "Version") -- @alpha@ version = "Devel" -- @end-alpha@ self.version = 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 MainPanel = addon.Frame local prev_profession if MainPanel then prev_profession = MainPanel.prof_name or private.ordered_professions[MainPanel.profession] end 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 MainPanel and MainPanel:IsVisible() and prev_profession == cur_profession then MainPanel:Hide() else addon:Scan(false) addon:AddWaypoint() end end end) scan_button:SetScript("OnEnter", function(self) GameTooltip_SetDefaultAnchor(GameTooltip, self) 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 ------------------------------------------------------------------------------- -- 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() if addon.Frame then addon.Frame:Hide() end -- 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 self.Frame and 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 or 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 methods. ------------------------------------------------------------------------------- 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 local SKILL_LEVEL_FORMAT = "[%d]" local SPELL_ENCHANTING = GetSpellInfo(51313) local function Recipe_GetDisplayName(recipe_entry) local _, _, _, quality_color = GetItemQualityColor(recipe_entry.quality) local recipe_name = recipe_entry.name if private.ordered_professions[addon.Frame.profession] == SPELL_ENCHANTING then recipe_name = string.gsub(recipe_name, _G.ENSCRIBE.." ", "") end local recipe_string = string.format("%s%s|r", quality_color, recipe_name) local skill_level = Player["ProfessionLevel"] local recipe_level = recipe_entry.skill_level local has_faction = Player:HasProperRepLevel(recipe_entry.acquire_data[A.REPUTATION]) local diff_color if not has_faction or recipe_level > skill_level then diff_color = "impossible" elseif skill_level >= recipe_entry.trivial_level then diff_color = "trivial" elseif skill_level >= recipe_entry.easy_level then diff_color = "easy" elseif skill_level >= recipe_entry.medium_level then diff_color = "medium" elseif skill_level >= recipe_entry.optimal_level then diff_color = "optimal" else --@debug@ addon:Debug("Skill level color fallback: %s.", recipe_string) --@end-debug@ diff_color = "trivial" end local level_text = string.format(private.SetTextColor(private.difficulty_colors[diff_color], SKILL_LEVEL_FORMAT), recipe_level) local skill_view = addon.db.profile.skill_view recipe_string = skill_view and string.format("%s - %s", level_text, recipe_string) or string.format("%s - %s", recipe_string, level_text) if addon.db.profile.exclusionlist[recipe_entry.spell_id] then recipe_string = string.format("** %s **", recipe_string) end return recipe_string 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) or ("%s: %d"):format(_G.UNKNOWN, tonumber(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, ["GetDisplayName"] = Recipe_GetDisplayName, } 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 ------------------------------------------------------------------------------- -- 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 = GetSpellBookItemName(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 ------------------------------------------------------------------------------- table.wipe(header_list) -- Save the state of the "Have Materials" checkbox. local have_materials = TradeSkillFrame.filterTbl.hasMaterials if MRTUIUtils_PushFilterSelection then MRTUIUtils_PushFilterSelection() else if not Skillet and have_materials then TradeSkillFrame.filterTbl.hasMaterials = false TradeSkillOnlyShowMakeable(false) end UIDropDownMenu_Initialize(TradeSkillFilterDropDown, TradeSkillInvSlotDropDown_Initialize) UIDropDownMenu_SetSelectedID(TradeSkillFilterDropDown, 1) SetTradeSkillInvSlotFilter(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. TradeSkillFrame.filterTbl.hasMaterials = 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 if private.InitializeFrame then private.InitializeFrame() end self.Frame:Display(current_prof, is_linked) end end end ------------------------------------------------------------------------------- -- Text dumping functions ------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ---- Creates a new frame with the contents of a text dump so you can copy and paste --- Code borrowed from Antiarc (Chatter) with permission --- @name AckisRecipeList:DisplayTextDump --- @param RecipeDB The database (array) which you wish read data from. --- @param profession Which profession are you displaying data for --- @param text The text to be dumped -------------------------------------------------------------------------------- do local copy_frame = CreateFrame("Frame", "ARLCopyFrame", UIParent) copy_frame:SetBackdrop({ bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], edgeFile = [[Interface\DialogFrame\UI-DialogBox-Border]], tile = true, tileSize = 16, edgeSize = 16, insets = { left = 3, right = 3, top = 5, bottom = 3 } }) copy_frame:SetBackdropColor(0, 0, 0, 1) copy_frame:SetWidth(750) copy_frame:SetHeight(400) copy_frame:SetPoint("CENTER", UIParent, "CENTER") copy_frame:SetFrameStrata("DIALOG") table.insert(UISpecialFrames, "ARLCopyFrame") local scrollArea = CreateFrame("ScrollFrame", "ARLCopyScroll", copy_frame, "UIPanelScrollFrameTemplate") scrollArea:SetPoint("TOPLEFT", copy_frame, "TOPLEFT", 8, -30) scrollArea:SetPoint("BOTTOMRIGHT", copy_frame, "BOTTOMRIGHT", -30, 8) local edit_box = CreateFrame("EditBox", nil, copy_frame) edit_box:SetMultiLine(true) edit_box:SetMaxLetters(0) edit_box:EnableMouse(true) edit_box:SetAutoFocus(true) edit_box:SetFontObject(ChatFontNormal) edit_box:SetWidth(650) edit_box:SetHeight(270) edit_box:SetScript("OnEscapePressed", function() copy_frame:Hide() end) edit_box:HighlightText(0) scrollArea:SetScrollChild(edit_box) local close = CreateFrame("Button", nil, copy_frame, "UIPanelCloseButton") close:SetPoint("TOPRIGHT", copy_frame, "TOPRIGHT") copy_frame:Hide() function addon:DisplayTextDump(RecipeDB, profession, text) local text = (not RecipeDB and not profession) and text or self:GetTextDump(profession) if text ~= "" then edit_box:SetText(text) edit_box:HighlightText(0) edit_box:SetCursorPosition(1) copy_frame:Show() end end end -- do do ------------------------------------------------------------------------------- -- Dumps recipe output in the format requested by the user ------------------------------------------------------------------------------- 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 (or machines) function addon:GetTextDump(profession) local output = addon.db.profile.textdumpformat table.wipe(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)) elseif output == "XML" then tinsert(text_table, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>") 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 --XML elseif output == "XML" then tinsert(text_table, "<recipe>") tinsert(text_table, " <id>..recipe_id..</id>") tinsert(text_table, " <name>"..recipe.name.."</name>") tinsert(text_table, " <skilllevel>..recipe.skill_level..</skilllevel>") tinsert(text_table, " <known>"..tostring(is_known).."</known>") tinsert(text_table, " <flags>") --Name elseif output == "Name" then tinsert(text_table, recipe.name) 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]]) elseif output == "XML" then tinsert(text_table, " <flag>"..filter_names[private.filter_flags[flag_name]].."</flag>") 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]") elseif output == "XML" then tinsert(text_table, " </flags>") tinsert(text_table, " <acquire>") end -- Find out which unique acquire methods we have local acquire_data = recipe["acquire_data"] table.wipe(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) elseif output == "XML" then tinsert(text_table, "<acquiremethod>"..i.."</acquiremethod>") end end if not output or output == "Comma" then tinsert(text_table, "\","..tostring(is_known).."\n") --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]") elseif output == "XML" then tinsert(text_table, " </acquire>") tinsert(text_table, "</recipe>") end end end -- for return tconcat(text_table, "") end end