--[[ *** DataStore_Crafts *** Written by : Thaoky, EU-Marécages de Zangar June 23rd, 2009 --]] if not DataStore then return end local addonName = "DataStore_Crafts" _G[addonName] = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0", "AceComm-3.0", "AceSerializer-3.0", "AceTimer-3.0") local addon = _G[addonName] local THIS_ACCOUNT = "Default" local commPrefix = "DS_Craft" local L = LibStub("AceLocale-3.0"):GetLocale("DataStore_Crafts") local PT = LibStub("LibPeriodicTable-3.1") local MSG_SEND_LOGIN = 1 -- Sends a login message, to request crafts to other players local MSG_LOGIN_REPLY = 2 -- ..reply local MSG_SEND_PROFESSION = 3 -- Sends a profession link, or profession id if no full link local AddonDB_Defaults = { global = { Options = { BroadcastProfs = 1, -- Broadcast professions at login or not }, Guilds = { ['*'] = { -- ["Account.Realm.Name"] Members = { ['*'] = { -- ["MemberName"] lastUpdate = nil, Version = nil, Professions = {}, -- 3 profession links : [1] & [2] for the 2 primary professions, [3] for cooking ([4] for archaeology ? wait & see) } } }, }, Characters = { ['*'] = { -- ["Account.Realm.Name"] lastUpdate = nil, Professions = { ['*'] = { FullLink = nil, -- Tradeskill link NumCrafts = 0, -- total number of crafts for this tradeskill Crafts = { ['*'] = nil }, Cooldowns = { ['*'] = nil }, -- list of active cooldowns } }, } } } } local SPELL_ID_ALCHEMY = 2259 local SPELL_ID_BLACKSMITHING = 3100 local SPELL_ID_ENCHANTING = 7411 local SPELL_ID_ENGINEERING = 4036 local SPELL_ID_INSCRIPTION = 45357 local SPELL_ID_JEWELCRAFTING = 25229 local SPELL_ID_LEATHERWORKING = 2108 local SPELL_ID_TAILORING = 3908 local SPELL_ID_SKINNING = 8613 local SPELL_ID_MINING = 2575 local SPELL_ID_HERBALISM = 13614 local SPELL_ID_SMELTING = 2656 local SPELL_ID_COOKING = 2550 local SPELL_ID_FIRSTAID = 3273 local SPELL_ID_FISHING = 7733 -- CoA custom professions on the Voljin/PTR realm. IDs sourced from -- coa-professionmenu (ProfessionMenu.lua:200-206) and verified against -- db.exil.es: spell 13977860 = "Woodcutting" (single base spell), -- spell 1005008 = Apprentice "Woodworking" (4 ranks 1005008..1005011, -- matching the vanilla Apprentice/Journeyman/Expert/Artisan pattern). -- Apprentice rank is used to mirror the vanilla constants above. local SPELL_ID_WOODCUTTING = 13977860 local SPELL_ID_WOODWORKING = 1005008 local ProfessionSpellID = { -- GetSpellInfo with this value will return localized spell name ["Alchemy"] = SPELL_ID_ALCHEMY, ["Blacksmithing"] = SPELL_ID_BLACKSMITHING, ["Enchanting"] = SPELL_ID_ENCHANTING, ["Engineering"] = SPELL_ID_ENGINEERING, ["Inscription"] = SPELL_ID_INSCRIPTION, ["Jewelcrafting"] = SPELL_ID_JEWELCRAFTING, ["Leatherworking"] = SPELL_ID_LEATHERWORKING, ["Tailoring"] = SPELL_ID_TAILORING, ["Skinning"] = SPELL_ID_SKINNING, ["Mining"] = SPELL_ID_MINING, ["Herbalism"] = SPELL_ID_HERBALISM, ["Smelting"] = SPELL_ID_SMELTING, ["Cooking"] = SPELL_ID_COOKING, ["First Aid"] = SPELL_ID_FIRSTAID, ["Fishing"] = SPELL_ID_FISHING, ["Woodcutting"] = SPELL_ID_WOODCUTTING, ["Woodworking"] = SPELL_ID_WOODWORKING, } -- *** Utility functions *** local function GetOption(option) return addon.db.global.Options[option] end local function GetProfessionID(profession) -- profession = localized profession name "Cooking" or "Cuisine", "Alchemy"... -- note: we're not using a reverse lookup table because of the localization issue. if ProfessionSpellID[profession] then return ProfessionSpellID[profession] end for _, id in pairs( ProfessionSpellID ) do if profession == GetSpellInfo(id) then -- profession found ? ProfessionSpellID[profession] = id -- cache the result to speed up future searches return id end end end local function GetThisGuild() local guild = GetGuildInfo("player") if guild then local key = format("%s.%s.%s", THIS_ACCOUNT, GetRealmName(), guild) return addon.db.global.Guilds[key] end end local function GetVersion() local _, version = GetBuildInfo() return tonumber(version) end local function SaveVersion(sender, version) local thisGuild = GetThisGuild() if thisGuild and sender and version then thisGuild.Members[sender].Version = version end end local function GuildBroadcast(messageType, ...) local serializedData = addon:Serialize(messageType, ...) addon:SendCommMessage(commPrefix, serializedData, "GUILD") end local function GuildWhisper(player, messageType, ...) if DataStore:IsGuildMemberOnline(player) then local serializedData = addon:Serialize(messageType, ...) addon:SendCommMessage(commPrefix, serializedData, "WHISPER", player) end end local professionQueue -- queue containing the professions to send to guildmates local professionTimer -- timer used to control the pace at which professions are place on comm channels. local function SendProfession() if #professionQueue == 0 then -- nothing left in the queue ? cancel the timer & exit addon:CancelTimer(professionTimer) professionTimer = nil return end -- send the last profession found in the queue, then remove it local profession = professionQueue[#professionQueue] -- last element local alt = profession[1] local data = profession[2] local index = profession[3] local recipient = profession[4] -- DEFAULT_CHAT_FRAME:AddMessage(format("sending %s, %s to %s (size : %d)", alt, index, recipient or "guild", #professionQueue )) if profession[4] then -- recipient found ? GuildWhisper(recipient, MSG_SEND_PROFESSION, alt, data, index) else GuildBroadcast(MSG_SEND_PROFESSION, alt, data, index) end table.remove(professionQueue) end local function QueueCharacterProfessions(character, recipient) local index = 1 local _, _, alt = strsplit(".", character) for profName, profession in pairs(addon.db.global.Characters[character].Professions) do local spellID = GetProfessionID(profName) or 0 local data = profession.FullLink or spellID if profession.isPrimary then table.insert(professionQueue, { alt, data, index, recipient }) index = index + 1 elseif profession.isSecondary then if profName == GetSpellInfo(SPELL_ID_COOKING) then table.insert(professionQueue, { alt, data, 3, recipient }) -- index = 3 end end end end local function SendAllProfessions(alts, recipient) if GetOption("BroadcastProfs") == 0 then return end professionQueue = professionQueue or {} -- sends the professions of the current character + his alts local character = DataStore:GetCharacter() -- this character QueueCharacterProfessions(character, recipient) if strlen(alts) > 0 then for _, name in pairs( { strsplit("|", alts) }) do -- then all his alts character = DataStore:GetCharacter(name) if character then QueueCharacterProfessions(character, recipient) end end end -- reuse current timer if already available (may be the case if 2 players connect simultaneously) professionTimer = professionTimer or addon:ScheduleRepeatingTimer(SendProfession, 0.5) -- send 1 profession every half-second, max 15 seconds for 30 professions on a realm end local function SaveProfession(sender, alt, data, index) local thisGuild = GetThisGuild() if thisGuild and sender then local version = thisGuild.Members[sender].Version local member = thisGuild.Members[alt] member.Version = version member.Professions[index] = data member.lastUpdate = time() end addon:SendMessage("DATASTORE_GUILD_PROFESSION_RECEIVED", sender, alt, data, index) end local function ClearExpiredProfessions() -- this function will clear all the guild profession links that were saved with a build number anterior to the current one (they're invalid after a patch anyway) local thisGuild = GetThisGuild() if not thisGuild then return end local version = GetVersion() for name, member in pairs(thisGuild.Members) do if member.Version ~= version then thisGuild.Members[name] = nil -- clear this member's entry if version is outdated end end end local function LocalizeProfessionSpellIDs() -- this function adds localized entries in the ProfessionSpellID table local localizedSpells = {} -- avoid infinite loop by storing in a temp table first local localizedName for englishName, spellID in pairs(ProfessionSpellID) do localizedName = GetSpellInfo(spellID) localizedSpells[localizedName] = spellID end for name, id in pairs(localizedSpells) do ProfessionSpellID[name] = id end end -- *** Scanning functions *** local selectedTradeSkillIndex local subClasses, subClassID local invSlots, invSlotID local haveMats local function GetSubClassID() -- The purpose of this function is to get the subClassID in a UI independant way -- ie: without relying on UIDropDownMenu_GetSelectedID(TradeSkillSubClassDropDown), which uses a hardcoded frame name. if GetTradeSkillSubClassFilter(0) then -- if "All Subclasses" is selected, GetTradeSkillSubClassFilter() will return 1 for all indexes, including 0 return 1 -- thus return 1 as selected id (as would be returned by UIDropDownMenu_GetSelectedID(TradeSkillSubClassDropDown)) end local filter for i = 1, #subClasses do filter = GetTradeSkillSubClassFilter(i) if filter then return i+1 -- ex: 3rd element of the subClasses array, but 4th in the dropdown due to "All Subclasses", so return i+1 end end end local function GetInvSlotID() -- The purpose of this function is to get the invSlotID in a UI independant way (same as GetSubClassID) -- ie: without relying on UIDropDownMenu_GetSelectedID(TradeSkillInvSlotDropDown), which uses a hardcoded frame name. if GetTradeSkillInvSlotFilter(0) then -- if "All Slots" is selected, GetTradeSkillInvSlotFilter() will return 1 for all indexes, including 0 return 1 -- thus return 1 as selected id (as would be returned by UIDropDownMenu_GetSelectedID(TradeSkillInvSlotDropDown)) end local filter for i = 1, #invSlots do filter = GetTradeSkillInvSlotFilter(i) if filter then return i+1 -- ex: 3rd element of the invSlots array, but 4th in the dropdown due to "All Slots", so return i+1 end end end local function SaveActiveFilters() selectedTradeSkillIndex = GetTradeSkillSelectionIndex() subClasses = { GetTradeSkillSubClasses() } invSlots = { GetTradeSkillInvSlots() } subClassID = GetSubClassID() invSlotID = GetInvSlotID() -- Subclasses SetTradeSkillSubClassFilter(0, 1, 1) -- this checks "All subclasses" if TradeSkillSubClassDropDown then UIDropDownMenu_SetSelectedID(TradeSkillSubClassDropDown, 1) end -- Inventory slots SetTradeSkillInvSlotFilter(0, 1, 1) -- this checks "All slots" if TradeSkillInvSlotDropDown then UIDropDownMenu_SetSelectedID(TradeSkillInvSlotDropDown, 1) end -- Have Materials if TradeSkillFrameAvailableFilterCheckButton then haveMats = TradeSkillFrameAvailableFilterCheckButton:GetChecked() -- nil or true TradeSkillFrameAvailableFilterCheckButton:SetChecked(false) end TradeSkillOnlyShowMakeable(false) end local function RestoreActiveFilters() -- Subclasses SetTradeSkillSubClassFilter(subClassID-1, 1, 1) -- this checks the previously checked value local frame = TradeSkillSubClassDropDown if frame then -- other addons might nil this frame (delayed load, etc..), so secure DDM calls local text = (subClassID == 1) and ALL_SUBCLASSES or subClasses[subClassID-1] UIDropDownMenu_SetSelectedID(frame, subClassID) UIDropDownMenu_SetText(frame, text); end subClassID = nil wipe(subClasses) subClasses = nil -- Inventory slots invSlotID = invSlotID or 1 SetTradeSkillInvSlotFilter(invSlotID-1, 1, 1) -- this checks the previously checked value frame = TradeSkillInvSlotDropDown if frame then local text = (invSlotID == 1) and ALL_INVENTORY_SLOTS or invSlots[invSlotID-1] UIDropDownMenu_SetSelectedID(frame, invSlotID) UIDropDownMenu_SetText(frame, text); end invSlotID = nil wipe(invSlots) invSlots = nil -- Have Materials if TradeSkillFrameAvailableFilterCheckButton then TradeSkillFrameAvailableFilterCheckButton:SetChecked(haveMats or false) end TradeSkillOnlyShowMakeable(haveMats or false) haveMats = nil SelectTradeSkill(selectedTradeSkillIndex) selectedTradeSkillIndex = nil end local headersState = {} local function SaveHeaders() local headerCount = 0 -- use a counter to avoid being bound to header names, which might not be unique. for i = GetNumTradeSkills(), 1, -1 do -- 1st pass, expand all categories local _, skillType, _, isExpanded = GetTradeSkillInfo(i) if (skillType == "header") then headerCount = headerCount + 1 if not isExpanded then ExpandTradeSkillSubClass(i) headersState[headerCount] = true end end end end local function RestoreHeaders() local headerCount = 0 for i = GetNumTradeSkills(), 1, -1 do local _, skillType = GetTradeSkillInfo(i) if (skillType == "header") then headerCount = headerCount + 1 if headersState[headerCount] then CollapseTradeSkillSubClass(i) end end end wipe(headersState) end local function ScanCooldowns() local tradeskillName = GetTradeSkillLine() local char = addon.ThisCharacter local profession = char.Professions[tradeskillName] wipe(profession.Cooldowns) for i = 1, GetNumTradeSkills() do local skillName, skillType = GetTradeSkillInfo(i) if skillType ~= "header" then local cooldown = GetTradeSkillCooldown(i) if cooldown then table.insert(profession.Cooldowns, skillName .. "|" .. cooldown .. "|" .. time()) end end end end local function ScanProfessionLinks() local char = addon.ThisCharacter if not char then return end local old_professions = {} -- start listing previously known professions for skillName, v in pairs(char.Professions) do old_professions[skillName] = 1 end for i = GetNumSkillLines(), 1, -1 do -- 1st pass, expand all categories local _, isHeader = GetSkillLineInfo(i) if isHeader then ExpandSkillHeader(i) end end local category for i = 1, GetNumSkillLines() do local skillName, isHeader, _, skillRank, _, _, skillMaxRank = GetSkillLineInfo(i) if isHeader then category = skillName else if category and skillName then local field if category == L["Professions"] then field = "isPrimary" end if category == L["Secondary Skills"] then field = "isSecondary" end if field then char.Professions[skillName][field] = true -- remove a confimed Profession from the "old" list old_professions[skillName] = nil -- should be nil anyway for fishing, mining, etc.. local newLink = select(2, GetSpellLink(skillName)) if newLink then -- sometimes a nil value may be returned, so keep the old one if nil char.Professions[skillName].FullLink = newLink end end end end end -- clear old professions which are no longer known for oldProfession,_ in pairs(old_professions) do wipe( char.Professions[oldProfession] ) -- clean the table first char.Professions[oldProfession] = nil -- remove the entry end end local SkillTypeToColor = { ["header"] = 0, ["optimal"] = 1, -- orange ["medium"] = 2, -- yellow ["easy"] = 3, -- green ["trivial"] = 4, -- grey } local function ScanRecipes() local tradeskillName = GetTradeSkillLine() if not tradeskillName or tradeskillName == "UNKNOWN" then return end -- may happen after a patch, or under extreme lag, so do not save anything to the db ! local char = addon.ThisCharacter local profession = char.Professions[tradeskillName] -- local crafts = profession.Crafts -- wipe(crafts) local crafts = {} local NumCrafts = 0 local NumHeaders = 0 local color, craftInfo, link for i = 1, GetNumTradeSkills() do local skillName, skillType = GetTradeSkillInfo(i) color = SkillTypeToColor[skillType] if skillType == "header" then craftInfo = skillName or "" NumHeaders = NumHeaders + 1 else link = GetTradeSkillRecipeLink(i) craftInfo = tonumber(link:match("enchant:(%d+)")) -- this actually extracts the spellID NumCrafts = NumCrafts + 1 end crafts[i] = color .. "|" .. craftInfo end if NumHeaders > 0 then -- only overwrite stored info if he have seen some headers (indicator that the whole profession is in game cache and the interface is shown in its entirety) profession.FullLink = select(2, GetSpellLink(tradeskillName)) profession.NumCrafts = NumCrafts wipe(profession.Crafts) -- wipe old table (for memory saving) profession.Crafts = crafts -- assign the valid new table else wipe(crafts) -- wipe unused table print(format("|cffFFFF20%s|r:",addonName), "Profession not yet fully loaded.") end end local function ScanTradeSkills() SaveActiveFilters() SaveHeaders() ScanRecipes() ScanCooldowns() RestoreHeaders() RestoreActiveFilters() addon.ThisCharacter.lastUpdate = time() end -- *** Event Handlers *** local function OnPlayerAlive() -- print("DataStore_Crafts.lua") -- DEBUG 2025 07 21 if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard ScanProfessionLinks() end local function OnTradeSkillClose() addon:UnregisterEvent("TRADE_SKILL_CLOSE") addon:UnregisterEvent("TRADE_SKILL_UPDATE") addon.isOpen = nil end local function OnTradeSkillShow() if IsTradeSkillLinked() then return end addon.isOpen = true addon:RegisterEvent("TRADE_SKILL_CLOSE", OnTradeSkillClose) ScanTradeSkills() end local function OnTradeSkillUpdate() -- The hook in DoTradeSkill will register this event so that we only update skills once. -- unregister it before calling the update, or the event will be called recursively (due to expand/collapse) addon:UnregisterEvent("TRADE_SKILL_UPDATE") ScanCooldowns() -- only cooldowns need to be refreshed end -- this turns -- "Your skill in %s has increased to %d." -- into -- "Your skill in (.+) has increased to (%d+)." local arg1pattern, arg2pattern if GetLocale() == "deDE" then -- ERR_SKILL_UP_SI = "Eure Fertigkeit '%1$s' hat sich auf %2$d erhöht."; arg1pattern = "'%%1%$s'" arg2pattern = "%%2%$d" else arg1pattern = "%%s" arg2pattern = "%%d" end local skillUpMsg = gsub(ERR_SKILL_UP_SI, arg1pattern, "(.+)") skillUpMsg = gsub(skillUpMsg, arg2pattern, "(%%d+)") local function OnChatMsgSkill(self, msg) if msg and addon.isOpen then -- point gained while ts window is open ? rescan local skill = msg:match(skillUpMsg) if skill and skill == GetTradeSkillLine() then -- if we gained a skill point in the currently opened profession pane, rescan ScanTradeSkills() end end end -- ** Mixins ** local function _GetProfession(character, name) return character.Professions[name] end local function _GetProfessions(character) return character.Professions end local function _GetProfessionInfo(profession) -- accepts either a pointer (type == table)to the profession table, as returned by addon:GetProfession() -- or a link (type == string) local link if type(profession) == "table" then link = profession.FullLink elseif type(profession) == "string" then link = profession end if link then local spellID, currentLevel, maxLevel = link:match("trade:(%d+):(%d+):(%d+):") return tonumber(currentLevel), tonumber(maxLevel), tonumber(spellID) end end local function _GetNumCraftLines(profession) -- CoA: profession is nil for an unscanned/custom profession on a partial-data alt; -- callers use this as a numeric `for` limit, so return 0 instead of crashing on #nil.Crafts if type(profession) ~= "table" or type(profession.Crafts) ~= "table" then return 0 end return #profession.Crafts end local function _GetCraftLineInfo(profession, index) local craft = profession.Crafts[index] local color, info = strsplit("|", craft) color = tonumber(color) if color ~= 0 then -- not a header ? info = tonumber(info) -- it's a spellID, return a number end return (color == 0), color, info -- 1st return value = isHeader end local function _GetCraftCooldownInfo(profession, index) local cooldown = profession.Cooldowns[index] local name, reset, lastCheck = strsplit("|", cooldown) reset = tonumber(reset) lastCheck = tonumber(lastCheck) local expiresIn = reset - (time() - lastCheck) return name, expiresIn, reset, lastCheck end local function _GetNumActiveCooldowns(profession) assert(type(profession) == "table") -- this is the pointer to a profession table, obtained through addon:GetProfession() return #profession.Cooldowns end local function _ClearExpiredCooldowns(profession) assert(type(profession) == "table") -- this is the pointer to a profession table, obtained through addon:GetProfession() for i = #profession.Cooldowns, 1, -1 do -- from last to first, to avoid messing up indexes when removing entries local _, expiresIn = _GetCraftCooldownInfo(profession, i) if expiresIn <= 0 then -- already expired ? remove it table.remove(profession.Cooldowns, i) end end end local function _GetCraftInfo(spellID) -- get the id of the item that can be crafted by this spellID local itemID = PT:ItemInSet("-"..spellID, "Tradeskill.RecipeLinks") local reagents if itemID then itemID = tonumber(itemID) -- ex: itemID 10046 is made with reagents : "2996x2;2318x1;2320x1" reagents = PT:ItemInSet(itemID, "TradeskillResultMats.Forward") if itemID == -spellID then -- enchants that do not yield an item will return this, ex: enchant 7420 will return itemID -7420 itemID = nil end end return itemID, reagents end local function _GetCraftLevels(spellID) -- get the id of the item that can be crafted by this spellID local itemID = PT:ItemInSet("-"..spellID, "Tradeskill.RecipeLinks") if itemID then itemID = tonumber(itemID) -- ex: itemID 10046 : levels = "20/50/67/85", the item turns yellow at 50, green at 67, grey at 85 local levels = PT:ItemInSet(itemID, "TradeskillLevels") if levels then local orange, yellow, green, grey = strsplit("/", levels) return tonumber(orange), tonumber(yellow), tonumber(green), tonumber(grey) end end end local function _GetNumRecipesByColor(profession) -- counts the number of orange, yellow, green and grey recipes. local counts = { 0, 0, 0, 0 } for i = 1, _GetNumCraftLines(profession) do local isHeader, color = _GetCraftLineInfo(profession, i) if not isHeader and color and counts[color] then -- CoA: custom-profession craft lines can carry an out-of-range/nil color counts[color] = counts[color] + 1 end end return counts[1], counts[2], counts[3], counts[4] -- orange, yellow, green, grey end local function _IsCraftKnown(profession, spellID) -- returns true if a given spell ID is known in the profession passed as first argument for i = 1, _GetNumCraftLines(profession) do local isHeader, _, info = _GetCraftLineInfo(profession, i) if not isHeader then if info == spellID then return true end end end end local function _GetGuildCrafters(guild) return guild.Members end local function _GetGuildMemberProfession(guild, member, index) local m = guild.Members[member] local profession = m.Professions[index] if type(profession) == "string" then local spellID = profession:match("trade:(%d+):") return tonumber(spellID), profession, m.lastUpdate -- return the profession spell ID + full link elseif type(profession) == "number" then return profession, nil, m.lastUpdate -- return the profession spell ID end end local function _GetProfessionSpellID(name) -- name can be either the english name or the localized name return ProfessionSpellID[name] end local PublicMethods = { GetProfession = _GetProfession, GetProfessions = _GetProfessions, GetProfessionInfo = _GetProfessionInfo, GetNumCraftLines = _GetNumCraftLines, GetCraftLineInfo = _GetCraftLineInfo, GetCraftCooldownInfo = _GetCraftCooldownInfo, GetNumActiveCooldowns = _GetNumActiveCooldowns, ClearExpiredCooldowns = _ClearExpiredCooldowns, GetCraftInfo = _GetCraftInfo, GetCraftLevels = _GetCraftLevels, GetNumRecipesByColor = _GetNumRecipesByColor, IsCraftKnown = _IsCraftKnown, GetGuildCrafters = _GetGuildCrafters, GetGuildMemberProfession = _GetGuildMemberProfession, GetProfessionSpellID = _GetProfessionSpellID, } -- *** Guild Comm *** local function OnGuildAltsReceived(self, sender, alts) if sender == UnitName("player") then -- if I receive my own list of alts in the same guild, same realm, same account.. GuildBroadcast(MSG_SEND_LOGIN, GetVersion()) addon:ScheduleTimer(SendAllProfessions, 5, alts) -- broadcast my crafts to the guild 5 seconds later, to decrease the load at startup end end local GuildCommCallbacks = { [MSG_SEND_LOGIN] = function(sender, version) local player = UnitName("player") if sender ~= player then -- don't send back to self GuildWhisper(sender, MSG_LOGIN_REPLY, GetVersion()) local alts = DataStore:GetGuildMemberAlts(player) -- get my own alts if alts then SendAllProfessions(alts, sender) -- when another player sends me his login, reply with my own crafts end end SaveVersion(sender, version) end, [MSG_LOGIN_REPLY] = function(sender, version) SaveVersion(sender, version) end, [MSG_SEND_PROFESSION] = function(sender, alt, data, index) SaveProfession(sender, alt, data, index) end, } function addon:OnInitialize() addon.db = LibStub("AceDB-3.0"):New(addonName .. "DB", AddonDB_Defaults) DataStore:RegisterModule(addonName, addon, PublicMethods) DataStore:SetGuildCommCallbacks(commPrefix, GuildCommCallbacks) DataStore:SetCharacterBasedMethod("GetProfession") DataStore:SetCharacterBasedMethod("GetProfessions") DataStore:SetGuildBasedMethod("GetGuildCrafters") DataStore:SetGuildBasedMethod("GetGuildMemberProfession") addon:RegisterMessage("DATASTORE_GUILD_ALTS_RECEIVED", OnGuildAltsReceived) addon:RegisterComm(commPrefix, DataStore:GetGuildCommHandler()) end function addon:OnEnable() addon:RegisterEvent("PLAYER_ALIVE", OnPlayerAlive) addon:RegisterEvent("TRADE_SKILL_SHOW", OnTradeSkillShow) addon:RegisterEvent("CHAT_MSG_SKILL", OnChatMsgSkill) addon:SetupOptions() ClearExpiredProfessions() -- automatically cleanup guild profession links that are from an older version LocalizeProfessionSpellIDs() end function addon:OnDisable() addon:UnregisterEvent("PLAYER_ALIVE") addon:UnregisterEvent("TRADE_SKILL_SHOW") addon:UnregisterEvent("CHAT_MSG_SKILL") end function addon:GetSource(searchedID) local PT = LibStub("LibPeriodicTable-3.1") -- Returns "Profession, level" ex: "Alchemy", "180" local level, data = PT:ItemInSet(searchedID, "Tradeskill.Crafted") if level and data then local _, _, profession = strsplit(".", data) -- ex: "Tradeskill.Crafted.Inscription" local localizedProfession if ProfessionSpellID[profession] then localizedProfession = GetSpellInfo(ProfessionSpellID[profession]) end return localizedProfession or profession, level end end function addon:IsTradeSkillWindowOpen() -- note : maybe there's a function in the WoW API to test this, but I did not find it :( return addon.isOpen end -- *** Hooks *** -- todo : change the hooks, do them the Ace way local Orig_DoTradeSkill = DoTradeSkill function DoTradeSkill(index, repeatCount, ...) -- this hook is necessary to get cooldown information after a craft Orig_DoTradeSkill(index, repeatCount, ...) addon:RegisterEvent("TRADE_SKILL_UPDATE", OnTradeSkillUpdate) end local Orig_AbandonSkill = AbandonSkill function AbandonSkill(index, ...) local skillName = GetSkillLineInfo(index) -- get the name of the profession that is being abandonned Orig_AbandonSkill(index, ...) -- clear the list of recipes local char = addon.ThisCharacter wipe(char.Professions[skillName]) char.Professions[skillName] = nil end