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")
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)
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
)
35 QuestHelper
:TextOut("Searching co-routine just exploded: "..display
)
40 QH_Hook(self
, "OnUpdate", nil)
45 function QuestHelper
:PerformCustomSearch(func
)
46 if not search_frame
:GetScript("OnUpdate") then
48 QH_Hook(search_frame
, "OnUpdate", func
)
52 function QuestHelper
:StopCustomSearch()
53 if not search_frame
.routine
then
55 QH_Hook(search_frame
, "OnUpdate", nil)
62 -- This next bit of stuff is for fuzzy string comarisons.
65 local row
, prow
= {}, {}
73 a
[j
-64] = i
==j
and 0 or 1
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
])
88 -- Characters that sound similar. At least in my opinion.
89 setgroup("BCDFGHJKLMNPQRSTVWXZ", 0.9)
90 setgroup("AEIOUY", 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]
105 elseif b
>= 65 and b
<= 90 then
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
124 row
, prow
= prow
, row
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
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))
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
))
157 self
:TextOut(QHText("UNKNOWN_OBJ"))
161 function search_frame
:CreateResultItem(r
, menu
)
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)
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
)
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"))
185 for i
, r
in ipairs(self
.results
) do
186 self
:CreateResultItem(r
, menu
)
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
205 local m
= math
.floor((mn
+mx
)*0.5)
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.
222 QuestHelper
:ReleaseTable(table.remove(r
, 20))
225 local obj
= QuestHelper
:CreateTable()
229 table.insert(r
, mn
, obj
)
233 function search_frame
:SearchRoutine(input
)
235 for obj
in pairs(QuestHelper
.user_objectives
) do
236 self
:AddResult(obj
.cat
, obj
.obj
, 0)
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
254 search_item
, search_npc
, search_loc
= true, true, true
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
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"))))
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"))))
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"))))
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"))))
310 local _
, _
, region
, x
, y
= string.find(input
, "^%s*([^%d%.]-)%s*([%d%.]+)%s*[,;:]?%s*([%d%.]+)%s*$")
313 x
, y
= tonumber(x
), tonumber(y
)
315 x
, y
= x
*0.01, y
*0.01
318 self
:AddResult("loc", string.format("%d,%.3f,%.3f", QuestHelper
.i
, x
, y
), 0)
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
)
340 function search_frame
:PerformSearch(input
)
341 QuestHelper
:TextOut("/qh find is currently disabled. Sorry! I'll get it back in once I can.")
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
)
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