update repo
[QuestHelper.git] / utility.lua
blobbfab3ac67b1abbc7cb4e8b783a8912199bda5405
1 QuestHelper_File["utility.lua"] = "Development Version"
3 local default_colour_theme =
4 {message_prefix={0.4, 0.78, 1},
5 message={1, 0.6, 0.2},
6 tooltip={1, 0.8, 0.5},
7 message_highlight={0.73, 1, 0.84},
8 menu_text={1, 1, 1},
9 menu_text_highlight={0, 0, 0},
10 menu={0, 0, 0},
11 menu_highlight={0.3, 0.5, 0.7},
12 menu_title_text={1, 1, 1},
13 menu_title_text_highlight={1, 1, 1},
14 menu_title={0, 0.2, 0.6},
15 menu_title_highlight={0.1, 0.4, 0.8}}
17 local xmas_colour_theme =
18 {message_prefix={0.0, 0.7, 0.0},
19 message={0.2, 1, 0.2},
20 tooltip={0.4, 1, 0.4},
21 message_highlight={1, 0.3, 0.1},
22 menu_text={1, 1, 1},
23 menu_text_highlight={0, 0, 0},
24 menu={0.2, 0, 0},
25 menu_highlight={1, 0.3, 0.3},
26 menu_title_text={0.8, 1, 0.8},
27 menu_title_text_highlight={1, 1, 1},
28 menu_title={0.2, 0.6, 0.2},
29 menu_title_highlight={0.4, 0.7, 0.4}}
31 function QuestHelper:GetColourTheme()
32 if date("%b%d") == "Dec25" then
33 return xmas_colour_theme
34 end
36 return default_colour_theme
37 end
39 QuestHelper.nop = function () end -- Who wouldn't want a function that does nothing?
41 function QuestHelper:HashString(text)
42 -- Computes an Adler-32 checksum.
43 local a, b = 1, 0
44 for i=1,string.len(text) do
45 a = (a+string.byte(text,i))%65521
46 b = (b+a)%65521
47 end
48 return b*65536+a
49 end
51 function QuestHelper:CreateUID(length)
52 local result = ""
53 local characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
54 math.randomseed(math.random(0, 2147483647)+GetTime()*100000)
55 local base = GetUnitName("player")..":"..GetRealmName()..":"..math.random(0, 2147483647)..":"..GetTime()..":"..time()
57 for c = 1,(length or 32) do
58 local pos = 1+math.floor(self:HashString(result..base..math.random(0, 2147483647))%string.len(characters))
59 result = result .. string.sub(characters, pos, pos)
60 end
62 return result
63 end
65 function QuestHelper:ZoneSanity()
66 local sane = true
68 for c=1, select("#", GetMapContinents()) do
69 for z=0, select("#", GetMapZones(c)) do
70 local name
72 if z == 0 then
73 name = select(c, GetMapContinents())
74 else
75 name = select(z, GetMapZones(c))
76 end
78 assert(name)
80 if QuestHelper_Zones[c][z] ~= name then
81 sane = false
82 QuestHelper:TextOut("'"..name.."' has the wrong ID.")
83 end
85 local pair = QuestHelper_ZoneLookup[name]
87 if not pair or c ~= pair[1] or z ~= pair[2] then
88 sane = false
89 QuestHelper:TextOut("ZoneLookup['"..name.."'] maps to wrong pair.")
90 end
92 local index = QuestHelper_IndexLookup[name]
93 if QuestHelper_ZoneLookup[index] ~= pair then
94 sane = false
95 QuestHelper:TextOut("ZoneLookup['"..name.."'] isn't equal to ZoneLookup["..index.."]")
96 end
98 if not index or QuestHelper_NameLookup[index] ~= name then
99 sane = false
100 QuestHelper:TextOut("NameLookup["..(index or "???").."'] doesn't equal '"..name.."'")
105 return sane
108 function QuestHelper:TextOut(text)
109 local theme = self:GetColourTheme()
110 DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff%2x%2x%2xQuestHelper: |r%s", theme.message_prefix[1]*255,
111 theme.message_prefix[2]*255,
112 theme.message_prefix[3]*255, text),
113 theme.message[1],
114 theme.message[2],
115 theme.message[3])
118 function QuestHelper:Error(what)
119 DEFAULT_CHAT_FRAME:AddMessage("QuestHelper Error: "..(what or "Unknown").."\n"..debugstack(2), 1,.5,0)
120 QuestHelper_ErrorCatcher_ExplicitError(what or "Unknown", nil, nil)
121 error("Abort!")
124 function QuestHelper:HighlightText(text)
125 local theme = self:GetColourTheme()
126 return string.format("|cff%2x%2x%2x%s|r", theme.message_highlight[1]*255,
127 theme.message_highlight[2]*255,
128 theme.message_highlight[3]*255, text)
131 function QuestHelper:GetUnitID(unit)
132 local id = UnitGUID(unit)
134 if id then
135 return (string.sub(id, 5, 5) == "3") and tonumber(string.sub(id, 6, 12), 16) or nil
138 return nil
141 function QuestHelper:GetQuestID(index)
142 return tonumber(select(3, string.find(GetQuestLink(index), "|Hquest:(%d+):")))
145 -- For future reference:
146 -- Hearthstone = 6948
147 -- Rune of Teleportation = 17031
148 -- Rune of Portals = 17032
150 function QuestHelper:CountItem(item_id)
151 local count = 0
153 for bag = 0,NUM_BAG_SLOTS do
154 for slot = 1,GetContainerNumSlots(bag) do
155 local link = GetContainerItemLink(bag, slot)
156 if link and string.find(link, string.format("|Hitem:%d:", item_id)) then
157 count = count + (select(2, GetContainerItemInfo(bag, slot)) or 0)
162 return count
165 function QuestHelper:ItemCooldown(item_id)
166 local now = GetTime()
167 local cooldown = nil
169 for bag = 0,NUM_BAG_SLOTS do
170 for slot = 1,GetContainerNumSlots(bag) do
171 local link = GetContainerItemLink(bag, slot)
172 if link and string.find(link, string.format("|Hitem:%d:", item_id)) then
173 local s, d, e = GetContainerItemCooldown(bag, slot)
174 if e then
175 if cooldown then
176 cooldown = math.min(cooldown, math.max(0, d-now+s))
177 else
178 cooldown = math.max(0, d-now+s)
180 else
181 return 0
187 return cooldown
190 function QuestHelper:TimeString(seconds)
191 local seconds = math.ceil(seconds)
192 local h, m, s = math.floor(seconds/(60*60)), math.floor(seconds/60)%60, seconds%60
193 if h > 0 then
194 return string.format("|cffffffff%d|r:|cffffffff%02d|r:|cffffffff%02d|r", h, m, s)
195 else
196 return string.format("|cffffffff%d|r:|cffffffff%02d|r", m, s)
200 function QuestHelper:ProgressString(str, pct)
201 if pct > 1 then
202 return string.format("|cff00ff00%s|r", str)
203 elseif pct < 0 then
204 return string.format("|cffff0000%s|r", str)
205 elseif pct > 0.5 then
206 return string.format("|cff%2xff00%s|r", 510-pct*510, str)
207 else
208 return string.format("|cffff%2x00%s|r", pct*510, str)
212 function QuestHelper:PercentString(pct)
213 if pct > 1 then
214 return string.format("|cff00ff00%.1f%%|r", pct*100)
215 elseif pct < 0 then
216 return string.format("|cffff0000%.1f%%|r", pct*100)
217 elseif pct > 0.5 then
218 return string.format("|cff%2xff00%.1f%%|r", 510-pct*510, pct*100)
219 else
220 return string.format("|cffff%2x00%.1f%%|r", pct*510, pct*100)
224 function QuestHelper:PlayerPosition()
225 return self.i, self.x, self.y
228 function QuestHelper:UnitPosition(unit)
229 local c, z, x, y = self.Astrolabe:GetUnitPosition(unit,true)
230 if c then
231 if z == 0 then
232 SetMapToCurrentZone()
233 z = GetCurrentMapZone()
234 if z ~= 0 then
235 x, y = self.Astrolabe:TranslateWorldMapPosition(c, 0, x, y, c, z)
238 return QuestHelper_IndexLookup[c][z], x, y
239 else
240 return self:PlayerPosition()
244 function QuestHelper:LocationString(i, x, y)
245 return ("[|cffffffff%s|r:|cffffffff%d,%.3f,%.3f|r]"):format(QuestHelper_NameLookup[i] or "nil", i, x, y)
248 function QuestHelper:Distance(i1, x1, y1, i2, x2, y2)
249 local p1, p2 = QuestHelper_ZoneLookup[i1], QuestHelper_ZoneLookup[i2]
250 return self.Astrolabe:ComputeDistance(p1[1], p1[2], x1, y1, p2[1], p2[2], x2, y2) or 10000
253 function QuestHelper:AppendPosition(list, index, x, y, w, min_dist)
254 if (x == 0 and y == 0) or x <= -0.1 or y <= -0.1 or x >= 1.1 or y >= 1.1 then
255 return list -- This isn't a real position.
258 local closest, distance = nil, 0
259 w = w or 1
260 min_dist = min_dist or 200
262 for i, p in ipairs(list) do
263 if index == p[1] then
264 local d = self:Distance(index, x, y, p[1], p[2], p[3])
265 if not closest or d < distance then
266 closest, distance = i, d
271 if closest and distance < min_dist then
272 local p = list[closest]
273 p[2] = (p[2]*p[4]+x*w)/(p[4]+w)
274 p[3] = (p[3]*p[4]+y*w)/(p[4]+w)
275 p[4] = p[4]+w
276 else
277 table.insert(list, {index, x, y, w})
280 return list
283 function QuestHelper:PositionListDistance(list, index, x, y)
284 local closest, distance = nil, 0
285 for i, p in ipairs(list) do
286 local d = self:Distance(index, x, y, p[1], p[2], p[3])
287 if not closest or d < distance then
288 closest, distance = p, d
291 if closest then
292 return distance, closest[1], closest[2], closest[3]
296 function QuestHelper:PositionListDistance2(list, i1, x1, y1, i2, x2, y2)
297 local closest, bd1, bd2, bdt = nil, 0, 0, 0
298 for i, p in ipairs(list) do
299 local d1 = self:Distance(i1, x1, y1, p[1], p[2], p[3])
300 local d2 = self:Distance(i2, x2, y2, p[1], p[2], p[3])
301 local t = d1+d2
302 if not closest or t < bdt then
303 closest, bd1, bd2, bdt = p, d1, d2, t
306 if closest then
307 return d1, d2, closest[1], closest[2], closest[3]
311 function QuestHelper:MergePositions(list1, list2)
312 for i, p in ipairs(list2) do
313 self:AppendPosition(list1, unpack(p))
317 function QuestHelper:MergeDrops(list1, list2)
318 for element, count in pairs(list2) do
319 list1[element] = (list1[element] or 0) + count
323 function QuestHelper: Assert(a, b) -- the space exists so the anti-assert script doesn't find it :D
324 if not a then
325 QuestHelper:Error(b or "Assertion Failed")
329 function QuestHelper:StringizeTable(a)
330 if not a then return "nil" end
331 acu = tostring(self.recycle_tabletyping[a])..": "
332 for i,v in pairs(a) do acu = acu.."["..tostring(i)..","..tostring(v).."] " end
333 return acu
336 function QuestHelper:StringizeTableDouble(a)
337 if not a then return "nil" end
338 acu = tostring(self.recycle_tabletyping[a])..": "
339 for i,v in pairs(a) do acu = acu.."["..self:StringizeTable(i)..","..self:StringizeTable(v).."] " end
340 return acu
343 function QuestHelper:StringizeRecursive(a, d)
344 if not a then return "nil" end
345 if d <= 0 or type(a) ~= "table" then return tostring(a) end
346 acu = tostring(self.recycle_tabletyping[a])..": "
347 for i,v in pairs(a) do acu = acu.."["..self:StringizeRecursive(i, d - 1)..","..self:StringizeRecursive(v, d - 1).."] " end
348 return acu
351 function QuestHelper:TableSize(tbl)
352 local count = 0
353 for k, v in pairs(tbl) do
354 count = count + 1
356 return count
359 function QuestHelper:IsWrath()
360 return GetBuildInfo():sub(1,1) == '3'