Files

218 lines
7.1 KiB
Lua

---@class QuestieSearch
local QuestieSearch = QuestieLoader:CreateModule("QuestieSearch");
---@type QuestieDB
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
QuestieSearch.types = {"npc", "object", "item", "quest"}
-- Save search results, so the next search has a smaller set to search
QuestieSearch.LastResult = {
query = '',
queryType = '',
quest = {},
npc = {},
object = {},
item = {},
}
local function _ResetResults()
QuestieSearch.LastResult = {
query = '',
queryType = '',
quest = {},
npc = {},
object = {},
item = {},
}
end
-- Execute a search by name for all types
function QuestieSearch:ByName(query)
_ResetResults()
for _, type in pairs(QuestieSearch.types) do
QuestieSearch:Search(query, type, "chars")
end
return QuestieSearch.LastResult
end
-- Execute a search by ID for all types
function QuestieSearch:ByID(query)
_ResetResults()
for _,type in pairs(QuestieSearch.types) do
QuestieSearch:Search(query, type, "int")
end
return QuestieSearch.LastResult
end
--[[
QuestieSearch:Search
This function searches a value from the database, including partial matches.
Adds the values to QuestieSearch.LastResult.
Returns table of found IDs for the selected search type.
Parameters:
query The search string/int
searchType Which database to search, possible values:
"npc"
"object"
"item"
"quest"
queryType Which type of search to run, possible values:
"chars"
"int"
Optional. Default: "chars"
--]]
function QuestieSearch:Search(rawQuery, searchType, queryType)
queryType = queryType or "chars"
local databaseQueryHandle
local databaseKeys
local overrideKeys
local ascensionKeys
if searchType == "npc" then
databaseQueryHandle = QuestieDB.QueryNPCSingle
databaseKeys = QuestieDB.NPCPointers
overrideKeys = QuestieDB.npcDataOverrides
ascensionKeys = QuestieDB.ascensionNpcIds
elseif searchType == "object" then
databaseQueryHandle = QuestieDB.QueryObjectSingle
databaseKeys = QuestieDB.ObjectPointers
overrideKeys = QuestieDB.objectDataOverrides
ascensionKeys = QuestieDB.ascensionObjectIds
elseif searchType == "item" then
databaseQueryHandle = QuestieDB.QueryItemSingle
databaseKeys = QuestieDB.ItemPointers
overrideKeys = QuestieDB.itemDataOverrides
ascensionKeys = QuestieDB.ascensionItemIds
elseif searchType == "quest" then
databaseQueryHandle = QuestieDB.QueryQuestSingle
databaseKeys = QuestieDB.QuestPointers
overrideKeys = QuestieDB.questDataOverrides
ascensionKeys = QuestieDB.ascensionQuestIds
else
return
end
local sanitizedQuery
local strictSearch = false
if type(rawQuery) ~= "number" then
local stringFirst, stringLast = rawQuery:sub(1, 1), rawQuery:sub(-1)
if (stringFirst == '"' or stringFirst == "'") and (stringLast == stringFirst) then
strictSearch = true
sanitizedQuery = rawQuery:sub(2, -2)
else
sanitizedQuery = rawQuery
end
else
sanitizedQuery = rawQuery
end
local searchCount = 0;
local isTextSearch = queryType == "chars"
local isIdSearch = queryType == "int" and tonumber(sanitizedQuery) ~= nil
local lastResults = QuestieSearch.LastResult[searchType]
-- Fast-path for ID search: allow direct lookups even when the ID isn't in the compiled pointer tables
-- (e.g. Ascension custom entries injected via *Overrides*).
if isIdSearch then
local directId = tonumber(sanitizedQuery)
if directId then
local directName = databaseQueryHandle(directId, "name")
if directName then
lastResults[directId] = true
QuestieSearch.LastResult.query = rawQuery
return lastResults
end
end
end
if isTextSearch then
local queryToFind = string.lower(sanitizedQuery)
local visited = {}
local function _RunNameMatch(id)
local name = databaseQueryHandle(id, "name") -- Some entries don't have a 'name' because of the way we load corrections
if strictSearch then
if name and (string.lower(name) == queryToFind) then -- strict search
searchCount = searchCount + 1;
QuestieSearch.LastResult[searchType][id] = true;
else
QuestieSearch.LastResult[searchType][id] = nil;
end
else
if name and string.find(string.lower(name), queryToFind) then -- fuzzy search
searchCount = searchCount + 1;
QuestieSearch.LastResult[searchType][id] = true;
else
QuestieSearch.LastResult[searchType][id] = nil;
end
end
end
if type(databaseKeys) == "table" then
for id, _ in pairs(databaseKeys) do
visited[id] = true
_RunNameMatch(id)
end
end
-- Include custom data injected via overrides, since these IDs may not exist in *Pointers.
if type(overrideKeys) == "table" then
for id, _ in pairs(overrideKeys) do
if not visited[id] then
_RunNameMatch(id)
end
end
end
-- Also include Ascension custom IDs
if type(ascensionKeys) == "table" then
for id, _ in pairs(ascensionKeys) do
if not visited[id] then
visited[id] = true
_RunNameMatch(id)
end
end
end
elseif isIdSearch then
-- Fast-path for custom data injected via overrides (may not exist in *Pointers)
local qid = tonumber(sanitizedQuery)
if qid and databaseQueryHandle(qid, "name") then
lastResults[qid] = true
searchCount = searchCount + 1
end
for id, _ in pairs(databaseKeys) do
if strictSearch then
if tostring(id) == sanitizedQuery then -- strict search
-- We have a search result or a favourite to display
searchCount = searchCount + 1;
lastResults[id] = true;
else
-- This entry doesn't meet the search criteria, removed from the last results
lastResults[id] = nil;
end
else
if string.find(tostring(id), sanitizedQuery) then -- fuzzy search
-- We have a search result or a favourite to display
searchCount = searchCount + 1;
lastResults[id] = true;
else
-- This entry doesn't meet the search criteria, removed from the last results
lastResults[id] = nil;
end
end
end
end
QuestieSearch.LastResult.query = rawQuery
return QuestieSearch.LastResult[searchType]
end