bbe2492a5b
Each DataStore_* / Altoholic_* addon now lives at the repo root, matching the Exiles fork-layout convention (one folder per addon, no wrapper dir).
352 lines
13 KiB
Lua
352 lines
13 KiB
Lua
local MAJOR = "LibTradeLinks-1.0";
|
|
local MINOR = "9902";
|
|
|
|
local LibTradeLinks, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
|
|
|
if not LibTradeLinks then return end -- no upgrade needed
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Constant skill line IDs --
|
|
--------------------------------------------------------------------------------
|
|
|
|
LibTradeLinks.SKILL_ALCHEMY = 171;
|
|
LibTradeLinks.SKILL_BLACKSMITHING = 164;
|
|
LibTradeLinks.SKILL_COOKING = 185;
|
|
LibTradeLinks.SKILL_ENCHANTING = 333;
|
|
LibTradeLinks.SKILL_ENGINEERING = 202;
|
|
LibTradeLinks.SKILL_FIRSTAID = 129;
|
|
LibTradeLinks.SKILL_JEWELCRAFTING = 755;
|
|
LibTradeLinks.SKILL_LEATHERWORKING = 165;
|
|
LibTradeLinks.SKILL_MINING = 186;
|
|
LibTradeLinks.SKILL_TAILORING = 197;
|
|
LibTradeLinks.SKILL_INSCRIPTION = 773;
|
|
|
|
local allSkills = {
|
|
LibTradeLinks.SKILL_ALCHEMY,
|
|
LibTradeLinks.SKILL_BLACKSMITHING,
|
|
LibTradeLinks.SKILL_COOKING,
|
|
LibTradeLinks.SKILL_ENCHANTING,
|
|
LibTradeLinks.SKILL_ENGINEERING,
|
|
LibTradeLinks.SKILL_FIRSTAID,
|
|
LibTradeLinks.SKILL_JEWELCRAFTING,
|
|
LibTradeLinks.SKILL_LEATHERWORKING,
|
|
LibTradeLinks.SKILL_MINING,
|
|
LibTradeLinks.SKILL_TAILORING,
|
|
LibTradeLinks.SKILL_INSCRIPTION,
|
|
};
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- --
|
|
--------------------------------------------------------------------------------
|
|
|
|
local _G = _G;
|
|
|
|
-- Lua/bitlib Functions
|
|
local next = _G.next;
|
|
local max = _G.math.max;
|
|
local floor = _G.math.floor;
|
|
local strchar = _G.string.char;
|
|
local strbyte = _G.string.byte;
|
|
local bor = _G.bit.bor;
|
|
local blshift = _G.bit.lshift;
|
|
local band = _G.bit.band;
|
|
|
|
-- WoW API Functions
|
|
local GetSpellInfo = _G.GetSpellInfo;
|
|
local GetSpellLink = _G.GetSpellLink;
|
|
local UnitGUID = _G.UnitGUID;
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- --
|
|
--------------------------------------------------------------------------------
|
|
local Data = { };
|
|
local BlackList = { };
|
|
local BoPList = { };
|
|
local Builds = { };
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Base 64 decode --
|
|
--------------------------------------------------------------------------------
|
|
|
|
local Base64MatchString = "[A-Za-z0-9+/]";
|
|
local base64chars = {
|
|
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
|
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
|
|
'0','1','2','3','4','5','6','7','8','9','+','/'
|
|
};
|
|
local base64values = {
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
|
-1, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14,
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
|
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1};
|
|
|
|
local function DecodeBase64Char(c)
|
|
c = strbyte(c);
|
|
if c < 0 or c > 127 then
|
|
return -1;
|
|
end
|
|
return base64values[c + 1];
|
|
end
|
|
|
|
local function EncodeBase64Char(val)
|
|
if val < 0 or val > 63 then
|
|
return '-';
|
|
else
|
|
return base64chars[val + 1];
|
|
end
|
|
end
|
|
|
|
local function getBuild(buildNumber)
|
|
return tonumber(buildNumber) or tonumber((select(2, GetBuildInfo())));
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Register a build number --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:RegisterBuild(buildVersion)
|
|
Builds[#Builds + 1] = buildVersion;
|
|
table.sort(Builds, function(a,b) return b < a end);
|
|
end
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Return the build versions for which the trade data is valid --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:GetBuildVersion()
|
|
return unpack(Builds);
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Set the black list containing non-recipe spellIds --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:SetBlackList(buildNumber, blackList)
|
|
BlackList[buildNumber] = blackList;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Set the black list containing non-recipe spellIds --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:SetBoPList(buildNumber, bopList)
|
|
BoPList[buildNumber] = bopList;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Add a skill to the list, where professionIdList is a table of spellIds for --
|
|
-- the profession to add, and data is a table containing spellId in the order --
|
|
-- as they appear in the trade-link bitmask. --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:AddData(buildNumber, skillId, professionIdList, spellIdList)
|
|
if Data[skillId] then
|
|
Data[skillId].Data[buildNumber] = spellIdList;
|
|
for _, id in next, professionIdList do Data[skillId].ProfessionIdList[id] = id; end
|
|
else
|
|
local idList = {};
|
|
for _, id in next, professionIdList do idList[id] = id; end
|
|
Data[skillId] = {
|
|
SkillId = skillId,
|
|
ProfessionIdList = idList,
|
|
MainProfessionId = professionIdList[1],
|
|
Label = GetSpellInfo(professionIdList[1]),
|
|
Data = { [buildNumber] = spellIdList, },
|
|
};
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Return the table containing spellIds for the specified skill id --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:GetData(skillId, buildNumber)
|
|
return Data[skillId] and Data[skillId].Data[getBuild(buildNumber)];
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Return a table containing all skillIds --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:GetSkillIds()
|
|
return allSkills;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Return the name of the specified skillId --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:GetSkillName(skillId)
|
|
return Data[skillId] and Data[skillId].Label;
|
|
end
|
|
--------------------------------------------------------------------------------
|
|
-- Returna s Base64 encoded bitmask, represented by the table t. Function --
|
|
-- func is called for each entry in t, to determine if that bit is 1 or 0. --
|
|
--------------------------------------------------------------------------------
|
|
local function Encode64(t, func)
|
|
local str = "";
|
|
local char = 0;
|
|
for index, data in next, t do
|
|
local b = (index - 1) % 6;
|
|
if b == 0 and index ~= 1 then
|
|
str = str .. EncodeBase64Char(char);
|
|
char = 0;
|
|
end
|
|
|
|
if func(data) then
|
|
char = bor(char, blshift(1, b));
|
|
end
|
|
end
|
|
return str .. EncodeBase64Char(char);
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Encode a trade link for the specified skill and list of spells, return --
|
|
-- a complete chat trade-link as 1st return value, and the trade-link as 2nd. --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:Encode(skillId, spells, skillLevel, skillMax, GUID)
|
|
local data = self:GetData(skillId);
|
|
if not data then return nil, nil; end
|
|
local professionSpellId = Data[skillId].MainProfessionId;
|
|
|
|
local encoded = ("trade:%d:%d:%d:%s:%s"):format(
|
|
professionSpellId,
|
|
skillLevel or 450,
|
|
skillMax or 450,
|
|
(GUID or UnitGUID("player")):gsub("0x0+", ""),
|
|
Encode64(data, function(id) return spells[id] end)
|
|
);
|
|
|
|
local link = ("|cffffd000|H%s|h[%s]|h|r"):format(
|
|
encoded,
|
|
GetSpellInfo(professionSpellId)
|
|
);
|
|
|
|
return link, encoded;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Decode a base64 encoded bitmask, looking up bits set to 1 in the table t. --
|
|
-- A table containing the entries set to 1 is returned. --
|
|
--------------------------------------------------------------------------------
|
|
local function Decode64(str, t, out)
|
|
out = out or {};
|
|
local offset = 0;
|
|
str:gsub(".", function(c)
|
|
local v = DecodeBase64Char(c);
|
|
for i = 0, 5 do
|
|
if band(v, blshift(1, i)) ~= 0 then
|
|
out[t[offset * 6 + i + 1]] = 1;
|
|
end
|
|
end
|
|
offset = offset + 1;
|
|
end);
|
|
return out;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Decode a trade-link and return a table of contained spell Ids --
|
|
-- If purge is specified, black-listed spells are purged from the list --
|
|
-- Returns: --
|
|
-- 1: table of spellIds --
|
|
-- 2: skillId --
|
|
-- 3: true if data for current build used, false otherwise --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:Decode(link, purgeNonRecipe, purgeBoP, buildNumber)
|
|
assert(type(link) == "string", "Error, LibTradeLinks:Decode() requires a string as first argument");
|
|
|
|
local profession, guid, skills = link:match("trade:(%d+):%d+:%d+:([0-9a-fA-F]+):(" .. Base64MatchString .. "+)");
|
|
if skills then
|
|
profession = tonumber(profession);
|
|
for skillId, data in next, Data do
|
|
if data.ProfessionIdList[profession] then
|
|
return self:Purge(Decode64(skills, self:GetData(skillId, buildNumber)), purgeNonRecipe, purgeBoP, buildNumber), skillId, getBuild() == getBuild(buildNumber);
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Decode a trade-link, and write to ChatFrame1 a list of all spells linked. --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:DecodeOut(link, purgeNonRecipe, purgeBoP, buildNumber)
|
|
local spells = self:Decode(link, purgeNonRecipe, purgeBoP, buildNumber);
|
|
if spells then
|
|
ChatFrame1:AddMessage(link .. " contains:");
|
|
local out = "";
|
|
local count = 0;
|
|
for spellId, _ in next, spells do
|
|
count = count + 1;
|
|
out = out .. GetSpellLink(spellId) .. "; ";
|
|
if count == 5 then
|
|
ChatFrame1:AddMessage(out);
|
|
out = "";
|
|
count = 0;
|
|
end
|
|
end
|
|
if out ~= "" then
|
|
ChatFrame1:AddMessage(out);
|
|
end
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Purge the specified list of spells for non-recipe spells and recipees that --
|
|
-- create BoP items. --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:Purge(spells, purgeNonRecipe, purgeBoP, buildNumber)
|
|
assert(type(spells) == "table", "Error, LibTradeLinks:Purge() requires a table as first argument");
|
|
buildNumber = getBuild(buildNumber);
|
|
|
|
if purgeNonRecipe and BlackList[buildNumber] then
|
|
for _, spellId in next, BlackList[buildNumber] do
|
|
spells[spellId] = nil;
|
|
end
|
|
end
|
|
|
|
if purgeBoP and BoPList[buildNumber] then
|
|
for _, spellId in next, BoPList[buildNumber] do
|
|
spells[spellId] = nil;
|
|
end
|
|
end
|
|
|
|
return spells;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Returns the specialization spells for the passed skillId --
|
|
-- If a trade-link is passed, returns the specialization spells contained --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:GetSpecializationSpells(skillId, buildNumber)
|
|
if type(skillId) == "string" then
|
|
local spells, skillId = self:Decode(skillId, nil, nil, buildNumber);
|
|
if not spells then
|
|
return;
|
|
end
|
|
|
|
local list = Data[skillId].ProfessionIdList;
|
|
for id, _ in next, spells do
|
|
if not list[id] then
|
|
list[id] = nil;
|
|
end
|
|
end
|
|
|
|
return list;
|
|
end
|
|
|
|
assert(type(skillId) == "number" and Data[skillId], "LibTradeLinks:GetSpecializationSpells() Requires a valid skill ID or tradelink as argument");
|
|
return Data[skillId].ProfessionIdList;
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Returns the skillId for the passed profession spell id, or nil --
|
|
--------------------------------------------------------------------------------
|
|
function LibTradeLinks:GetSkillId(professionSpell)
|
|
if type(professionSpell) == "string" then
|
|
professionSpell = tonumber(professionSpell:match("trade:(%d+):%d+:%d+:"));
|
|
end
|
|
assert(type(professionSpell) == "number", "LibTradeLinks:GetSkillId() requires a numerical spell id or a trade-link as argument");
|
|
for skillId, data in next, Data do
|
|
if data.ProfessionIdList[professionSpell] then
|
|
return skillId;
|
|
end
|
|
end
|
|
end
|