Arrgh
[QuestHelper.git] / custom.lua
blobd48ecae6ec853ffe086ba8e67d8adf55dd4a830c
1 QuestHelper_File["custom.lua"] = "Development Version"
2 QuestHelper_Loadtime["custom.lua"] = GetTime()
4 local search_frame = CreateFrame("Button", nil, UIParent)
5 search_frame.text = search_frame:CreateFontString()
6 search_frame.text:SetFont(QuestHelper.font.sans, 15)
7 search_frame.text:SetTextColor(1, 1, 1)
8 search_frame.text:SetJustifyH("CENTER")
9 search_frame.text:SetJustifyV("MIDDLE")
10 search_frame.text:SetDrawLayer("OVERLAY")
11 search_frame.text:SetAllPoints()
12 search_frame.text:Show()
13 search_frame.background = search_frame:CreateTexture()
14 search_frame.background:SetTexture(0, 0, 0, 0.5)
15 search_frame.background:SetDrawLayer("BACKGROUND")
16 search_frame.background:SetAllPoints()
17 search_frame.background:Show()
18 search_frame:SetPoint("CENTER", UIParent, "CENTER")
19 search_frame:Hide()
21 search_frame.results = {}
23 function search_frame:SetText(text)
24 self.text:SetText(text)
25 self:SetWidth(self.text:GetWidth()+10)
26 self:SetHeight(self.text:GetHeight()+10)
27 end
29 function search_frame:OnUpdate()
30 if self.routine and coroutine.status(self.routine) ~= "dead" then
31 local no_error, display = coroutine.resume(self.routine, self, self.query)
32 if no_error then
33 self:SetText(display)
34 else
35 QuestHelper:TextOut("Searching co-routine just exploded: "..display)
36 end
37 else
38 self:ShowResults()
39 self.routine = nil
40 QH_Hook(self, "OnUpdate", nil)
41 self:Hide()
42 end
43 end
45 function QuestHelper:PerformCustomSearch(func)
46 if not search_frame:GetScript("OnUpdate") then
47 search_frame:Show()
48 QH_Hook(search_frame, "OnUpdate", func)
49 end
50 end
52 function QuestHelper:StopCustomSearch()
53 if not search_frame.routine then
54 search_frame:Hide()
55 QH_Hook(search_frame, "OnUpdate", nil)
56 end
57 end
60 do return end
62 -- This next bit of stuff is for fuzzy string comarisons.
65 local row, prow = {}, {}
67 local difftable = {}
69 for i = 65,90 do
70 local a = {}
71 difftable[i-64] = a
72 for j = 65,90 do
73 a[j-64] = i==j and 0 or 1
74 end
75 end
77 local function setgroup(a, w)
78 for i = 1,string.len(a)-1 do
79 for j = i+1,string.len(a) do
80 local c1, c2 = string.byte(a,i)-64, string.byte(a,j)-64
82 difftable[c1][c2] = math.min(w, difftable[c1][c2])
83 difftable[c2][c1] = math.min(w, difftable[c2][c1])
84 end
85 end
86 end
88 -- Characters that sound similar. At least in my opinion.
89 setgroup("BCDFGHJKLMNPQRSTVWXZ", 0.9)
90 setgroup("AEIOUY", 0.6)
91 setgroup("TD", 0.6)
92 setgroup("CKQ", 0.4)
93 setgroup("MN", 0.4)
94 setgroup("EIY", 0.3)
95 setgroup("UO", 0.2)
96 setgroup("SZ", 0.6)
98 local function diffness(a, b)
99 if a >= 65 and a <=90 then
100 if b >= 65 and b <= 90 then
101 return difftable[a-64][b-64]
102 else
103 return 1
105 elseif b >= 65 and b <= 90 then
106 return 1
107 else
108 return 0
112 local function fuzzyCompare(a, b)
113 local m, n = string.len(a), string.len(b)
115 if n == 0 or m == 0 then
116 return n == m and 0 or 1
119 for j = 1,n+1 do
120 row[j] = j-1
123 for i = 1,m do
124 row, prow = prow, row
125 row[1] = i
127 for j = 1,n do
128 row[j+1] = math.min(prow[j+1]+1, row[j]+.4, prow[j]+diffness(string.byte(a,i), string.byte(b,j)))
132 return row[n+1]/math.max(n,m)
135 function QuestHelper:ToggleUserObjective(cat, what)
136 local objective = self:GetObjective(cat, what)
138 if self.user_objectives[objective] then
139 self:TextOut(QHFormat("REMOVED_OBJ", self.user_objectives[objective]))
140 self:RemoveObjectiveWatch(objective, self.user_objectives[objective])
141 self.user_objectives[objective] = nil
142 elseif objective:Known() then
143 local name
144 if cat == "loc" then
145 local _, _, i, x, y = string.find(what, "^(%d+),([%d%.]+),([%d%.]+)$")
146 name = QHFormat("USER_OBJ", self:HighlightText(QuestHelper_NameLookup[tonumber(i)])..": "..self:HighlightText(x*100)..", "..self:HighlightText(y*100))
147 else
148 name = QHFormat("USER_OBJ", self:HighlightText(string.gsub(cat, "^(.)", string.upper))..": "..self:HighlightText(what))
151 objective.priority = 1
152 self.user_objectives[objective] = name
153 self:AddObjectiveWatch(objective, name)
155 self:TextOut(QHFormat("CREATED_OBJ", name))
156 else
157 self:TextOut(QHText("UNKNOWN_OBJ"))
161 function search_frame:CreateResultItem(r, menu)
162 local item
164 if r.cat == "loc" then
165 local _, _, i, x, y = string.find(r.what, "^(%d+),([%d%.]+),([%d%.]+)$")
166 item = QuestHelper:CreateMenuItem(menu, QuestHelper_NameLookup[tonumber(i)]..": "..(x*100)..", "..(y*100).." ["..QuestHelper:PercentString(1-r.w).."]")
167 item:AddTexture(QuestHelper:CreateIconTexture(item, 6), true)
168 else
169 item = QuestHelper:CreateMenuItem(menu, r.what .. " ["..QuestHelper:PercentString(1-r.w).."]")
170 item:AddTexture(QuestHelper:CreateIconTexture(item, (r.cat == "monster" and 1) or 2), true)
173 item:SetFunction(QuestHelper.ToggleUserObjective, QuestHelper, r.cat, r.what)
175 return item
178 function search_frame:ShowResults()
179 local menu = QuestHelper:CreateMenu()
180 QuestHelper:CreateMenuTitle(menu, QHText("RESULTS_TITLE"))
182 if #self.results == 0 then
183 QuestHelper:CreateMenuItem(menu, QHText("NO_RESULTS"))
184 else
185 for i, r in ipairs(self.results) do
186 self:CreateResultItem(r, menu)
190 menu:ShowAtCursor()
191 self:ClearResults()
194 function search_frame:ClearResults()
195 while #self.results > 0 do
196 QuestHelper:ReleaseTable(table.remove(self.results))
200 function search_frame:AddResult(cat, what, w)
201 local r = self.results
202 local mn, mx = 1, #r+1
204 while mn ~= mx do
205 local m = math.floor((mn+mx)*0.5)
207 if r[m].w < w then
208 mn = m+1
209 else
210 mx = m
214 if mn <= 20 then
215 if r[mn] and r[mn].cat == cat and r[mn].what == what then
216 -- Don't add the same item twice.
217 -- Might miss it if multiple items have the same score. Dont care.
218 return
221 if #r >= 20 then
222 QuestHelper:ReleaseTable(table.remove(r, 20))
225 local obj = QuestHelper:CreateTable()
226 obj.cat = cat
227 obj.what = what
228 obj.w = w
229 table.insert(r, mn, obj)
233 function search_frame:SearchRoutine(input)
234 if input == "" then
235 for obj in pairs(QuestHelper.user_objectives) do
236 self:AddResult(obj.cat, obj.obj, 0)
238 return
241 input = string.upper(input)
242 local _, _, command, argument = string.find(input, "^%s*([^%s]-)%s+(.-)%s*$")
244 local search_item, search_npc, search_loc = false, false, false
246 if command and argument then
247 if command == "ITEM" then
248 search_item, input = true, argument
249 elseif command == "NPC" or command == "MONSTER" then
250 search_npc, input = true, argument
251 elseif command == "LOCATION" or command == "LOC" then
252 search_loc, input = true, argument
253 else
254 search_item, search_npc, search_loc = true, true, true
256 else
257 search_item, search_npc, search_loc = true, true, true
260 local yield_countdown_max = math.max(1, math.floor(2000/string.len(input)+0.5))
261 local yield_countdown = yield_countdown_max
263 if search_item then
264 local list = QuestHelper_Objectives_Local["item"]
265 if list then for n in pairs(list) do
266 self:AddResult("item", n, fuzzyCompare(input, string.upper(n)))
267 yield_countdown = yield_countdown - 1
268 if yield_countdown == 0 then
269 yield_countdown = yield_countdown_max
270 coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_LOCAL", QHText("SEARCHING_ITEMS"))))
272 end end
274 list = QuestHelper_StaticData[QuestHelper.locale].objective
275 list = list and list.item
276 if list then for n in pairs(list) do
277 self:AddResult("item", n, fuzzyCompare(input, string.upper(n)))
278 yield_countdown = yield_countdown - 1
279 if yield_countdown == 0 then
280 yield_countdown = yield_countdown_max
281 coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_STATIC", QHText("SEARCHING_ITEMS"))))
283 end end
286 if search_npc then
287 local list = QuestHelper_Objectives_Local["monster"]
288 if list then for n in pairs(list) do
289 self:AddResult("monster", n, fuzzyCompare(input, string.upper(n)))
290 yield_countdown = yield_countdown - 1
291 if yield_countdown == 0 then
292 yield_countdown = yield_countdown_max
293 coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_LOCAL", QHText("SEARCHING_NPCS"))))
295 end end
297 list = QuestHelper_StaticData[QuestHelper.locale].objective
298 list = list and list.monster
299 if list then for n in pairs(list) do
300 self:AddResult("monster", n, fuzzyCompare(input, string.upper(n)))
301 yield_countdown = yield_countdown - 1
302 if yield_countdown == 0 then
303 yield_countdown = yield_countdown_max
304 coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_STATIC", QHText("SEARCHING_NPCS"))))
306 end end
309 if search_loc then
310 local _, _, region, x, y = string.find(input, "^%s*([^%d%.]-)%s*([%d%.]+)%s*[,;:]?%s*([%d%.]+)%s*$")
312 if region then
313 x, y = tonumber(x), tonumber(y)
314 if x and y then
315 x, y = x*0.01, y*0.01
317 if region == "" then
318 self:AddResult("loc", string.format("%d,%.3f,%.3f", QuestHelper.i, x, y), 0)
319 else
320 for i, name in pairs(QuestHelper_NameLookup) do
321 self:AddResult("loc", string.format("%d,%.3f,%.3f", i, x, y), fuzzyCompare(region, string.upper(name)))
322 yield_countdown = yield_countdown - 1
323 if yield_countdown == 0 then
324 yield_countdown = yield_countdown_max
325 coroutine.yield(QHFormat("SEARCHING_STATE", QHText("SEARCHING_ZONES")))
333 return QHText("SEARCHING_DONE")
336 local function ReturnArgument(x)
337 return x
340 function search_frame:PerformSearch(input)
341 QuestHelper:TextOut("/qh find is currently disabled. Sorry! I'll get it back in once I can.")
342 do return end
343 if not self.routine then
344 self.query = string.gsub(input, "|c.-|H.-|h%[(.-)%]|h|r", ReturnArgument)
345 self.routine = coroutine.create(self.SearchRoutine)
346 self:Show()
347 QH_Hook(self, "OnUpdate", self.OnUpdate)
351 function QuestHelper:PerformSearch(query)
352 search_frame:PerformSearch(query)
355 SLASH_QuestHelperFind1 = "/qhfind"
356 SLASH_QuestHelperFind2 = "/find"
357 SlashCmdList["QuestHelperFind"] = function (text) QuestHelper:PerformSearch(text) end