480 lignes
15 Kio
Lua
480 lignes
15 Kio
Lua
QuestHelper_File["custom.lua"] = "4.0.1.$svnversion$"
|
|
QuestHelper_Loadtime["custom.lua"] = GetTime()
|
|
|
|
local search_frame = CreateFrame("Button", nil, UIParent)
|
|
search_frame.text = search_frame:CreateFontString()
|
|
search_frame.text:SetFont(QuestHelper.font.sans, 15)
|
|
search_frame.text:SetTextColor(1, 1, 1)
|
|
search_frame.text:SetJustifyH("CENTER")
|
|
search_frame.text:SetJustifyV("MIDDLE")
|
|
search_frame.text:SetDrawLayer("OVERLAY")
|
|
search_frame.text:SetAllPoints()
|
|
search_frame.text:Show()
|
|
search_frame.background = search_frame:CreateTexture()
|
|
search_frame.background:SetTexture(0, 0, 0, 0.5)
|
|
search_frame.background:SetDrawLayer("BACKGROUND")
|
|
search_frame.background:SetAllPoints()
|
|
search_frame.background:Show()
|
|
search_frame:SetPoint("CENTER", UIParent, "CENTER")
|
|
search_frame:Hide()
|
|
|
|
search_frame.results = {}
|
|
|
|
function search_frame:SetText(text)
|
|
self.text:SetText(text)
|
|
self:SetWidth(self.text:GetWidth()+10)
|
|
self:SetHeight(self.text:GetHeight()+10)
|
|
end
|
|
|
|
function search_frame:OnUpdate()
|
|
if self.routine and coroutine.status(self.routine) ~= "dead" then
|
|
local no_error, display = coroutine.resume(self.routine, self, self.query)
|
|
if no_error then
|
|
self:SetText(display)
|
|
else
|
|
QuestHelper:TextOut("Searching co-routine just exploded: "..display)
|
|
end
|
|
else
|
|
self:ShowResults()
|
|
self.routine = nil
|
|
QH_Hook(self, "OnUpdate", nil)
|
|
self:Hide()
|
|
end
|
|
end
|
|
|
|
function QuestHelper:PerformCustomSearch(func)
|
|
if not search_frame:GetScript("OnUpdate") then
|
|
search_frame:Show()
|
|
QH_Hook(search_frame, "OnUpdate", func)
|
|
end
|
|
end
|
|
|
|
function QuestHelper:StopCustomSearch()
|
|
if not search_frame.routine then
|
|
search_frame:Hide()
|
|
QH_Hook(search_frame, "OnUpdate", nil)
|
|
end
|
|
end
|
|
|
|
|
|
do return end
|
|
|
|
-- This next bit of stuff is for fuzzy string comarisons.
|
|
|
|
|
|
local row, prow = {}, {}
|
|
|
|
local difftable = {}
|
|
|
|
for i = 65,90 do
|
|
local a = {}
|
|
difftable[i-64] = a
|
|
for j = 65,90 do
|
|
a[j-64] = i==j and 0 or 1
|
|
end
|
|
end
|
|
|
|
local function setgroup(a, w)
|
|
for i = 1,string.len(a)-1 do
|
|
for j = i+1,string.len(a) do
|
|
local c1, c2 = string.byte(a,i)-64, string.byte(a,j)-64
|
|
|
|
difftable[c1][c2] = math.min(w, difftable[c1][c2])
|
|
difftable[c2][c1] = math.min(w, difftable[c2][c1])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Characters that sound similar. At least in my opinion.
|
|
setgroup("BCDFGHJKLMNPQRSTVWXZ", 0.9)
|
|
setgroup("AEIOUY", 0.6)
|
|
setgroup("TD", 0.6)
|
|
setgroup("CKQ", 0.4)
|
|
setgroup("MN", 0.4)
|
|
setgroup("EIY", 0.3)
|
|
setgroup("UO", 0.2)
|
|
setgroup("SZ", 0.6)
|
|
|
|
local function diffness(a, b)
|
|
if a >= 65 and a <=90 then
|
|
if b >= 65 and b <= 90 then
|
|
return difftable[a-64][b-64]
|
|
else
|
|
return 1
|
|
end
|
|
elseif b >= 65 and b <= 90 then
|
|
return 1
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
|
|
local function fuzzyCompare(a, b)
|
|
local m, n = string.len(a), string.len(b)
|
|
|
|
if n == 0 or m == 0 then
|
|
return n == m and 0 or 1
|
|
end
|
|
|
|
for j = 1,n+1 do
|
|
row[j] = j-1
|
|
end
|
|
|
|
for i = 1,m do
|
|
row, prow = prow, row
|
|
row[1] = i
|
|
|
|
for j = 1,n do
|
|
row[j+1] = math.min(prow[j+1]+1, row[j]+.4, prow[j]+diffness(string.byte(a,i), string.byte(b,j)))
|
|
end
|
|
end
|
|
|
|
return row[n+1]/math.max(n,m)
|
|
end
|
|
|
|
function QuestHelper:ToggleUserObjective(cat, what)
|
|
local objective = self:GetObjective(cat, what)
|
|
|
|
if self.user_objectives[objective] then
|
|
self:TextOut(QHFormat("REMOVED_OBJ", self.user_objectives[objective]))
|
|
self:RemoveObjectiveWatch(objective, self.user_objectives[objective])
|
|
self.user_objectives[objective] = nil
|
|
elseif objective:Known() then
|
|
local name
|
|
if cat == "loc" then
|
|
local _, _, i, x, y = string.find(what, "^(%d+),([%d%.]+),([%d%.]+)$")
|
|
name = QHFormat("USER_OBJ", self:HighlightText(QuestHelper_NameLookup[tonumber(i)])..": "..self:HighlightText(x*100)..", "..self:HighlightText(y*100))
|
|
else
|
|
name = QHFormat("USER_OBJ", self:HighlightText(string.gsub(cat, "^(.)", string.upper))..": "..self:HighlightText(what))
|
|
end
|
|
|
|
objective.priority = 1
|
|
self.user_objectives[objective] = name
|
|
self:AddObjectiveWatch(objective, name)
|
|
|
|
self:TextOut(QHFormat("CREATED_OBJ", name))
|
|
else
|
|
self:TextOut(QHText("UNKNOWN_OBJ"))
|
|
end
|
|
end
|
|
|
|
function search_frame:CreateResultItem(r, menu)
|
|
local item
|
|
|
|
if r.cat == "loc" then
|
|
local _, _, i, x, y = string.find(r.what, "^(%d+),([%d%.]+),([%d%.]+)$")
|
|
item = QuestHelper:CreateMenuItem(menu, QuestHelper_NameLookup[tonumber(i)]..": "..(x*100)..", "..(y*100).." ["..QuestHelper:PercentString(1-r.w).."]")
|
|
item:AddTexture(QuestHelper:CreateIconTexture(item, 6), true)
|
|
else
|
|
item = QuestHelper:CreateMenuItem(menu, r.what .. " ["..QuestHelper:PercentString(1-r.w).."]")
|
|
item:AddTexture(QuestHelper:CreateIconTexture(item, (r.cat == "monster" and 1) or 2), true)
|
|
end
|
|
|
|
item:SetFunction(QuestHelper.ToggleUserObjective, QuestHelper, r.cat, r.what)
|
|
|
|
return item
|
|
end
|
|
|
|
function search_frame:ShowResults()
|
|
local menu = QuestHelper:CreateMenu()
|
|
QuestHelper:CreateMenuTitle(menu, QHText("RESULTS_TITLE"))
|
|
|
|
if #self.results == 0 then
|
|
QuestHelper:CreateMenuItem(menu, QHText("NO_RESULTS"))
|
|
else
|
|
for i, r in ipairs(self.results) do
|
|
self:CreateResultItem(r, menu)
|
|
end
|
|
end
|
|
|
|
menu:ShowAtCursor()
|
|
self:ClearResults()
|
|
end
|
|
|
|
function search_frame:ClearResults()
|
|
while #self.results > 0 do
|
|
QuestHelper:ReleaseTable(table.remove(self.results))
|
|
end
|
|
end
|
|
|
|
function search_frame:AddResult(cat, what, w)
|
|
local r = self.results
|
|
local mn, mx = 1, #r+1
|
|
|
|
while mn ~= mx do
|
|
local m = math.floor((mn+mx)*0.5)
|
|
|
|
if r[m].w < w then
|
|
mn = m+1
|
|
else
|
|
mx = m
|
|
end
|
|
end
|
|
|
|
if mn <= 20 then
|
|
if r[mn] and r[mn].cat == cat and r[mn].what == what then
|
|
-- Don't add the same item twice.
|
|
-- Might miss it if multiple items have the same score. Dont care.
|
|
return
|
|
end
|
|
|
|
if #r >= 20 then
|
|
QuestHelper:ReleaseTable(table.remove(r, 20))
|
|
end
|
|
|
|
local obj = QuestHelper:CreateTable()
|
|
obj.cat = cat
|
|
obj.what = what
|
|
obj.w = w
|
|
table.insert(r, mn, obj)
|
|
end
|
|
end
|
|
|
|
local function searchNPC(input, yield_countdown, yield_countdown_max)
|
|
local list = QuestHelper_Objectives_Local["monster"]
|
|
if list then
|
|
for n in pairs(list) do
|
|
self:AddResult("monster", n, fuzzyCompare(input, string.upper(n)))
|
|
yield_countdown = yield_countdown - 1
|
|
if yield_countdown == 0 then
|
|
yield_countdown = yield_countdown_max
|
|
coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_LOCAL", QHText("SEARCHING_NPCS"))))
|
|
end
|
|
end
|
|
end
|
|
|
|
list = QuestHelper_StaticData[QuestHelper.locale].objective
|
|
list = list and list.monster
|
|
if list then
|
|
for n in pairs(list) do
|
|
self:AddResult("monster", n, fuzzyCompare(input, string.upper(n)))
|
|
yield_countdown = yield_countdown - 1
|
|
if yield_countdown == 0 then
|
|
yield_countdown = yield_countdown_max
|
|
coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_STATIC", QHText("SEARCHING_NPCS"))))
|
|
end
|
|
end
|
|
end
|
|
|
|
return yield_countdown
|
|
end
|
|
|
|
local elders = {
|
|
["Alliance"] = {
|
|
["Bladeswift"] = "Darnassus 39 32",
|
|
["Bronzebeard"] = "Ironforge 29 16",
|
|
["Hammershout"] = "Elwynn Forest 34 50"
|
|
},
|
|
["Horde"] = {
|
|
["Darkhorn"] = "Orgrimmar 52 60",
|
|
["Wheathoof"] = "Thunder Bluff 72 33",
|
|
["Darkcore"] = "Undercity 67 38"
|
|
},
|
|
["Kalimdor"] = {
|
|
["Riversong"] = "Ashenvale 35 48",
|
|
["Skygleam"] = "Azshara 64 79",
|
|
["High Mountain"] = "Southern Barrens 41 47", -- 41.5 47.5
|
|
["Moonwarden"] = "Northern Barrens 48 59", -- 48.5 59.2
|
|
["Windtotem"] = "Northern Barrens 68 69",
|
|
["Starweave"] = "Darkshore 49 19", -- 49.5 19.0
|
|
["Runetotem"] = "Durotar 54 44",
|
|
["Nightwind"] = "Felwood 37 52",
|
|
["Grimtotem"] = "Feralas 76 37",
|
|
["Mistwalker"] = "Feralas 62 31",
|
|
["Bloodhoof"] = "Mulgore 48 53",
|
|
["Bladesing"] = "Silithus 53 35",
|
|
["Primestone"] = "Silithus 30 13", -- 30.7 13.3
|
|
["Dreamseer"] = "Tanaris 50 28",
|
|
["Ragetotem"] = "Tanaris 36 80",
|
|
["Bladeleaf"] = "Teldrassil 57 53",
|
|
["Skyseer"] = "Thousand Needles 45 50",
|
|
["Morningdew"] = "Thousand Needles 77 75", -- 77.0 75.6
|
|
["Thunderhorn"] = "Un'Goro Crater 51 75",
|
|
["Brightspear"] = "Winterspring 53 56", -- 53.0 56.7
|
|
["Stonespire"] = "Winterspring 60 50", -- 60.0 50.0
|
|
},
|
|
["Eastern Kingdoms"] = {
|
|
["Bellowrage"] = "Blasted Lands 54 49",
|
|
["Rumblerock"] = "Burning Steppes 70 45",
|
|
["Dawnstrider"] = "Burning Steppes 53 24",
|
|
["Goldwell"] = "Dun Morogh 53 49",
|
|
["Windrun"] = "Eastern Plaguelands 35 68",
|
|
["Snowcrown"] = "Eastern Plaguelands 75 54",
|
|
["Stormbrow"] = "Elwynn Forest 40 63",
|
|
["Highpeak"] = "Hinterlands 50 48",
|
|
["Silvervein"] = "Loch Modan 33 46",
|
|
["Ironband"] = "Searing Gorge 21 79",
|
|
["Obsidian"] = "Silverpine Forest 45 41",
|
|
["Starglade"] = "Stranglethorn Vale 63 22", -- Jungle 71 34
|
|
["Winterhoof"] = "Stranglethorn Vale 37 79", -- Cape 39 72
|
|
["Graveborn"] = "Tirisfal Glades 61 53",
|
|
["Moonstrike"] = "Western Plaguelands 69 73",
|
|
["Meadowrun"] = "Western Plaguelands 63 36",
|
|
["Skychaser"] = "Westfall 56 47"
|
|
},
|
|
["Northrend"] = {
|
|
["Arp"] = "Borean Tundra 57 44",
|
|
["Northal"] = "Borean Tundra 34 34",
|
|
["Pamuya"] = "Borean Tundra 43 50",
|
|
["Sardis"] = "Borean Tundra 59 66",
|
|
["Morthie"] = "Dragonblight 30 56",
|
|
["Skywarden"] = "Dragonblight 35 48",
|
|
["Thoim"] = "Dragonblight 49 78",
|
|
["Beldak"] = "Grizzly Hills 61 28",
|
|
["Lunaro"] = "Grizzly Hills 81 37",
|
|
["Whurain"] = "Grizzly Hills 64 47",
|
|
["Bluewolf"] = "Wintergrasp 49 14",
|
|
["Sandrene"] = "Sholazar Basin 50 64",
|
|
["Wanikaya"] = "Sholazar Basin 64 49",
|
|
["Fargal"] = "Storm Peaks 29 74",
|
|
["Graymane"] = "Storm Peaks 41 85",
|
|
["Muraco"] = "Storm Peaks 64 51",
|
|
["Stonebeard"] = "Storm Peaks 31 38",
|
|
["Tauros"] = "Zul'Drak 59 56"
|
|
}
|
|
}
|
|
|
|
local function searchItem(input, yield_countdown, yield_countdown_max)
|
|
local list = QuestHelper_Objectives_Local["item"]
|
|
if list then
|
|
for n in pairs(list) do
|
|
self:AddResult("item", n, fuzzyCompare(input, string.upper(n)))
|
|
yield_countdown = yield_countdown - 1
|
|
if yield_countdown == 0 then
|
|
yield_countdown = yield_countdown_max
|
|
coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_LOCAL", QHText("SEARCHING_ITEMS"))))
|
|
end
|
|
end
|
|
end
|
|
|
|
list = QuestHelper_StaticData[QuestHelper.locale].objective
|
|
list = list and list.item
|
|
if list then
|
|
for n in pairs(list) do
|
|
self:AddResult("item", n, fuzzyCompare(input, string.upper(n)))
|
|
yield_countdown = yield_countdown - 1
|
|
if yield_countdown == 0 then
|
|
yield_countdown = yield_countdown_max
|
|
coroutine.yield(QHFormat("SEARCHING_STATE", QHFormat("SEARCHING_STATIC", QHText("SEARCHING_ITEMS"))))
|
|
end
|
|
end
|
|
end
|
|
|
|
return yield_countdown
|
|
end
|
|
|
|
local function searchLoc(input, yield_countdown, yield_countdown_max)
|
|
local _, _, region, x, y = string.find(input, "^%s*([^%d%.]-)%s*([%d%.]+)%s*[,;:]?%s*([%d%.]+)%s*$")
|
|
|
|
if region then
|
|
x, y = tonumber(x), tonumber(y)
|
|
if x and y then
|
|
x, y = x*0.01, y*0.01
|
|
|
|
if region == "" then
|
|
self:AddResult("loc", string.format("%d,%.3f,%.3f", QuestHelper.i, x, y), 0)
|
|
else
|
|
for i, name in pairs(QuestHelper_NameLookup) do
|
|
self:AddResult("loc", string.format("%d,%.3f,%.3f", i, x, y), fuzzyCompare(region, string.upper(name)))
|
|
yield_countdown = yield_countdown - 1
|
|
if yield_countdown == 0 then
|
|
yield_countdown = yield_countdown_max
|
|
coroutine.yield(QHFormat("SEARCHING_STATE", QHText("SEARCHING_ZONES")))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return yield_countdown
|
|
end
|
|
|
|
local function searchElders(input, yield_countdown, yield_countdown_max)
|
|
local yc = yield_countdown
|
|
local achievement_match, elder_match = false, false
|
|
for achievement, eldrs in pairs(elders) do
|
|
if input and input ~= "" then
|
|
if input == string.upper(achievement) then achievement_match = true end
|
|
end
|
|
|
|
for elder, elder_loc in pairs(eldrs) do
|
|
if input and input ~= "" then
|
|
if achievement_match then -- just add it
|
|
yc = searchLoc(string.upper(elder_loc), yc, yield_countdown_max)
|
|
elseif input == string.upper(elder) then -- We have input and it's not an achievement, so it must be an elder.
|
|
elder_match = true
|
|
yc = searchLoc(string.upper(elder_loc), yc, yield_countdown_max)
|
|
break -- We've found him or her.
|
|
end -- No need for else here. We've got input so we either have an achievement or we have an elder.
|
|
else -- We came in without an input, therefore we add all.
|
|
yc = searchLoc(string.upper(elder_loc), yc, yield_countdown_max)
|
|
end
|
|
end
|
|
|
|
if achievement_match or elder_match then break end -- We've done our match.
|
|
end
|
|
|
|
return yc
|
|
end
|
|
|
|
function search_frame:SearchRoutine(input)
|
|
if input == "" then
|
|
for obj in pairs(QuestHelper.user_objectives) do
|
|
self:AddResult(obj.cat, obj.obj, 0)
|
|
end
|
|
return
|
|
end
|
|
|
|
input = string.upper(input)
|
|
local _, _, command, argument = string.find(input, "^%s*([^%s]-)%s+(.-)%s*$")
|
|
|
|
local search_item, search_npc, search_loc, search_elders = false, false, false, false
|
|
|
|
local yield_countdown_max = math.max(1, math.floor(2000/string.len(input)+0.5))
|
|
local yield_countdown = yield_countdown_max
|
|
|
|
if command and argument then
|
|
if command == "ITEM" then
|
|
searchItem(argument, yield_countdown, yield_countdown_max)
|
|
elseif command == "NPC" or command == "MONSTER" then
|
|
searchNPC(argument, yield_countdown, yield_countdown_max)
|
|
elseif command == "LOCATION" or command == "LOC" then
|
|
searchLoc(argument, yield_countdown, yield_countdown_max)
|
|
elseif command == "ELDER" or command == "ELDERS" then
|
|
searchElders(argument, yield_countdown, yield_countdown_max)
|
|
else
|
|
yield_countdown = searchItem(input, yield_countdown, yield_countdown_max)
|
|
yield_countdown = searchNPC(input, yield_countdown, yield_countdown_max)
|
|
searchLoc(input, yield_countdown, yield_countdown_max)
|
|
end
|
|
else
|
|
yield_countdown = searchItem(input, yield_countdown, yield_countdown_max)
|
|
yield_countdown = searchNPC(input, yield_countdown, yield_countdown_max)
|
|
searchLoc(input, yield_countdown, yield_countdown_max)
|
|
end
|
|
|
|
return QHText("SEARCHING_DONE")
|
|
end
|
|
|
|
local function ReturnArgument(x)
|
|
return x
|
|
end
|
|
|
|
function search_frame:PerformSearch(input)
|
|
QuestHelper:TextOut("/qh find is currently disabled. Sorry! I'll get it back in once I can.")
|
|
do return end
|
|
if not self.routine then
|
|
self.query = string.gsub(input, "|c.-|H.-|h%[(.-)%]|h|r", ReturnArgument)
|
|
self.routine = coroutine.create(self.SearchRoutine)
|
|
self:Show()
|
|
QH_Hook(self, "OnUpdate", self.OnUpdate)
|
|
end
|
|
end
|
|
|
|
function QuestHelper:PerformSearch(query)
|
|
search_frame:PerformSearch(query)
|
|
end
|
|
|
|
SLASH_QuestHelperFind1 = "/qhfind"
|
|
SLASH_QuestHelperFind2 = "/find"
|
|
SlashCmdList["QuestHelperFind"] = function (text) QuestHelper:PerformSearch(text) end
|