vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[
        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,
}