vanilla-wow-addons – Rev 1
?pathlinks?
--[[
Enchantrix Addon for World of Warcraft(tm).
Version: 3.6.1 (Platypus)
Revision: $Id: EnxStorage.lua 899 2006-06-05 11:03:30Z aradan $
Database functions and saved variables.
License:
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program(see GLP.txt); if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
]]
-- Global functions
local addonLoaded -- Enchantrix.Storage.AddonLoaded()
local saveDisenchant -- Enchantrix.Storage.SaveDisenchant()
local getItemDisenchants -- Enchantrix.Storage.GetItemDisenchants()
-- Local functions
local unserialize
local serialize
local mergeDisenchant
local normalizeDisenchant
local cleanupDisenchant
local disenchantTotal
local disenchantListHash
local mergeDisenchantLists
local saveLocal
local getLocal
-- Database
local LocalBaseItems = {} -- EnchantedLocal merged by item id
local EnchantedItemTypes = {} -- LocalBaseItems and EnchantedBaseItems merged by type
local N_DISENCHANTS = 1
local N_REAGENTS = 2
Enchantrix.State.MAX_ITEM_ID = 26000
local const = Enchantrix.Constants
function addonLoaded()
-- Create and setup saved variables
if not EnchantConfig then EnchantConfig = {} end
if not EnchantConfig.filters then EnchantConfig.filters = {} end
if not EnchantConfigChar then EnchantConfigChar = {} end
if not EnchantConfigChar.filters then EnchantConfigChar.filters = {} end
if not EnchantConfig.cache then EnchantConfig.cache = {} end
if not EnchantConfig.cache.reagentinfo then EnchantConfig.cache.reagentinfo = {} end
if not EnchantConfig.cache.names then EnchantConfig.cache.names = {} end
if not EnchantedLocal then EnchantedLocal = {} end
if not EnchantedBaseItems then EnchantedBaseItems = {} end
if not EnchantConfig.zomgBlizzardAreMeanies then
-- Push disenchant reagents into cache if needed
for id in Enchantrix.Constants.StaticPrices do
if not (Enchantrix.Util.GetReagentInfo(id)) then
EnchantConfig.zomgBlizzardAreMeanies = true
GameTooltip:SetHyperlink(string.format("item:%d:0:0:0", id))
GameTooltip:Hide()
EnchantConfig.zomgBlizzardAreMeanies = nil
end
end
else
-- We were kicked from the server last time, better not try that again for a while
EnchantConfig.zomgBlizzardAreMeanies = nil
end
mergeDisenchantLists()
end
function unserialize(str)
-- Break up a disenchant string to a table for easy manipulation
local tbl = {}
if type(str) == "string" then
for de in Enchantrix.Util.Spliterator(str, ";") do
local _, _, id, d, r = string.find(de, "(%d+):(%d+):(%d+)")
id, d, r = tonumber(id), tonumber(d), tonumber(r)
if (id and d > 0 and r > 0) then
tbl[id] = {[N_DISENCHANTS] = d, [N_REAGENTS] = r}
end
end
end
return tbl
end
function serialize(tbl)
-- Serialize a table into a string
if type(tbl) == "table" then
local str
for id, counts in pairs(tbl) do
if (type(id) == "number" and counts[N_DISENCHANTS] > 0 and counts[N_REAGENTS] > 0) then
if (str) then
str = string.format("%s;%d:%d:%d:0", str, id, counts[N_DISENCHANTS], counts[N_REAGENTS])
else
str = string.format("%d:%d:%d:0", id, counts[N_DISENCHANTS], counts[N_REAGENTS])
end
end
end
return str
end
end
function mergeDisenchant(str1, str2)
-- Merge two disenchant strings into a single string
local tbl1, tbl2 = unserialize(str1), unserialize(str2)
for id, counts in pairs(tbl2) do
if (not tbl1[id]) then
tbl1[id] = counts
else
tbl1[id][N_DISENCHANTS] = tbl1[id][N_DISENCHANTS] + counts[N_DISENCHANTS]
tbl1[id][N_REAGENTS] = tbl1[id][N_REAGENTS] + counts[N_REAGENTS]
end
end
return serialize(tbl1)
end
function normalizeDisenchant(str)
-- Divide all counts in disenchant string by gcd
local div = 0
local count = 0
local tbl = unserialize(str)
for id, counts in pairs(tbl) do
div = Enchantrix.Util.GCD(div, counts[N_DISENCHANTS])
div = Enchantrix.Util.GCD(div, counts[N_REAGENTS])
count = count + 1
end
-- Only normalize if there's more than one kind of reagent
if count > 1 then
for id, counts in pairs(tbl) do
counts[N_DISENCHANTS] = counts[N_DISENCHANTS] / div
counts[N_REAGENTS] = counts[N_REAGENTS] / div
end
return serialize(tbl)
end
return str
end
function cleanupDisenchant(str, id)
-- Remove reagents that don't appear in level rules table
if (type(str) == "string") and (id ~= nil) then
local _, _, quality, level, _, _, _, equip = GetItemInfo(id)
local itype = const.InventoryTypes[equip]
if quality and itype then
local tbl = unserialize(str)
local clean = {}
level = Enchantrix.Util.RoundUp(level, 5)
for id, counts in pairs(tbl) do
if const.LevelRules[itype][level][id] then
if quality == 2 then
-- Uncommon item, remove nexus crystal
if (const.LevelRules[itype][level][id] < const.CRYSTAL) then
clean[id] = counts
end
else
-- Rare or epic item, remove dusts and essences
if (const.LevelRules[itype][level][id] > const.ESSENCE_GREATER) then
clean[id] = counts
end
end
end
end
return serialize(clean)
end
end
return str
end
function disenchantTotal(str)
-- Return total number of disenchants
local tot = 0
local tbl = unserialize(str)
for id in tbl do
tot = tot + tbl[id][N_DISENCHANTS]
end
return tot
end
function disenchantListHash()
-- Generate a hash for DisenchantList
local hash = 1
local id
for sig, val in pairs(DisenchantList) do
id = Enchantrix.Util.GetItemIdFromSig(sig)
Enchantrix.State.MAX_ITEM_ID = math.max(Enchantrix.State.MAX_ITEM_ID, id or 0)
hash = math.mod(3 * hash + 2 * (id or 0) + string.len(val), 16777216)
end
return hash
end
function mergeDisenchantLists()
-- Merge DisenchantList by base item, i.e. all "Foobar of <x>" are merged into "Foobar"
-- This can be rather time consuming so we store this in a saved variable and use a hash
-- signature to determine if we need to update the table
local hash = disenchantListHash()
if (not EnchantedBaseItems.hash) then
EnchantedBaseItems.hash = -hash
end
if (EnchantedBaseItems.hash ~= hash) then
-- Hash has changed, update EnchantedBaseItems
EnchantedBaseItems = {}
for sig, disenchant in pairs(DisenchantList) do
local item = Enchantrix.Util.GetItemIdFromSig(sig)
if (Enchantrix.Util.IsDisenchantable(item)) then
EnchantedBaseItems[item] = mergeDisenchant(EnchantedBaseItems[item],
normalizeDisenchant(disenchant))
end
end
EnchantedBaseItems.hash = hash
end
-- We don't need DisenchantList anymore
DisenchantList = nil
-- Merge items from EnchantedLocal
for sig, disenchant in pairs(EnchantedLocal) do
local item = Enchantrix.Util.GetItemIdFromSig(sig)
if type(disenchant) == "table" then
saveLocal(sig, disenchant)
disenchant = EnchantedLocal[sig]
end
if Enchantrix.Util.IsDisenchantable(item) and (type(disenchant) == "string") then
LocalBaseItems[item] = mergeDisenchant(LocalBaseItems[item], disenchant)
end
end
-- Merge by item type
for id, disenchant in pairs(EnchantedBaseItems) do
local itype = Enchantrix.Util.GetItemType(id)
if itype then
EnchantedItemTypes[itype] = mergeDisenchant(EnchantedItemTypes[itype], disenchant)
end
end
for id, disenchant in pairs(LocalBaseItems) do
local itype = Enchantrix.Util.GetItemType(id)
if itype then
EnchantedItemTypes[itype] = mergeDisenchant(EnchantedItemTypes[itype], disenchant)
end
end
-- Take out the trash
collectgarbage()
end
function saveDisenchant(sig, reagentID, count)
-- Update tables after a disenchant has been detected
assert(type(sig) == "string"); assert(tonumber(reagentID)); assert(tonumber(count))
local id = Enchantrix.Util.GetItemIdFromSig(sig)
local itype = Enchantrix.Util.GetItemType(id)
local disenchant = string.format("%d:1:%d:0", reagentID, count)
EnchantedLocal[sig] = mergeDisenchant(EnchantedLocal[sig], disenchant)
LocalBaseItems[id] = mergeDisenchant(LocalBaseItems[id], disenchant)
if itype then
EnchantedItemTypes[itype] = mergeDisenchant(EnchantedItemTypes[itype], disenchant)
end
end
function saveLocal(sig, lData)
local str = "";
for eResult, eData in lData do
eResult = tonumber(eResult)
if not eResult then
return
end
if (eData and type(eData) == "table") then
local iCount = tonumber(eData.i) or 0;
local dCount = tonumber(eData.d) or 0;
local oCount = tonumber(eData.o) or 0;
local serial = string.format("%d:%d:%d:%d", eResult, iCount, dCount, oCount);
if (str == "") then str = serial else str = str..";"..serial end
else
eData = nil;
end
end
EnchantedLocal[sig] = str;
end
function getLocal(sig)
local enchantItem = {};
if (EnchantedLocal and EnchantedLocal[sig]) then
if (type(EnchantedLocal[sig]) == "table") then
-- Time to convert it into the new serialized format
saveLocal(sig, EnchantedLocal[sig]);
end
-- Get the string and break it apart
for enchantResult in Enchantrix.Util.Spliterator(EnchantedLocal[sig], ";") do
local enchantBreak = Enchantrix.Util.Split(enchantResult, ":");
local rSig = tonumber(enchantBreak[1]) or 0;
local iCount = tonumber(enchantBreak[2]) or 0;
local dCount = tonumber(enchantBreak[3]) or 0;
local oCount = tonumber(enchantBreak[4]) or 0;
enchantItem[rSig] = { i=iCount, d=dCount, o=oCount };
end
end
return enchantItem;
end
function getItemDisenchants(sig, name, useCache)
local disenchantsTo = {};
if (not Enchantrix.Storage.Price_Cache) or (time()-Enchantrix.Storage.Price_Cache.t > 300) then
Enchantrix.Storage.Price_Cache = {t=time()};
end
-- Automatically convert any named EnchantedLocal items to new items
if (name and EnchantedLocal[name]) then
local iData = getLocal(name)
for dName, count in iData do
local link = Enchantrix.Util.GetLinkFromName(dName);
local dSig = Enchantrix.Util.GetItemIdFromLink(link)
if (dSig ~= nil) then
if (not iData[dSig]) then iData[dSig] = {}; end
local oCount = tonumber(iData[dSig].o);
if (oCount == nil) then oCount = 0; end
iData[dSig].o = ""..count;
end
end
EnchantedLocal[name] = nil;
saveLocal(sig, iData);
end
local item = Enchantrix.Util.GetItemIdFromSig(sig)
if (not (item and Enchantrix.Util.IsDisenchantable(item))) then
-- Item is not disenchantable
return {}
end
-- If there is data, then work out the disenchant data
if (EnchantedBaseItems[item] or LocalBaseItems[item]) then
local biTotal = 0;
local bdTotal = 0;
local iTotal = 0;
local dTotal = 0;
local baseDisenchant = EnchantedBaseItems[item]
local itype = Enchantrix.Util.GetItemType(item)
if (itype and EnchantedItemTypes[itype]) then
if (disenchantTotal(EnchantedItemTypes[itype]) > disenchantTotal(baseDisenchant)) then
baseDisenchant = EnchantedItemTypes[itype]
end
end
baseDisenchant = cleanupDisenchant(baseDisenchant, item)
if (baseDisenchant) then
-- Base Disenchantments are now serialized
local baseResults = unserialize(baseDisenchant)
for dSig, counts in pairs(baseResults) do
if (dSig > 0) then
disenchantsTo[dSig] = {
biCount = counts[N_DISENCHANTS],
bdCount = counts[N_REAGENTS],
iCount = 0,
dCount = 0,
}
biTotal = biTotal + counts[N_DISENCHANTS]
bdTotal = bdTotal + counts[N_REAGENTS]
end
end
end
if (LocalBaseItems[item]) then
local enchantedLocal = unserialize(LocalBaseItems[item])
for dSig, counts in pairs(enchantedLocal) do
if (dSig and dSig > 0) then
if (not disenchantsTo[dSig]) then
disenchantsTo[dSig] = {biCount = 0, bdCount = 0, iCount = 0, dCount = 0}
end
disenchantsTo[dSig].dCount = counts[N_REAGENTS]
disenchantsTo[dSig].iCount = counts[N_DISENCHANTS]
dTotal = dTotal + counts[N_REAGENTS]
iTotal = iTotal + counts[N_DISENCHANTS]
end
end
end
local total = biTotal + iTotal;
local hspGuess = 0;
local medianGuess = 0;
local marketGuess = 0;
if (total > 0) then
for dSig, counts in pairs(disenchantsTo) do
local item = 0;
if (dSig) then item = tonumber(dSig); end
local dName = Enchantrix.Util.GetReagentInfo(item);
if (not dName) then dName = "Item "..dSig; end
local count = (counts.biCount or 0) + (counts.iCount or 0);
local countI = (counts.biCount or 0) + (counts.iCount or 0);
local countD = (counts.bdCount or 0) + (counts.dCount or 0);
local pct = tonumber(string.format("%0.1f", count / total * 100));
local rate
if (countI > 0) then
rate = countD / countI
end
local count = 1;
if (rate and rate > 0) then count = rate; end
disenchantsTo[dSig].name = dName;
disenchantsTo[dSig].pct = pct;
disenchantsTo[dSig].rate = count;
local hsp, med, mkt = Enchantrix.Util.GetReagentPrice(item)
local hspValue = (hsp or 0) * pct * count / 100
local medValue = (med or 0) * pct * count / 100
local mktValue = (mkt or 0) * pct * count / 100
disenchantsTo[dSig].hspValue = hspValue;
disenchantsTo[dSig].medValue = medValue;
disenchantsTo[dSig].mktValue = mktValue;
hspGuess = hspGuess + hspValue;
medianGuess = medianGuess + medValue;
marketGuess = marketGuess + mktValue;
end
end
local confidence = math.log(math.min(total, 19)+1)/3;
disenchantsTo.totals = {};
disenchantsTo.totals.hspValue = hspGuess or 0;
disenchantsTo.totals.medValue = medianGuess or 0;
disenchantsTo.totals.mktValue = marketGuess or 0;
disenchantsTo.totals.biCount = biTotal or 0;
disenchantsTo.totals.bdCount = bdTotal or 0;
disenchantsTo.totals.dCount = dTotal or 0;
disenchantsTo.totals.iCount = iTotal or 0;
disenchantsTo.totals.total = total or 0;
disenchantsTo.totals.conf = confidence or 0;
end
return disenchantsTo;
end
Enchantrix.Storage = {
Revision = "$Revision: 899 $",
AddonLoaded = addonLoaded,
GetItemDisenchants = getItemDisenchants,
SaveDisenchant = saveDisenchant,
}