local GetTime = QuestHelper_GetTime QuestHelper_File["db_get.lua"] = "4.0.1.$svnversion$" QuestHelper_Loadtime["db_get.lua"] = GetTime() local dev_mode = (QuestHelper_File["db_get.lua"] == "Development Version") QHDB_Export = {} local export = false -- yoink --[[ local QHDB_temp = QHDB QHDB = nil local QHDB = QHDB_temp]] QuestHelper: Assert(#QHDB == 4, "Please make sure that you are loading a locale.") local weak_v = { __mode = 'v' } local weak_k = { __mode = 'k' } local cache = {} local frequencies = setmetatable({}, weak_k) -- guhhh just want this to work local freq_group = setmetatable({}, weak_k) local freq_id = setmetatable({}, weak_k) local function DBC_Get(group, id) if not cache[group] then return end return cache[group][id] end local function DBC_Put(group, id, item) if not cache[group] then cache[group] = setmetatable({}, weak_v) end QuestHelper: Assert(not cache[group][id]) cache[group][id] = item --DB_how_many_are_used() end local function mark(tab, tomark) for k, v in pairs(tab) do if k ~= "__owner" and type(v) == "table" then mark(v, tomark) end end tab.__owner = tomark end local function read_adaptint(data, offset) local stx = 0 local acu = 1 while true do local v = strbyte(data, offset) QuestHelper: Assert(v, string.format("%d %d", #data, offset)) stx = stx + acu * math.floor(v / 2) offset = offset + 1 acu = acu * 128 if mod(v, 2) == 0 then break end end return stx, offset end local function search_index(index, data, item) --[[Header format: Itemid (0 for endnode) Offset Length Rightlink]] local cofs = 1 --[[ assert(index and type(index) == "string") ]] --[[ assert(data and type(data) == "string") ]] while true do local idx, ofs, len, rlink idx, cofs = read_adaptint(index, cofs) if idx == 0 then return end ofs, cofs = read_adaptint(index, cofs) len, cofs = read_adaptint(index, cofs) rlink, cofs = read_adaptint(index, cofs) if idx == item then return strsub(data, ofs, ofs + len) end if idx < item then cofs = cofs + rlink end end end local initted = false function DB_Init() QuestHelper: Assert(not initted) for _, db in ipairs(QHDB) do for _, v in pairs(db) do --print("db", not not v.__dictionary, not not v.__tokens) if v.__dictionary and v.__tokens then local redictix = v.__dictionary if not redictix:find("\"") then redictix = redictix .. "\"" end if not redictix:find(",") then redictix = redictix .. "," end if not redictix:find("\\") then redictix = redictix .. "\\" end local tokens = loadstring("return {" .. QH_LZW_Decompress_Dicts_Arghhacky(v.__tokens, redictix) .. "}")() QuestHelper: Assert(tokens) local _, _, prep = QH_LZW_Prepare_Arghhacky(v.__dictionary, tokens) QuestHelper: Assert(prep) QuestHelper: Assert(type(prep) == "table") v.__tokens = prep end end end initted = true QH_UpdateQuests() -- just in case it's been waiting on us (it has almost certainly been waiting on us) end function DB_Ready() return initted end function DB_HasItem(group, id) QuestHelper: Assert(initted) if group == "quest" then if type(id) == "number" and id > 0 then return true else return false end end for _, db in ipairs(QHDB) do if db[group] then if db[group][id] then return true end if type(id) == "number" and id > 0 and db[group].__serialize_index and db[group].__serialize_data and search_index(db[group].__serialize_index, db[group].__serialize_data, id) then return true end end end return false end local function GetBlizzardQuestInfoFrameMainLoop(qid) local POIFrame, questFrame QuestMapUpdateAllQuests() QuestPOIUpdateIcons() WorldMapFrame_UpdateQuests() for i = 1, MAX_NUM_QUESTS do questFrame = _G["WorldMapQuestFrame" .. i] if questFrame then if questFrame.questId == qid then POIFrame = questFrame.poiIcon break end end end return questFrame, POIFrame end local function GetBlizzardQuestInfoFrame(qid, map) if map then SetMapByID(map) end QuestMapUpdateAllQuests() QuestPOIUpdateIcons() WorldMapFrame_UpdateQuests() local POIFrame local questFrame local dLvl if map and GetNumDungeonMapLevels() > 0 then -- Suspicion is that each dungeon level has to be iterated. for i = 1, GetNumDungeonMapLevels() do SetDungeonMapLevel(i) questFrame, POIFrame = GetBlizzardQuestInfoFrameMainLoop(qid) if POIFrame then dLvl = i break end end else -- Only need to call once questFrame, POIFrame = GetBlizzardQuestInfoFrameMainLoop(qid) end return questFrame, POIFrame, dLvl or 0 end local function GetBlizzardQuestInfo(qid) local questFrame, POIFrame, qdLvl = GetBlizzardQuestInfoFrame(qid) local mapId = GetCurrentMapAreaID() local c, z local dLvl = false local qMapId if GetNumDungeonMapLevels() > 0 then dLvl = GetCurrentMapDungeonLevel() end if not POIFrame then -- Iterate over all maps to try and find it (we are of course assuming that the player has the quest, otherwise we will be SOL. local maps = QuestHelper.LibMapData:GetAllMapIDs() for _, map in pairs(maps) do if map >= 0 and not QuestHelper.LibMapData:IsContinentMap(map) then questFrame, POIFrame, qdLvl = GetBlizzardQuestInfoFrame(qid, map) if POIFrame then qMapId = map break end end end c, z = GetCurrentMapContinent(), GetCurrentMapZone() SetMapByID(mapId) if not POIFrame then return nil end -- At this point we either have the quest or we are SOL. else qMapId = mapId c, z = GetCurrentMapContinent(), GetCurrentMapZone() end local _, _, _, x, y = POIFrame:GetPoint() if not x or not y then return nil end local frame = WorldMapDetailFrame local width, height = frame:GetWidth(), frame:GetHeight() local wm_scale, poi_scale = frame:GetScale(), POIFrame:GetScale() -- Convert from yards to % local cx = ((x / (wm_scale / poi_scale)) / width) local cy = ((-y / (wm_scale / poi_scale)) / height) if cx < 0 or cx > 1 or cy < 0 or cy > 1 then return nil end -- Now we want to convert to proper format for QH. -- Look at WoWPro:findBlizzCoords(qid) for remainder local _, contX, contY = QuestHelper.Astrolabe:GetAbsoluteContinentPosition(c, z, cx, cy) --contX, contY = contX / QuestHelper.Astrolabe:GetZoneWidth(c, 0), contY / QuestHelper.Astrolabe:GetZoneHeight(c, 0) local solid = { ["continent"] = c, contX - 10, contY - 10, contX + 10, contY - 10, contX + 10, contY + 10, contX - 10, contY + 10 } local ret = {} ret.solid = solid ret.criteria = {} local questIdx = GetQuestLogIndexByID(qid) local numCrit = GetNumQuestLeaderBoards(questIdx) local loc = { ["loc"] = { { ["p"] = qMapId, ["x"] = contX, ["y"] = contY} } } for i = 1, numCrit do table.insert(ret.criteria, loc) end ret.name = GetQuestLogTitle(questIdx) ret.Blizzard = true return ret end local function GetLightHeadedQuestInfo(qid) if not LightHeaded or type(LightHeaded) ~= "table" then return nil end -- LH not loaded local npcid, npcname, stype local coords = {} _, _, _, _, _, _, _, stype, npcname, npcid = LightHeaded:GetQuestInfo(qid) -- Note: If we want the quest giver, we need fields 5, 6 and 7 above, rather than what we have gotten. -- if stype == "npc" then local data = LightHeaded:LoadNPCData(tonumber(npcid)) if not data then return end -- LightHeaded has no clue about the given NPC, despite giving us the id. for zid,x,y in data:gmatch("([^,]+),([^,]+),([^:]+):") do table.insert(coords, {["p"] = zid, ["x"] = x, ["y"] = y}) end end --can't return coordinates until we know more about what we are getting and then convert accordingly. for k, v in pairs(coords) do for k1, v1 in pairs(v) do print(k, k1, v1) end end --return coords return nil end local function GetSelfQuestInfo(qid) return nil end -- Returns x and y in yards relative to something... Continent? local function GetQuestInfo(qid) assert(type(qid) == "number") local coords = GetSelfQuestInfo(qid) --if not coords then coords = GetLightHeadedQuestInfo(qid) end if not coords then coords = GetBlizzardQuestInfo(qid) end return coords -- Might still be nil, but if we don't have anything from Blizz, prolly nothing can be done. end function DB_GetItem(group, id, silent, register) QuestHelper: Assert(initted) QuestHelper: Assert(group, string.format("%s %s", tostring(group), tostring(id))) QuestHelper: Assert(id, string.format("%s %s", tostring(group), tostring(id))) local ite = DBC_Get(group, id) if not ite and group == "flightpaths" then return nil end if not ite and group == "quest" then -- Loop over zones AND floors -- see QuestRouterLite.lua for specific calls. -- In the end, the item returned MUST be "formatted" the same as a true QHDB item. ite = GetQuestInfo(id) end if not ite then if type(id) == "string" then QuestHelper: Assert(not id:match("__.*")) end --QuestHelper:TextOut(string.format("%s %d", group, id)) for _, db in ipairs(QHDB) do --print(db, db[group], db[group] and db[group][id], type(group), type(id)) if db[group] then local dat if db[group][id] then dat = db[group][id] end --print(not dat, type(id), id, not not db[group].__serialize_index, not not db[group].__serialize_index) if not dat and type(id) == "number" and id > 0 and db[group].__serialize_index and db[group].__serialize_data then dat = search_index(db[group].__serialize_index, db[group].__serialize_data, id) end if dat then if not ite then ite = QuestHelper:CreateTable("db") end local srctab if type(dat) == "string" then QuestHelper: Assert(db[group].__tokens == nil or type(db[group].__tokens) == "table") srctab = loadstring("return {" .. (db[group].__prefix or "") .. QH_LZW_Decompress_Dicts_Prepared_Arghhacky(dat, db[group].__dictionary, nil, db[group].__tokens) .. (db[group].__suffix or "") .. "}")() elseif type(dat) == "table" then srctab = dat else QuestHelper: Assert() end for k, v in pairs(srctab) do QuestHelper: Assert(not ite[k] or k == "used") ite[k] = v if export then if not QHDB_Export[group] then QHDB_Export[group] = {} end if not QHDB_Export[group][id] then QHDB_Export[group][id] = {} end QHDB_Export[group][id][k] = v end end end end end if ite then --print("marking", group, id, silent, register) mark(ite, ite) --print("done marking") DBC_Put(group, id, ite) freq_group[ite] = group freq_id[ite] = id else if not silent then QuestHelper:TextOut(string.format("Tried to get %s/%s, failed", tostring(group), tostring(id))) end end end if ite then frequencies[ite] = (frequencies[ite] or 0) + (register and 1 or 1000000000) -- effectively infinity end return ite end local function incinerate(ite, crunchy) if dev_mode then return end -- wellllp if not crunchy[ite] then crunchy[ite] = true for k, v in pairs(ite) do if type(k) == "table" then incinerate(k, crunchy) end if type(v) == "table" then incinerate(v, crunchy) end end end end function DB_ReleaseItem(ite) QuestHelper: Assert(ite) frequencies[ite] = frequencies[ite] - 1 if frequencies[ite] == 0 then --print("incinerating", freq_group[ite], freq_id[ite]) if cache[freq_group[ite]] then cache[freq_group[ite]][freq_id[ite]] = nil end freq_group[ite] = nil freq_id[ite] = nil local incin = QuestHelper:CreateTable("incinerate") incinerate(ite, incin) for k, _ in pairs(incin) do QuestHelper:ReleaseTable(k) end -- burn baby burn QuestHelper:ReleaseTable(incin) end end function DB_ListItems(group) local tab = {} for _, db in ipairs(QHDB) do if db[group] then QuestHelper: Assert(not db.__serialize_index and not db.__serialize_data) for k, _ in pairs(db[group]) do if type(k) ~= "string" or not k:match("__.*") then tab[k] = true end end end end local rv = {} for k, _ in pairs(tab) do table.insert(rv, k) end return rv end function DB_how_many_are_used() local count = 0 for k, v in pairs(cache) do for k2, v2 in pairs(v) do count = count + 1 end end print(count) end function DB_DumpItems() local dt = {} for k, v in pairs(freq_group) do dt[string.format("%s/%s", freq_group[k], tostring(freq_id[k]))] = true end return dt end