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: EnxUtil.lua 878 2006-05-28 16:50:50Z aradan $
5  
6 General utility functions
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 isDisenchantable
26 local getReagentInfo
27 local getLinkFromName
28 local getReagentPrice
29 local getItemType
30 local getItemIdFromSig
31 local getItemIdFromLink
32 local getSigFromLink
33  
34 local getRevision
35 local split
36 local spliterator
37 local chatPrint
38  
39 local gcd
40 local roundUp
41 local round
42 local confidenceInterval
43  
44 local createProfiler
45  
46 ------------------------
47 -- Item functions --
48 ------------------------
49  
50 -- Return false if item id can't be disenchanted
51 function isDisenchantable(id)
52 if (id) then
53 local _, _, quality, _, _, _, count, equip = GetItemInfo(id)
54 if (not quality) then
55 -- GetItemInfo() failed, item might be disenchantable
56 return true
57 end
58 if (not Enchantrix.Constants.InventoryTypes[equip]) then
59 -- Neither weapon nor armor
60 return false
61 end
62 if (quality and quality < 2) then
63 -- Low quality
64 return false
65 end
66 if (count and count > 1) then
67 -- Stackable item
68 return false
69 end
70 return true
71 end
72 return false
73 end
74  
75 -- Frontend to GetItemInfo()
76 -- Information for disenchant reagents are kept in a saved variable cache
77 function getReagentInfo(id)
78 local cache = EnchantConfig.cache.reagentinfo
79  
80 if type(id) == "string" then
81 local _, _, i = string.find(id, "item:(%d+):")
82 id = i
83 end
84 id = tonumber(id)
85  
86 local sName, sLink, iQuality, iLevel, sType, sSubtype, iStack, sEquip, sTexture = GetItemInfo(id)
87 if id and Enchantrix.Constants.StaticPrices[id] then
88 if sName then
89 cache[id] = sName.."|"..iQuality.."|"..sTexture
90 cache["t"] = sType
91 elseif type(cache[id]) == "string" then
92 local cdata = split(cache[id], "|")
93  
94 sName = cdata[1]
95 iQuality = tonumber(cdata[2])
96 iLevel = 0
97 sType = cache["t"]
98 sSubtype = cache["t"]
99 iStack = 10
100 sEquip = ""
101 sTexture = cdata[3]
102 sLink = "item:"..id..":0:0:0"
103 end
104 end
105  
106 if sName and id then
107 -- Might as well save this name while we have the data
108 EnchantConfig.cache.names[sName] = "item:"..id..":0:0:0"
109 end
110  
111 return sName, sLink, iQuality, iLevel, sType, sSubtype, iStack, sEquip, sTexture
112 end
113  
114 function getLinkFromName(name)
115 assert(type(name) == "string")
116  
117 if not EnchantConfig.cache then
118 EnchantConfig.cache = {}
119 end
120 if not EnchantConfig.cache.names then
121 EnchantConfig.cache.names = {}
122 end
123  
124 local link = EnchantConfig.cache.names[name]
125 if link then
126 local n = GetItemInfo(link)
127 if n ~= name then
128 EnchantConfig.cache.names[name] = nil
129 end
130 end
131 if not EnchantConfig.cache.names[name] then
132 for i = 1, Enchantrix.State.MAX_ITEM_ID + 4000 do
133 local n, link = GetItemInfo(i)
134 if n then
135 if n == name then
136 EnchantConfig.cache.names[name] = link
137 break
138 end
139 Enchantrix.State.MAX_ITEM_ID = math.max(Enchantrix.State.MAX_ITEM_ID, i)
140 end
141 end
142 end
143 return EnchantConfig.cache.names[name]
144 end
145  
146 -- Returns HSP, median and static price for reagent
147 -- Auctioneer values are kept in cache for 48h in case Auctioneer isn't loaded
148 function getReagentPrice(reagentID)
149 -- reagentID ::= number | hyperlink
150 if type(reagentID) == "string" then
151 local _, _, i = string.find(reagentID, "item:(%d+):")
152 reagentID = i
153 end
154 reagentID = tonumber(reagentID)
155 if not reagentID then return nil end
156  
157 local hsp, median, market
158  
159 market = Enchantrix.Constants.StaticPrices[reagentID]
160  
161 if Enchantrix.State.Auctioneer_Loaded then
162 local itemKey = string.format("%d:0:0", reagentID);
163 local realm = Auctioneer.Util.GetAuctionKey()
164 hsp = Auctioneer.Statistic.GetHSP(itemKey, realm)
165 median = Auctioneer.Statistic.GetUsableMedian(itemKey, realm)
166 end
167  
168 if not EnchantConfig.cache then EnchantConfig.cache = {} end
169 if not EnchantConfig.cache.prices then EnchantConfig.cache.prices = {} end
170 if not EnchantConfig.cache.prices[reagentID] then EnchantConfig.cache.prices[reagentID] = {} end
171 local cache = EnchantConfig.cache.prices[reagentID]
172 if cache.timestamp and time() - cache.timestamp > 172800 then
173 cache = {}
174 end
175  
176 cache.hsp = hsp or cache.hsp
177 cache.median = median or cache.median
178 cache.market = market or cache.market
179 cache.timestamp = time()
180  
181 return cache.hsp, cache.median, cache.market
182 end
183  
184 -- Return item level (rounded up to nearest 5 levels), quality and type as string,
185 -- e.g. "20:2:Armor" for uncommon level 20 armor
186 function getItemType(id)
187 if (id) then
188 local _, _, quality, level, _, _, _, equip = GetItemInfo(id)
189 if (quality and quality >= 2 and level > 0 and Enchantrix.Constants.InventoryTypes[equip]) then
190 return string.format("%d:%d:%s", Enchantrix.Util.RoundUp(level, 5), quality, Enchantrix.Constants.InventoryTypes[equip])
191 end
192 end
193 end
194  
195 -- Return item id as integer
196 function getItemIdFromSig(sig)
197 if type(sig) == "string" then
198 _, _, sig = string.find(sig, "(%d+)")
199 end
200 return tonumber(sig)
201 end
202  
203 function getItemIdFromLink(link)
204 return (EnhTooltip.BreakLink(link))
205 end
206  
207 function getSigFromLink(link)
208 assert(type(link) == "string")
209  
210 local _, _, id, rand = string.find(link, "item:(%d+):%d+:(%d+):%d+")
211 if id and rand then
212 return id..":0:"..rand
213 end
214 end
215  
216 -----------------------------------
217 -- General Utility Functions --
218 -----------------------------------
219  
220 -- Extract the revision number from SVN keyword string
221 function getRevision(str)
222 if not str then return 0 end
223 local _, _, rev = string.find(str, "Revision: (%d+)")
224 return tonumber(rev) or 0
225 end
226  
227 function split(str, at)
228 local splut = {};
229  
230 if (type(str) ~= "string") then return nil end
231 if (not str) then str = "" end
232  
233 if (not at)
234 then table.insert(splut, str)
235  
236 else
237 for n, c in string.gfind(str, '([^%'..at..']*)(%'..at..'?)') do
238 table.insert(splut, n);
239  
240 if (c == '') then break end
241 end
242 end
243 return splut;
244 end
245  
246 -- Iterator version of split()
247 -- for i in spliterator(a, b) do
248 -- is equivalent to
249 -- for _, i in ipairs(split(a, b)) do
250 -- but puts less strain on the garbage collector
251 function spliterator(str, at)
252 local start
253 local found = 0
254 local done = (type(str) ~= "string")
255 return function()
256 if done then return nil end
257 start = found + 1
258 found = string.find(str, at, start, true)
259 if not found then
260 found = 0
261 done = true
262 end
263 return string.sub(str, start, found - 1)
264 end
265 end
266  
267 function chatPrint(text, cRed, cGreen, cBlue, cAlpha, holdTime)
268 local frameIndex = Enchantrix.Config.GetFrameIndex();
269  
270 if (cRed and cGreen and cBlue) then
271 if getglobal("ChatFrame"..frameIndex) then
272 getglobal("ChatFrame"..frameIndex):AddMessage(text, cRed, cGreen, cBlue, cAlpha, holdTime);
273  
274 elseif (DEFAULT_CHAT_FRAME) then
275 DEFAULT_CHAT_FRAME:AddMessage(text, cRed, cGreen, cBlue, cAlpha, holdTime);
276 end
277  
278 else
279 if getglobal("ChatFrame"..frameIndex) then
280 getglobal("ChatFrame"..frameIndex):AddMessage(text, 1.0, 0.5, 0.25);
281 elseif (DEFAULT_CHAT_FRAME) then
282 DEFAULT_CHAT_FRAME:AddMessage(text, 1.0, 0.5, 0.25);
283 end
284 end
285 end
286  
287  
288 ------------------------
289 -- Math Functions --
290 ------------------------
291  
292 function gcd(a, b)
293 -- Greatest Common Divisor, Euclidean algorithm
294 local m, n = tonumber(a), tonumber(b) or 0
295 while (n ~= 0) do
296 m, n = n, math.mod(m, n)
297 end
298 return m
299 end
300  
301 -- Round up m to nearest multiple of n
302 function roundUp(m, n)
303 return math.ceil(m / n) * n
304 end
305  
306 -- Round m to n digits in given base
307 function round(m, n, base, offset)
308 base = base or 10 -- Default to base 10
309 offset = offset or 0.5
310  
311 if (n or 0) == 0 then
312 return math.floor(m + offset)
313 end
314  
315 if m == 0 then
316 return 0
317 elseif m < 0 then
318 return -round(-m, n, base, offset)
319 end
320  
321 -- Get integer and fractional part of n
322 local f = math.floor(n)
323 n, f = f, n - f
324  
325 -- Pre-rounding multiplier is 1 / f
326 local mul = 1
327 if f > 0.1 then
328 mul = math.floor(1 / f + 0.5)
329 end
330  
331 local d
332 if n > 0 then
333 d = base^(n - math.floor(math.log(m) / math.log(base)) - 1)
334 else
335 d = 1
336 end
337 if offset >= 1 then
338 return math.ceil(m * d * mul) / (d * mul)
339 else
340 return math.floor(m * d * mul + offset) / (d * mul)
341 end
342 end
343  
344 -- Returns confidence interval for binomial distribution given observed
345 -- probability p, sample size n, and z-value
346 function confidenceInterval(p, n, z)
347 if not z then
348 --[[
349 z conf
350 1.282 80%
351 1.645 90%
352 1.960 95%
353 2.326 98%
354 2.576 99%
355 3.090 99.8%
356 3.291 99.9%
357 ]]
358 z = 1.645
359 end
360 assert(p >= 0 and p <= 1)
361 assert(n > 0)
362  
363 local a = p + z^2 / (2 * n)
364 local b = z * math.sqrt(p * (1 - p) / n + z^2 / (4 * n^2))
365 local c = 1 + z^2 / n
366  
367 return (a - b) / c, (a + b) / c
368 end
369  
370 ---------------------
371 -- Debug functions --
372 ---------------------
373  
374 -- profiler:Start()
375 -- Record start time and memory, set state to running
376 local function _profilerStart(this)
377 this.t = GetTime()
378 this.m = gcinfo()
379 this.r = true
380 end
381  
382 -- profiler:Stop()
383 -- Record time and memory change, set state to stopped
384 local function _profilerStop(this)
385 this.m = (gcinfo()) - this.m
386 this.t = GetTime() - this.t
387 this.r = false
388 end
389  
390 -- profiler:DebugPrint()
391 local function _profilerDebugPrint(this)
392 if this.n then
393 EnhTooltip.DebugPrint("Profiler ["..this.n.."]")
394 else
395 EnhTooltip.DebugPrint("Profiler")
396 end
397 if this.r == nil then
398 EnhTooltip.DebugPrint(" Not started")
399 else
400 EnhTooltip.DebugPrint(string.format(" Time: %0.3f s", this:Time()))
401 EnhTooltip.DebugPrint(string.format(" Mem: %0.0f KiB", this:Mem()))
402 if this.r then
403 EnhTooltip.DebugPrint(" Running...")
404 end
405 end
406 end
407  
408 -- time = profiler:Time()
409 -- Return time (in seconds) from Start() [until Stop(), if stopped]
410 local function _profilerTime(this)
411 if this.r == false then
412 return this.t
413 elseif this.r == true then
414 return GetTime() - this.t
415 end
416 end
417  
418 -- mem = profiler:Mem()
419 -- Return memory change (in kilobytes) from Start() [until Stop(), if stopped]
420 local function _profilerMem(this)
421 if this.r == false then
422 return this.m
423 elseif this.r == true then
424 return (gcinfo()) - this.m
425 end
426 end
427  
428 -- profiler = Enchantrix.Util.CreateProfiler("foobar")
429 function createProfiler(name)
430 return {
431 Start = _profilerStart,
432 Stop = _profilerStop,
433 DebugPrint = _profilerDebugPrint,
434 Time = _profilerTime,
435 Mem = _profilerMem,
436 n = name,
437 }
438 end
439  
440 Enchantrix.Util = {
441 Revision = "$Revision: 878 $",
442  
443 IsDisenchantable = isDisenchantable,
444 GetReagentInfo = getReagentInfo,
445 GetLinkFromName = getLinkFromName,
446 GetReagentPrice = getReagentPrice,
447 GetItemType = getItemType,
448 GetItemIdFromSig = getItemIdFromSig,
449 GetItemIdFromLink = getItemIdFromLink,
450 GetSigFromLink = getSigFromLink,
451 SigFromLink = sigFromLink,
452  
453 GetRevision = getRevision,
454 Split = split,
455 Spliterator = spliterator,
456 ChatPrint = chatPrint,
457  
458 GCD = gcd,
459 RoundUp = roundUp,
460 Round = round,
461 ConfidenceInterval = confidenceInterval,
462  
463 CreateProfiler = createProfiler,
464 }