vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Enchantrix Addon for World of Warcraft(tm).
3 Version: 3.6.1 (Platypus)
4 Revision: $Id: EnxStorage.lua 899 2006-06-05 11:03:30Z aradan $
5  
6 Database functions and saved variables.
7  
8 License:
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13  
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18  
19 You should have received a copy of the GNU General Public License
20 along with this program(see GLP.txt); if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 ]]
23  
24 -- Global functions
25 local addonLoaded -- Enchantrix.Storage.AddonLoaded()
26 local saveDisenchant -- Enchantrix.Storage.SaveDisenchant()
27 local getItemDisenchants -- Enchantrix.Storage.GetItemDisenchants()
28  
29 -- Local functions
30 local unserialize
31 local serialize
32 local mergeDisenchant
33 local normalizeDisenchant
34 local cleanupDisenchant
35 local disenchantTotal
36 local disenchantListHash
37 local mergeDisenchantLists
38 local saveLocal
39 local getLocal
40  
41 -- Database
42 local LocalBaseItems = {} -- EnchantedLocal merged by item id
43 local EnchantedItemTypes = {} -- LocalBaseItems and EnchantedBaseItems merged by type
44  
45 local N_DISENCHANTS = 1
46 local N_REAGENTS = 2
47  
48 Enchantrix.State.MAX_ITEM_ID = 26000
49  
50 local const = Enchantrix.Constants
51  
52 function addonLoaded()
53 -- Create and setup saved variables
54 if not EnchantConfig then EnchantConfig = {} end
55 if not EnchantConfig.filters then EnchantConfig.filters = {} end
56 if not EnchantConfigChar then EnchantConfigChar = {} end
57 if not EnchantConfigChar.filters then EnchantConfigChar.filters = {} end
58  
59 if not EnchantConfig.cache then EnchantConfig.cache = {} end
60 if not EnchantConfig.cache.reagentinfo then EnchantConfig.cache.reagentinfo = {} end
61 if not EnchantConfig.cache.names then EnchantConfig.cache.names = {} end
62  
63 if not EnchantedLocal then EnchantedLocal = {} end
64 if not EnchantedBaseItems then EnchantedBaseItems = {} end
65  
66 if not EnchantConfig.zomgBlizzardAreMeanies then
67 -- Push disenchant reagents into cache if needed
68 for id in Enchantrix.Constants.StaticPrices do
69 if not (Enchantrix.Util.GetReagentInfo(id)) then
70 EnchantConfig.zomgBlizzardAreMeanies = true
71 GameTooltip:SetHyperlink(string.format("item:%d:0:0:0", id))
72 GameTooltip:Hide()
73 EnchantConfig.zomgBlizzardAreMeanies = nil
74 end
75 end
76 else
77 -- We were kicked from the server last time, better not try that again for a while
78 EnchantConfig.zomgBlizzardAreMeanies = nil
79 end
80  
81 mergeDisenchantLists()
82 end
83  
84 function unserialize(str)
85 -- Break up a disenchant string to a table for easy manipulation
86 local tbl = {}
87 if type(str) == "string" then
88 for de in Enchantrix.Util.Spliterator(str, ";") do
89 local _, _, id, d, r = string.find(de, "(%d+):(%d+):(%d+)")
90 id, d, r = tonumber(id), tonumber(d), tonumber(r)
91 if (id and d > 0 and r > 0) then
92 tbl[id] = {[N_DISENCHANTS] = d, [N_REAGENTS] = r}
93 end
94 end
95 end
96 return tbl
97 end
98  
99 function serialize(tbl)
100 -- Serialize a table into a string
101 if type(tbl) == "table" then
102 local str
103 for id, counts in pairs(tbl) do
104 if (type(id) == "number" and counts[N_DISENCHANTS] > 0 and counts[N_REAGENTS] > 0) then
105 if (str) then
106 str = string.format("%s;%d:%d:%d:0", str, id, counts[N_DISENCHANTS], counts[N_REAGENTS])
107 else
108 str = string.format("%d:%d:%d:0", id, counts[N_DISENCHANTS], counts[N_REAGENTS])
109 end
110 end
111 end
112 return str
113 end
114 end
115  
116 function mergeDisenchant(str1, str2)
117 -- Merge two disenchant strings into a single string
118 local tbl1, tbl2 = unserialize(str1), unserialize(str2)
119 for id, counts in pairs(tbl2) do
120 if (not tbl1[id]) then
121 tbl1[id] = counts
122 else
123 tbl1[id][N_DISENCHANTS] = tbl1[id][N_DISENCHANTS] + counts[N_DISENCHANTS]
124 tbl1[id][N_REAGENTS] = tbl1[id][N_REAGENTS] + counts[N_REAGENTS]
125 end
126 end
127 return serialize(tbl1)
128 end
129  
130 function normalizeDisenchant(str)
131 -- Divide all counts in disenchant string by gcd
132 local div = 0
133 local count = 0
134 local tbl = unserialize(str)
135 for id, counts in pairs(tbl) do
136 div = Enchantrix.Util.GCD(div, counts[N_DISENCHANTS])
137 div = Enchantrix.Util.GCD(div, counts[N_REAGENTS])
138 count = count + 1
139 end
140 -- Only normalize if there's more than one kind of reagent
141 if count > 1 then
142 for id, counts in pairs(tbl) do
143 counts[N_DISENCHANTS] = counts[N_DISENCHANTS] / div
144 counts[N_REAGENTS] = counts[N_REAGENTS] / div
145 end
146 return serialize(tbl)
147 end
148 return str
149 end
150  
151 function cleanupDisenchant(str, id)
152 -- Remove reagents that don't appear in level rules table
153 if (type(str) == "string") and (id ~= nil) then
154 local _, _, quality, level, _, _, _, equip = GetItemInfo(id)
155 local itype = const.InventoryTypes[equip]
156 if quality and itype then
157 local tbl = unserialize(str)
158 local clean = {}
159 level = Enchantrix.Util.RoundUp(level, 5)
160 for id, counts in pairs(tbl) do
161 if const.LevelRules[itype][level][id] then
162 if quality == 2 then
163 -- Uncommon item, remove nexus crystal
164 if (const.LevelRules[itype][level][id] < const.CRYSTAL) then
165 clean[id] = counts
166 end
167 else
168 -- Rare or epic item, remove dusts and essences
169 if (const.LevelRules[itype][level][id] > const.ESSENCE_GREATER) then
170 clean[id] = counts
171 end
172 end
173 end
174 end
175 return serialize(clean)
176 end
177 end
178 return str
179 end
180  
181 function disenchantTotal(str)
182 -- Return total number of disenchants
183 local tot = 0
184 local tbl = unserialize(str)
185 for id in tbl do
186 tot = tot + tbl[id][N_DISENCHANTS]
187 end
188 return tot
189 end
190  
191 function disenchantListHash()
192 -- Generate a hash for DisenchantList
193 local hash = 1
194 local id
195 for sig, val in pairs(DisenchantList) do
196 id = Enchantrix.Util.GetItemIdFromSig(sig)
197 Enchantrix.State.MAX_ITEM_ID = math.max(Enchantrix.State.MAX_ITEM_ID, id or 0)
198 hash = math.mod(3 * hash + 2 * (id or 0) + string.len(val), 16777216)
199 end
200 return hash
201 end
202  
203 function mergeDisenchantLists()
204 -- Merge DisenchantList by base item, i.e. all "Foobar of <x>" are merged into "Foobar"
205 -- This can be rather time consuming so we store this in a saved variable and use a hash
206 -- signature to determine if we need to update the table
207 local hash = disenchantListHash()
208 if (not EnchantedBaseItems.hash) then
209 EnchantedBaseItems.hash = -hash
210 end
211 if (EnchantedBaseItems.hash ~= hash) then
212 -- Hash has changed, update EnchantedBaseItems
213 EnchantedBaseItems = {}
214 for sig, disenchant in pairs(DisenchantList) do
215 local item = Enchantrix.Util.GetItemIdFromSig(sig)
216 if (Enchantrix.Util.IsDisenchantable(item)) then
217 EnchantedBaseItems[item] = mergeDisenchant(EnchantedBaseItems[item],
218 normalizeDisenchant(disenchant))
219 end
220 end
221 EnchantedBaseItems.hash = hash
222 end
223  
224 -- We don't need DisenchantList anymore
225 DisenchantList = nil
226  
227 -- Merge items from EnchantedLocal
228 for sig, disenchant in pairs(EnchantedLocal) do
229 local item = Enchantrix.Util.GetItemIdFromSig(sig)
230 if type(disenchant) == "table" then
231 saveLocal(sig, disenchant)
232 disenchant = EnchantedLocal[sig]
233 end
234 if Enchantrix.Util.IsDisenchantable(item) and (type(disenchant) == "string") then
235 LocalBaseItems[item] = mergeDisenchant(LocalBaseItems[item], disenchant)
236 end
237 end
238  
239 -- Merge by item type
240 for id, disenchant in pairs(EnchantedBaseItems) do
241 local itype = Enchantrix.Util.GetItemType(id)
242 if itype then
243 EnchantedItemTypes[itype] = mergeDisenchant(EnchantedItemTypes[itype], disenchant)
244 end
245 end
246 for id, disenchant in pairs(LocalBaseItems) do
247 local itype = Enchantrix.Util.GetItemType(id)
248 if itype then
249 EnchantedItemTypes[itype] = mergeDisenchant(EnchantedItemTypes[itype], disenchant)
250 end
251 end
252  
253 -- Take out the trash
254 collectgarbage()
255 end
256  
257 function saveDisenchant(sig, reagentID, count)
258 -- Update tables after a disenchant has been detected
259 assert(type(sig) == "string"); assert(tonumber(reagentID)); assert(tonumber(count))
260  
261 local id = Enchantrix.Util.GetItemIdFromSig(sig)
262 local itype = Enchantrix.Util.GetItemType(id)
263 local disenchant = string.format("%d:1:%d:0", reagentID, count)
264 EnchantedLocal[sig] = mergeDisenchant(EnchantedLocal[sig], disenchant)
265 LocalBaseItems[id] = mergeDisenchant(LocalBaseItems[id], disenchant)
266 if itype then
267 EnchantedItemTypes[itype] = mergeDisenchant(EnchantedItemTypes[itype], disenchant)
268 end
269 end
270  
271 function saveLocal(sig, lData)
272 local str = "";
273 for eResult, eData in lData do
274 eResult = tonumber(eResult)
275 if not eResult then
276 return
277 end
278 if (eData and type(eData) == "table") then
279 local iCount = tonumber(eData.i) or 0;
280 local dCount = tonumber(eData.d) or 0;
281 local oCount = tonumber(eData.o) or 0;
282 local serial = string.format("%d:%d:%d:%d", eResult, iCount, dCount, oCount);
283 if (str == "") then str = serial else str = str..";"..serial end
284 else
285 eData = nil;
286 end
287 end
288 EnchantedLocal[sig] = str;
289 end
290  
291 function getLocal(sig)
292 local enchantItem = {};
293 if (EnchantedLocal and EnchantedLocal[sig]) then
294 if (type(EnchantedLocal[sig]) == "table") then
295 -- Time to convert it into the new serialized format
296 saveLocal(sig, EnchantedLocal[sig]);
297 end
298  
299 -- Get the string and break it apart
300 for enchantResult in Enchantrix.Util.Spliterator(EnchantedLocal[sig], ";") do
301 local enchantBreak = Enchantrix.Util.Split(enchantResult, ":");
302 local rSig = tonumber(enchantBreak[1]) or 0;
303 local iCount = tonumber(enchantBreak[2]) or 0;
304 local dCount = tonumber(enchantBreak[3]) or 0;
305 local oCount = tonumber(enchantBreak[4]) or 0;
306  
307 enchantItem[rSig] = { i=iCount, d=dCount, o=oCount };
308 end
309 end
310 return enchantItem;
311 end
312  
313 function getItemDisenchants(sig, name, useCache)
314 local disenchantsTo = {};
315  
316 if (not Enchantrix.Storage.Price_Cache) or (time()-Enchantrix.Storage.Price_Cache.t > 300) then
317 Enchantrix.Storage.Price_Cache = {t=time()};
318 end
319  
320 -- Automatically convert any named EnchantedLocal items to new items
321 if (name and EnchantedLocal[name]) then
322 local iData = getLocal(name)
323 for dName, count in iData do
324 local link = Enchantrix.Util.GetLinkFromName(dName);
325 local dSig = Enchantrix.Util.GetItemIdFromLink(link)
326 if (dSig ~= nil) then
327 if (not iData[dSig]) then iData[dSig] = {}; end
328 local oCount = tonumber(iData[dSig].o);
329 if (oCount == nil) then oCount = 0; end
330 iData[dSig].o = ""..count;
331 end
332 end
333 EnchantedLocal[name] = nil;
334 saveLocal(sig, iData);
335 end
336  
337 local item = Enchantrix.Util.GetItemIdFromSig(sig)
338 if (not (item and Enchantrix.Util.IsDisenchantable(item))) then
339 -- Item is not disenchantable
340 return {}
341 end
342  
343 -- If there is data, then work out the disenchant data
344 if (EnchantedBaseItems[item] or LocalBaseItems[item]) then
345 local biTotal = 0;
346 local bdTotal = 0;
347 local iTotal = 0;
348 local dTotal = 0;
349  
350 local baseDisenchant = EnchantedBaseItems[item]
351  
352 local itype = Enchantrix.Util.GetItemType(item)
353 if (itype and EnchantedItemTypes[itype]) then
354 if (disenchantTotal(EnchantedItemTypes[itype]) > disenchantTotal(baseDisenchant)) then
355 baseDisenchant = EnchantedItemTypes[itype]
356 end
357 end
358  
359 baseDisenchant = cleanupDisenchant(baseDisenchant, item)
360  
361 if (baseDisenchant) then
362 -- Base Disenchantments are now serialized
363 local baseResults = unserialize(baseDisenchant)
364 for dSig, counts in pairs(baseResults) do
365 if (dSig > 0) then
366 disenchantsTo[dSig] = {
367 biCount = counts[N_DISENCHANTS],
368 bdCount = counts[N_REAGENTS],
369 iCount = 0,
370 dCount = 0,
371 }
372 biTotal = biTotal + counts[N_DISENCHANTS]
373 bdTotal = bdTotal + counts[N_REAGENTS]
374 end
375 end
376 end
377  
378 if (LocalBaseItems[item]) then
379 local enchantedLocal = unserialize(LocalBaseItems[item])
380 for dSig, counts in pairs(enchantedLocal) do
381 if (dSig and dSig > 0) then
382 if (not disenchantsTo[dSig]) then
383 disenchantsTo[dSig] = {biCount = 0, bdCount = 0, iCount = 0, dCount = 0}
384 end
385 disenchantsTo[dSig].dCount = counts[N_REAGENTS]
386 disenchantsTo[dSig].iCount = counts[N_DISENCHANTS]
387  
388 dTotal = dTotal + counts[N_REAGENTS]
389 iTotal = iTotal + counts[N_DISENCHANTS]
390 end
391 end
392 end
393  
394 local total = biTotal + iTotal;
395  
396 local hspGuess = 0;
397 local medianGuess = 0;
398 local marketGuess = 0;
399 if (total > 0) then
400 for dSig, counts in pairs(disenchantsTo) do
401 local item = 0;
402 if (dSig) then item = tonumber(dSig); end
403 local dName = Enchantrix.Util.GetReagentInfo(item);
404 if (not dName) then dName = "Item "..dSig; end
405 local count = (counts.biCount or 0) + (counts.iCount or 0);
406 local countI = (counts.biCount or 0) + (counts.iCount or 0);
407 local countD = (counts.bdCount or 0) + (counts.dCount or 0);
408 local pct = tonumber(string.format("%0.1f", count / total * 100));
409 local rate
410 if (countI > 0) then
411 rate = countD / countI
412 end
413  
414 local count = 1;
415 if (rate and rate > 0) then count = rate; end
416 disenchantsTo[dSig].name = dName;
417 disenchantsTo[dSig].pct = pct;
418 disenchantsTo[dSig].rate = count;
419  
420 local hsp, med, mkt = Enchantrix.Util.GetReagentPrice(item)
421  
422 local hspValue = (hsp or 0) * pct * count / 100
423 local medValue = (med or 0) * pct * count / 100
424 local mktValue = (mkt or 0) * pct * count / 100
425  
426 disenchantsTo[dSig].hspValue = hspValue;
427 disenchantsTo[dSig].medValue = medValue;
428 disenchantsTo[dSig].mktValue = mktValue;
429  
430 hspGuess = hspGuess + hspValue;
431 medianGuess = medianGuess + medValue;
432 marketGuess = marketGuess + mktValue;
433 end
434 end
435  
436 local confidence = math.log(math.min(total, 19)+1)/3;
437  
438 disenchantsTo.totals = {};
439 disenchantsTo.totals.hspValue = hspGuess or 0;
440 disenchantsTo.totals.medValue = medianGuess or 0;
441 disenchantsTo.totals.mktValue = marketGuess or 0;
442 disenchantsTo.totals.biCount = biTotal or 0;
443 disenchantsTo.totals.bdCount = bdTotal or 0;
444 disenchantsTo.totals.dCount = dTotal or 0;
445 disenchantsTo.totals.iCount = iTotal or 0;
446 disenchantsTo.totals.total = total or 0;
447 disenchantsTo.totals.conf = confidence or 0;
448 end
449  
450 return disenchantsTo;
451 end
452  
453 Enchantrix.Storage = {
454 Revision = "$Revision: 899 $",
455 AddonLoaded = addonLoaded,
456  
457 GetItemDisenchants = getItemDisenchants,
458 SaveDisenchant = saveDisenchant,
459 }