feat: standardize v1.5.0 and consolidate stability fixes
This commit is contained in:
@@ -41,13 +41,7 @@ function QuestieJourneyUtils:AddLine(frame, text)
|
||||
end
|
||||
|
||||
function QuestieJourneyUtils:GetZoneName(id)
|
||||
local name = l10n("Unknown Zone")
|
||||
for category, data in pairs(l10n.zoneLookup) do
|
||||
if data[id] then
|
||||
name = l10n.zoneLookup[category][id]
|
||||
break
|
||||
end
|
||||
end
|
||||
local name = l10n:GetLocalNameByAreaId(id)
|
||||
|
||||
-- Ascension can use custom UiMapIds for zones/sub-zones (e.g. 1238 Northshire Valley).
|
||||
-- Those won't exist in l10n.zoneLookup (which is AreaId-based), so fallback to UiMapData / mapInfo.
|
||||
|
||||
@@ -164,148 +164,3 @@ function MessageHandlerFactory.New()
|
||||
|
||||
return handler
|
||||
end
|
||||
|
||||
----- Tests -----
|
||||
do
|
||||
--? This is the tests for MessageHandlerFactory
|
||||
local function RunMessageHandlerTests()
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, " -- Running " .. Questie:Colorize("MessageHandlerFactory", "yellow") .. " tests --")
|
||||
local testEvent = "EVENT_TEST"
|
||||
|
||||
--- Test simple usage
|
||||
do
|
||||
local MessageHandler = MessageHandlerFactory:New()
|
||||
local returnedCount = 0
|
||||
|
||||
local incrementFunction = function()
|
||||
returnedCount = returnedCount + 1
|
||||
end
|
||||
|
||||
-- Add and fire
|
||||
MessageHandler:RegisterRepeating(testEvent, incrementFunction)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 1, Questie:Colorize(" -- FAILED: Event was not fired", "red"))
|
||||
|
||||
-- Unregister and fire
|
||||
MessageHandler:UnregisterRepeating(testEvent, incrementFunction)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 1, Questie:Colorize(" -- FAILED: Event was fired after unregistering", "red"))
|
||||
|
||||
-- Register two events and fire
|
||||
MessageHandler:RegisterRepeating(testEvent, incrementFunction)
|
||||
MessageHandler:RegisterRepeating(testEvent, incrementFunction)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 3, Questie:Colorize(" -- FAILED: Event was not fired twice", "red"))
|
||||
|
||||
-- Unregister all events and fire
|
||||
MessageHandler:UnregisterAll(testEvent)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 3, Questie:Colorize(" -- FAILED: Event was fired after unregistering all", "red"))
|
||||
|
||||
-- Register once and fire
|
||||
MessageHandler:RegisterOnce(testEvent, incrementFunction)
|
||||
MessageHandler:Fire(testEvent)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 4, Questie:Colorize(" -- FAILED: Event was not fired once", "red"))
|
||||
end
|
||||
|
||||
--- Test multiple registered events
|
||||
do
|
||||
local MessageHandler = MessageHandlerFactory:New()
|
||||
local returnedCount = 0
|
||||
local incrementFunction = function()
|
||||
returnedCount = returnedCount + 1
|
||||
end
|
||||
local incrementFunction2 = function()
|
||||
returnedCount = returnedCount + 1
|
||||
end
|
||||
|
||||
local testEvent2 = "EVENT_TEST2"
|
||||
|
||||
MessageHandler:RegisterRepeating(testEvent, incrementFunction)
|
||||
MessageHandler:RegisterRepeating(testEvent2, incrementFunction2)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 1, Questie:Colorize(" -- FAILED: Event 1 was not fired", "red"))
|
||||
MessageHandler:Fire(testEvent2)
|
||||
assert(returnedCount == 2, Questie:Colorize(" -- FAILED: Event 2 was not fired", "red"))
|
||||
|
||||
-- Unregister and fire
|
||||
MessageHandler:UnregisterRepeating(testEvent, incrementFunction)
|
||||
MessageHandler:Fire(testEvent)
|
||||
assert(returnedCount == 2, Questie:Colorize(" -- FAILED: Event 1 was fired after unregistering", "red"))
|
||||
MessageHandler:Fire(testEvent2)
|
||||
assert(returnedCount == 3, Questie:Colorize(" -- FAILED: Event 2 was not fired", "red"))
|
||||
MessageHandler:UnregisterRepeating(testEvent2, incrementFunction2)
|
||||
MessageHandler:Fire(testEvent2)
|
||||
assert(returnedCount == 3, Questie:Colorize(" -- FAILED: Event 2 was fired after unregistering", "red"))
|
||||
end
|
||||
|
||||
--- Test return
|
||||
do
|
||||
local MessageHandler = MessageHandlerFactory:New()
|
||||
local returnedCount = 0
|
||||
local incrementReturnFunction = function()
|
||||
returnedCount = returnedCount + 1
|
||||
return returnedCount
|
||||
end
|
||||
|
||||
-- Register mutliple events and fire
|
||||
for _ = 1, 5 do
|
||||
MessageHandler:RegisterRepeating(testEvent, incrementReturnFunction)
|
||||
end
|
||||
local retVal = MessageHandler:Fire(testEvent)
|
||||
assert(retVal, Questie:Colorize(" -- FAILED: Return value was nil", "red"))
|
||||
assert(retVal[1] == 1, Questie:Colorize(" -- FAILED: 1 Function value was not returned", "red"))
|
||||
assert(retVal[2] == 2, Questie:Colorize(" -- FAILED: 2 Function value was not returned", "red"))
|
||||
assert(retVal[3] == 3, Questie:Colorize(" -- FAILED: 3 Function value was not returned", "red"))
|
||||
assert(retVal[4] == 4, Questie:Colorize(" -- FAILED: 4 Function value was not returned", "red"))
|
||||
assert(retVal[5] == 5, Questie:Colorize(" -- FAILED: 5 Function value was not returned", "red"))
|
||||
end
|
||||
|
||||
--- Test async and async return
|
||||
do
|
||||
local MessageHandler = MessageHandlerFactory:New()
|
||||
local returnedCount = 0
|
||||
|
||||
local incrementReturnFunction = function()
|
||||
returnedCount = returnedCount + 1
|
||||
return returnedCount
|
||||
end
|
||||
|
||||
-- Register mutliple events and fire
|
||||
for _ = 1, 5 do
|
||||
MessageHandler:RegisterRepeating(testEvent, incrementReturnFunction)
|
||||
end
|
||||
|
||||
local routine = coroutine.create(
|
||||
function()
|
||||
MessageHandler:FireAsync(testEvent, 2)
|
||||
assert(returnedCount == 5, Questie:Colorize(" -- FAILED: Event was not fired the correct amount of times", "red"))
|
||||
end
|
||||
)
|
||||
local timer
|
||||
timer = C_Timer.NewTicker(0, function()
|
||||
local success, retVal = coroutine.resume(routine)
|
||||
if retVal then
|
||||
assert(retVal[1] == 1, Questie:Colorize(" -- FAILED: 1 Function value was not returned", "red"))
|
||||
assert(retVal[2] == 2, Questie:Colorize(" -- FAILED: 2 Function value was not returned", "red"))
|
||||
assert(retVal[3] == 3, Questie:Colorize(" -- FAILED: 3 Function value was not returned", "red"))
|
||||
assert(retVal[4] == 4, Questie:Colorize(" -- FAILED: 4 Function value was not returned", "red"))
|
||||
assert(retVal[5] == 5, Questie:Colorize(" -- FAILED: 5 Function value was not returned", "red"))
|
||||
end
|
||||
assert(success, Questie:Colorize(" -- FAILED: Coroutine failed", "red"), retVal)
|
||||
|
||||
-- Kill the timer when the coroutine is dead.
|
||||
if (coroutine.status(routine) == "dead") then
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "- MessageHandlerFactory - |cFF00FF00SUCCESS!|r")
|
||||
timer:Cancel()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- Run it after all files has been loaded
|
||||
C_Timer.After(2, RunMessageHandlerTests)
|
||||
|
||||
end
|
||||
-----------------
|
||||
|
||||
@@ -135,7 +135,7 @@ function QuestiePlugin:InjectZoneTables(customZoneTables)
|
||||
while uiMapId do
|
||||
if uiMapId and areaId then
|
||||
ZoneDB.private.uiMapIdToAreaId[uiMapId] = areaId
|
||||
ZoneDB.private.areaIdToUiMapId[uiMapId] = uiMapId
|
||||
ZoneDB.private.areaIdToUiMapId[areaId] = uiMapId
|
||||
|
||||
if type(ZoneDB.private.dungeons) == "table" and ZoneDB.private.dungeons[areaId] then
|
||||
ZoneDB.private.areaIdToUiMapId[areaId] = uiMapId
|
||||
|
||||
+121
-226
@@ -30,52 +30,41 @@ local C_Map = QuestieCompat.C_Map
|
||||
QuestieMap.ICON_MAP_TYPE = "MAP";
|
||||
QuestieMap.ICON_MINIMAP_TYPE = "MINIMAP";
|
||||
|
||||
--Useful links.
|
||||
-- https://github.com/tomrus88/BlizzardInterfaceCode/blob/master/Interface/SharedXML/Pools.lua
|
||||
-- https://www.townlong-yak.com/framexml/27101/Blizzard_MapCanvas/Blizzard_MapCanvas.lua
|
||||
-- https://www.townlong-yak.com/framexml/27101/Blizzard_MapCanvas/MapCanvas_DataProviderBase.lua
|
||||
-- https://www.townlong-yak.com/framexml/27101/Blizzard_MapCanvas/MapCanvas_PinFrameLevelsManager.lua
|
||||
|
||||
-- List of frames sorted by quest ID (automatic notes)
|
||||
-- E.g. {[questId] = {[frameName] = frame, ...}, ...}
|
||||
-- For details about frame.data see calls to QuestieMap.DrawWorldIcon
|
||||
QuestieMap.questIdFrames = {}
|
||||
-- List of frames sorted by NPC/object ID (manual notes)
|
||||
-- id > 0: NPC
|
||||
-- id < 0: object
|
||||
-- E.g. {[-objectId] = {[frameName] = frame, ...}, ...}
|
||||
-- For details about frame.data see QuestieMap.ShowNPC and QuestieMap.ShowObject
|
||||
QuestieMap.manualFrames = {}
|
||||
|
||||
|
||||
--Used in my fadelogic.
|
||||
local fadeOverDistance = 10;
|
||||
local normalizedValue = 1 / fadeOverDistance; --Opacity / Distance to fade over
|
||||
local normalizedValue = 1 / fadeOverDistance;
|
||||
|
||||
local HBD = QuestieCompat.HBD or LibStub("HereBeDragonsQuestie-2.0")
|
||||
local HBDPins = QuestieCompat.HBDPins or LibStub("HereBeDragonsQuestie-Pins-2.0")
|
||||
|
||||
--We should really try and squeeze out all the performance we can, especially in this.
|
||||
local tostring = tostring;
|
||||
local tinsert = table.insert;
|
||||
local pairs = pairs;
|
||||
local ipairs = ipairs;
|
||||
local tremove = table.remove;
|
||||
local tunpack = unpack;
|
||||
|
||||
|
||||
local drawTimer
|
||||
local fadeLogicTimerShown
|
||||
local fadeLogicCoroutine
|
||||
|
||||
local isDrawQueueDisabled = false
|
||||
|
||||
|
||||
--* TODO: How the frames are handled needs to be reworked, why are we getting them from _G
|
||||
--Get the frames for a quest, this returns all of the frames
|
||||
function QuestieMap:GetFramesForQuest(questId)
|
||||
local frames = {}
|
||||
--If no frames exists or if the quest does not exist we just return an empty list
|
||||
if QuestieMap.questIdFrames[questId] then
|
||||
for _, name in next, QuestieMap.questIdFrames[questId] do
|
||||
for _, name in pairs(QuestieMap.questIdFrames[questId]) do
|
||||
if _G[name] then
|
||||
frames[name] = _G[name]
|
||||
end
|
||||
@@ -87,14 +76,12 @@ end
|
||||
function QuestieMap:UnloadQuestFrames(questId, iconType)
|
||||
if QuestieMap.questIdFrames[questId] then
|
||||
if not iconType then
|
||||
local frameList = QuestieMap:GetFramesForQuest(questId)
|
||||
for _, frame in next, frameList do
|
||||
for _, frame in pairs(QuestieMap:GetFramesForQuest(questId)) do
|
||||
frame:Unload();
|
||||
end
|
||||
QuestieMap.questIdFrames[questId] = nil;
|
||||
else
|
||||
local frameList = QuestieMap:GetFramesForQuest(questId)
|
||||
for name, frame in next, frameList do
|
||||
for name, frame in pairs(QuestieMap:GetFramesForQuest(questId)) do
|
||||
if frame and frame.data and frame.data.Icon == iconType then
|
||||
frame:Unload();
|
||||
QuestieMap.questIdFrames[questId][name] = nil
|
||||
@@ -111,9 +98,8 @@ end
|
||||
function QuestieMap:GetManualFrames(id, typ)
|
||||
typ = typ or "any"
|
||||
local frames = {}
|
||||
--If no frames exists or if the quest does not exist we just return an empty list
|
||||
if QuestieMap.manualFrames[typ] and (QuestieMap.manualFrames[typ][id]) then
|
||||
for _, name in next, QuestieMap.manualFrames[typ][id] do
|
||||
for _, name in pairs(QuestieMap.manualFrames[typ][id]) do
|
||||
tinsert(frames, _G[name])
|
||||
end
|
||||
end
|
||||
@@ -124,8 +110,7 @@ end
|
||||
function QuestieMap:UnloadManualFrames(id, typ)
|
||||
typ = typ or "any"
|
||||
if QuestieMap.manualFrames[typ] and (QuestieMap.manualFrames[typ][id]) then
|
||||
local frameList = QuestieMap:GetManualFrames(id, typ)
|
||||
for _, frame in next, frameList do
|
||||
for _, frame in ipairs(QuestieMap:GetManualFrames(id, typ)) do
|
||||
frame:Unload();
|
||||
end
|
||||
QuestieMap.manualFrames[typ][id] = nil;
|
||||
@@ -134,22 +119,24 @@ end
|
||||
|
||||
function QuestieMap:ResetManualFrames(typ)
|
||||
typ = typ or "any"
|
||||
for id in next, QuestieMap.manualFrames[typ] do
|
||||
QuestieMap:UnloadManualFrames(id, typ)
|
||||
if QuestieMap.manualFrames[typ] then
|
||||
for id in pairs(QuestieMap.manualFrames[typ]) do
|
||||
QuestieMap:UnloadManualFrames(id, typ)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Rescale all the icons
|
||||
function QuestieMap:RescaleIcons()
|
||||
local mapScale = QuestieMap.GetScaleValue()
|
||||
for _, framelist in next, QuestieMap.questIdFrames do
|
||||
for _, frameName in next, framelist do
|
||||
for _, framelist in pairs(QuestieMap.questIdFrames) do
|
||||
for _, frameName in pairs(framelist) do
|
||||
QuestieMap.utils:RescaleIcon(frameName, mapScale)
|
||||
end
|
||||
end
|
||||
for _, frameTypeList in next, QuestieMap.manualFrames do
|
||||
for _, framelist in next, frameTypeList do
|
||||
for _, frameName in next, framelist do
|
||||
for _, frameTypeList in pairs(QuestieMap.manualFrames) do
|
||||
for _, framelist in pairs(frameTypeList) do
|
||||
for _, frameName in ipairs(framelist) do
|
||||
QuestieMap.utils:RescaleIcon(frameName, mapScale)
|
||||
end
|
||||
end
|
||||
@@ -162,15 +149,15 @@ local minimapDrawQueue = {};
|
||||
QuestieMap._mapDrawQueue = mapDrawQueue
|
||||
QuestieMap._minimapDrawQueue = minimapDrawQueue
|
||||
|
||||
function QuestieMap:InitializeQueue() -- now called on every loading screen
|
||||
--- Called at startup (Stage 3) and on PLAYER_ENTERING_WORLD to reset the draw queue.
|
||||
function QuestieMap:InitializeQueue()
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieMap] Starting draw queue timer!")
|
||||
local isInInstance, instanceType = IsInInstance()
|
||||
|
||||
if (not isInInstance) or instanceType ~= "raid" then -- only run map updates when not in a raid
|
||||
if (not isInInstance) or instanceType ~= "raid" then
|
||||
isDrawQueueDisabled = false
|
||||
if not drawTimer then
|
||||
drawTimer = C_Timer.NewTicker(0.2, QuestieMap.ProcessQueue)
|
||||
-- ! Remember to update the distance variable in ProcessShownMinimapIcons if you change the timer
|
||||
fadeLogicTimerShown = C_Timer.NewTicker(0.1, function()
|
||||
if fadeLogicCoroutine and coroutine.status(fadeLogicCoroutine) == "suspended" then
|
||||
local success, errorMsg = coroutine.resume(fadeLogicCoroutine)
|
||||
@@ -185,7 +172,7 @@ function QuestieMap:InitializeQueue() -- now called on every loading screen
|
||||
fadeLogicCoroutine = coroutine.create(QuestieMap.ProcessShownMinimapIcons)
|
||||
end
|
||||
else
|
||||
if drawTimer then -- cancel existing timer while in dungeon/raid
|
||||
if drawTimer then
|
||||
drawTimer:Cancel()
|
||||
drawTimer = nil
|
||||
fadeLogicTimerShown:Cancel()
|
||||
@@ -197,91 +184,71 @@ end
|
||||
|
||||
---@return number @A scale value that is based of the map currently open, smaller icons for World and Continent
|
||||
function QuestieMap.GetScaleValue()
|
||||
if not HBDPins or not HBDPins.worldmapProvider then return 1 end
|
||||
local mapId = HBDPins.worldmapProvider:GetMap():GetMapID();
|
||||
local scaling = 1;
|
||||
if C_Map and C_Map.GetAreaInfo then
|
||||
local mapInfo = C_Map.GetMapInfo(mapId)
|
||||
if (mapInfo.mapType == 0) then --? Cosmic, This is probably not needed but for the sake of completion...
|
||||
scaling = 0.85
|
||||
elseif (mapInfo.mapType == 1) then -- World
|
||||
scaling = 0.85
|
||||
elseif (mapInfo.mapType == 2) then -- Continent
|
||||
scaling = 0.9
|
||||
if mapInfo then
|
||||
if (mapInfo.mapType == 0) then
|
||||
scaling = 0.85
|
||||
elseif (mapInfo.mapType == 1) then
|
||||
scaling = 0.85
|
||||
elseif (mapInfo.mapType == 2) then
|
||||
scaling = 0.9
|
||||
end
|
||||
end
|
||||
end
|
||||
return scaling
|
||||
end
|
||||
|
||||
function QuestieMap:ProcessShownMinimapIcons()
|
||||
--Upvalue the most used functions in here
|
||||
local getTime, cYield, getWorldPos = GetTime, coroutine.yield, HBD.GetPlayerWorldPosition
|
||||
|
||||
--Max icons per tick
|
||||
local maxCount = 50
|
||||
|
||||
--Local variables defined here instead of in loop
|
||||
--saves time because it doesn't need to remake the variables
|
||||
local doEdgeUpdate = true
|
||||
local playerX, playerY
|
||||
local count
|
||||
local lastUpdate = getTime()
|
||||
|
||||
local xd, yd
|
||||
local totalDistance = 0
|
||||
|
||||
--This coroutine never dies, we want it to keep looping forever
|
||||
--yield stops it from being "infinite" and crashing the game
|
||||
while true do
|
||||
count = 0
|
||||
|
||||
playerX, playerY = getWorldPos()
|
||||
|
||||
--Calculate squared distance
|
||||
-- No need for absolute values as these are used only as squared
|
||||
xd = (playerX or 0) - (QuestieMap.playerX or 0)
|
||||
yd = (playerY or 0) - (QuestieMap.playerY or 0)
|
||||
--Instead of math.sqrt we just used the square distance for speed
|
||||
totalDistance = totalDistance + (xd * xd + yd * yd)
|
||||
|
||||
|
||||
--These variables are used inside the fadelogic
|
||||
QuestieMap.playerX = playerX
|
||||
QuestieMap.playerY = playerY
|
||||
|
||||
-- Only update icons on the edge every 1 seconds
|
||||
-- totalDistance is used because sometimes we move so fast that we need to update it more often.
|
||||
-- ! Remember to update the distance variable if you change the timer
|
||||
if totalDistance > 3 or getTime() - lastUpdate >= 1 then
|
||||
doEdgeUpdate = true
|
||||
lastUpdate = getTime()
|
||||
--print("Dist:", totalDistance)
|
||||
totalDistance = 0
|
||||
end
|
||||
|
||||
---@param minimapFrame IconFrame
|
||||
for minimapFrame, data in next, HBDPins.activeMinimapPins do
|
||||
if minimapFrame.miniMapIcon and ((data.distanceFromMinimapCenter < 1.1) or doEdgeUpdate) then
|
||||
if minimapFrame.FadeLogic then
|
||||
minimapFrame:FadeLogic()
|
||||
if HBDPins and HBDPins.activeMinimapPins then
|
||||
for minimapFrame, data in pairs(HBDPins.activeMinimapPins) do
|
||||
if minimapFrame.miniMapIcon and ((data.distanceFromMinimapCenter < 1.1) or doEdgeUpdate) then
|
||||
if minimapFrame.FadeLogic then
|
||||
minimapFrame:FadeLogic()
|
||||
end
|
||||
if minimapFrame.GlowUpdate then
|
||||
minimapFrame:GlowUpdate()
|
||||
end
|
||||
end
|
||||
if minimapFrame.GlowUpdate then
|
||||
minimapFrame:GlowUpdate()
|
||||
end
|
||||
end
|
||||
|
||||
--Never run more than maxCount in a single run
|
||||
if count > maxCount then
|
||||
cYield()
|
||||
if (not HBDPins.activeMinimapPins[minimapFrame]) then
|
||||
-- table has been edited during traversal at critical key. we can't continue iterating over it. stop iteration and start again.
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieMap:ProcessShownMinimapIcons] FadeLogic loop coroutine: HBDPins.activeMinimapPins doesn't have the key anymore.")
|
||||
-- force reupdate imeadiately
|
||||
totalDistance = 9000
|
||||
break
|
||||
if count > maxCount then
|
||||
cYield()
|
||||
if not HBDPins.activeMinimapPins[minimapFrame] then
|
||||
totalDistance = 9000
|
||||
break
|
||||
end
|
||||
count = 0
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
count = 0
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
cYield()
|
||||
@@ -290,7 +257,7 @@ function QuestieMap:ProcessShownMinimapIcons()
|
||||
end
|
||||
|
||||
function QuestieMap:QueueDraw(drawType, ...)
|
||||
if (not isDrawQueueDisabled) then -- dont queue when in raid
|
||||
if (not isDrawQueueDisabled) then
|
||||
if (drawType == QuestieMap.ICON_MAP_TYPE) then
|
||||
tinsert(mapDrawQueue, { ... });
|
||||
elseif (drawType == QuestieMap.ICON_MINIMAP_TYPE) then
|
||||
@@ -301,21 +268,18 @@ end
|
||||
|
||||
function QuestieMap.ProcessQueue()
|
||||
if (not next(mapDrawQueue) and (not next(minimapDrawQueue))) then
|
||||
-- Nothing to process
|
||||
return
|
||||
end
|
||||
|
||||
local scaleValue = QuestieMap.GetScaleValue()
|
||||
for _ = 1, math.min(24, math.max(table.getn(mapDrawQueue), table.getn(minimapDrawQueue))) do
|
||||
for _ = 1, math.min(24, math.max(#mapDrawQueue, #minimapDrawQueue)) do
|
||||
local mapDrawCall = tremove(mapDrawQueue, 1);
|
||||
if mapDrawCall then
|
||||
local frame = mapDrawCall[2];
|
||||
HBDPins:AddWorldMapIconMap(tunpack(mapDrawCall));
|
||||
|
||||
--? If you ever chanage this logic, make sure you change the logic in QuestieMap.utils:RescaleIcon function too!
|
||||
local size = (16 * (frame.data.IconScale or 1) * (Questie.db.profile.globalScale or 0.7)) * scaleValue;
|
||||
frame:SetSize(size, size)
|
||||
|
||||
QuestieMap.utils:SetDrawOrder(frame);
|
||||
end
|
||||
|
||||
@@ -323,40 +287,38 @@ function QuestieMap.ProcessQueue()
|
||||
if minimapDrawCall then
|
||||
local frame = minimapDrawCall[2];
|
||||
HBDPins:AddMinimapIconMap(tunpack(minimapDrawCall));
|
||||
|
||||
QuestieMap.utils:SetDrawOrder(frame);
|
||||
end
|
||||
|
||||
mapDrawCall[2]._loaded = true
|
||||
if mapDrawCall[2]._needsUnload then
|
||||
mapDrawCall[2]:Unload()
|
||||
if mapDrawCall then
|
||||
mapDrawCall[2]._loaded = true
|
||||
if mapDrawCall[2]._needsUnload then
|
||||
mapDrawCall[2]:Unload()
|
||||
end
|
||||
end
|
||||
|
||||
minimapDrawCall[2]._loaded = true
|
||||
if minimapDrawCall[2]._needsUnload then
|
||||
minimapDrawCall[2]:Unload()
|
||||
if minimapDrawCall then
|
||||
minimapDrawCall[2]._loaded = true
|
||||
if minimapDrawCall[2]._needsUnload then
|
||||
minimapDrawCall[2]:Unload()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Show NPC on map
|
||||
-- This function does the same for manualFrames as similar functions in
|
||||
-- QuestieQuest do for questIdFrames
|
||||
---@param npcID number @The ID of the NPC
|
||||
function QuestieMap:ShowNPC(npcID, icon, scale, title, body, disableShiftToRemove, typ, excludeDungeon)
|
||||
if type(npcID) ~= "number" then
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:ShowNPC] Got <" .. type(npcID) .. "> instead of <number>")
|
||||
return
|
||||
end
|
||||
-- get the NPC data
|
||||
local npc = QuestieDB:GetNPC(npcID)
|
||||
if (not npc) or (not npc.spawns) then return end
|
||||
|
||||
-- create the icon data
|
||||
local data = {}
|
||||
data.id = npc.id
|
||||
data.Icon = icon or "Interface\\WorldMap\\WorldMapPartyIcon"
|
||||
|
||||
data.GetIconScale = function() return scale or Questie.db.profile.manualScale or 0.7 end
|
||||
data.IconScale = data:GetIconScale()
|
||||
data.Type = "manual"
|
||||
@@ -366,7 +328,7 @@ function QuestieMap:ShowNPC(npcID, icon, scale, title, body, disableShiftToRemov
|
||||
data.IsObjectiveNote = false
|
||||
data.ManualTooltipData = {}
|
||||
local baseTitle = title or (npc.name .. " (" .. l10n("NPC") .. ")")
|
||||
data.ManualTooltipData.Title = WeaponMasterSkills.AppendSkillsToTitle(baseTitle, data.id)
|
||||
data.ManualTooltipData.Title = WeaponMasterSkills and WeaponMasterSkills.AppendSkillsToTitle(baseTitle, data.id) or baseTitle
|
||||
local level = tostring(npc.minLevel)
|
||||
local health = tostring(npc.minLevelHealth)
|
||||
if npc.minLevel ~= npc.maxLevel then
|
||||
@@ -381,32 +343,22 @@ function QuestieMap:ShowNPC(npcID, icon, scale, title, body, disableShiftToRemov
|
||||
data.ManualTooltipData.disableShiftToRemove = disableShiftToRemove
|
||||
|
||||
local manualIcons = {}
|
||||
-- draw the notes
|
||||
for zone, spawns in next, npc.spawns do
|
||||
for zone, spawns in pairs(npc.spawns) do
|
||||
if (zone ~= nil and spawns ~= nil) and ((not excludeDungeon) or (not ZoneDB.IsDungeonZone(zone))) then
|
||||
local spIndex = 1
|
||||
while spawns[spIndex] do
|
||||
local coords = spawns[spIndex]
|
||||
-- instance spawn, draw entrance on map
|
||||
for _, coords in ipairs(spawns) do
|
||||
local dungeonLocation = ZoneDB:GetDungeonLocation(zone)
|
||||
if dungeonLocation ~= nil then
|
||||
local dlIndex = 1
|
||||
while dungeonLocation[dlIndex] do
|
||||
local value = dungeonLocation[dlIndex]
|
||||
for _, value in ipairs(dungeonLocation) do
|
||||
QuestieMap:DrawManualIcon(data, value[1], value[2], value[3], typ)
|
||||
dlIndex = dlIndex + 1
|
||||
end
|
||||
-- world spawn
|
||||
else
|
||||
manualIcons[zone] = QuestieMap:DrawManualIcon(data, zone, coords[1], coords[2], typ)
|
||||
end
|
||||
spIndex = spIndex + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
-- draw waypoints
|
||||
if npc.waypoints then
|
||||
for zone, waypoints in next, npc.waypoints do
|
||||
for zone, waypoints in pairs(npc.waypoints) do
|
||||
if not ZoneDB:GetDungeonLocation(zone) and waypoints[1] and waypoints[1][1] and waypoints[1][1][1] then
|
||||
if not manualIcons[zone] then
|
||||
manualIcons[zone] = QuestieMap:DrawManualIcon(data, zone, waypoints[1][1][1], waypoints[1][1][2])
|
||||
@@ -418,18 +370,13 @@ function QuestieMap:ShowNPC(npcID, icon, scale, title, body, disableShiftToRemov
|
||||
end
|
||||
|
||||
-- Show object on map
|
||||
-- This function does the same for manualFrames as similar functions in
|
||||
-- QuestieQuest do for questIdFrames
|
||||
---@param objectID number
|
||||
function QuestieMap:ShowObject(objectID, icon, scale, title, body, disableShiftToRemove, typ)
|
||||
if type(objectID) ~= "number" then return end
|
||||
-- get the gameobject data
|
||||
local object = QuestieDB:GetObject(objectID)
|
||||
if not object or not object.spawns then return end
|
||||
|
||||
-- create the icon data
|
||||
local data = {}
|
||||
-- hack: clean up this code, we shouldnt be using negative indexes
|
||||
if typ then
|
||||
data.id = object.id
|
||||
else
|
||||
@@ -450,26 +397,17 @@ function QuestieMap:ShowObject(objectID, icon, scale, title, body, disableShiftT
|
||||
}
|
||||
data.ManualTooltipData.disableShiftToRemove = disableShiftToRemove
|
||||
|
||||
-- draw the notes
|
||||
for zone, spawns in next, object.spawns do
|
||||
for zone, spawns in pairs(object.spawns) do
|
||||
if (zone ~= nil and spawns ~= nil) then
|
||||
local spIndex = 1
|
||||
while spawns[spIndex] do
|
||||
local coords = spawns[spIndex]
|
||||
-- instance spawn, draw entrance on map
|
||||
for _, coords in ipairs(spawns) do
|
||||
local dungeonLocation = ZoneDB:GetDungeonLocation(zone)
|
||||
if dungeonLocation ~= nil then
|
||||
local dlIndex = 1
|
||||
while dungeonLocation[dlIndex] do
|
||||
local value = dungeonLocation[dlIndex]
|
||||
for _, value in ipairs(dungeonLocation) do
|
||||
QuestieMap:DrawManualIcon(data, value[1], value[2], value[3], typ)
|
||||
dlIndex = dlIndex + 1
|
||||
end
|
||||
-- world spawn
|
||||
else
|
||||
QuestieMap:DrawManualIcon(data, zone, coords[1], coords[2], typ)
|
||||
end
|
||||
spIndex = spIndex + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -477,32 +415,34 @@ end
|
||||
|
||||
function QuestieMap:DrawLineIcon(lineFrame, areaID, x, y)
|
||||
if type(areaID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
|
||||
error("Questie" .. ": AddWorldMapIconMap: 'AreaID', 'x' and 'y' must be numbers " .. areaID .. " " .. x .. " " .. y)
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawLineIcon] 'AreaID', 'x' and 'y' must be numbers:", areaID, x, y)
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local uiMapId = ZoneDB:GetUiMapIdByAreaId(areaID)
|
||||
if not uiMapId then
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieMap:DrawLineIcon] No UiMapID for areaId:", areaID)
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
HBDPins:AddWorldMapIconMap(Questie, lineFrame, uiMapId, x, y, HBD_PINS_WORLDMAP_SHOW_CURRENT)
|
||||
end
|
||||
|
||||
-- Draw manually added NPC/object notes
|
||||
-- TODO: item and custom notes
|
||||
--@param data table<...> @A table created by the calling function, must contain `id`, `Name`, `GetIconScale()`, and `Type`
|
||||
--@param AreaID number @The zone ID from the raw data
|
||||
--@param x float @The X coordinate in 0-100 format
|
||||
--@param y float @The Y coordinate in 0-100 format
|
||||
function QuestieMap:DrawManualIcon(data, areaID, x, y, typ)
|
||||
if type(data) ~= "table" then
|
||||
error("Questie" .. ": AddWorldMapIconMap: must have some data")
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawManualIcon] must have some data")
|
||||
return nil, nil
|
||||
end
|
||||
if type(areaID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
|
||||
error("Questie" .. ": AddWorldMapIconMap: 'AreaID', 'x' and 'y' must be numbers " .. areaID .. " " .. x .. " " .. y)
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawManualIcon] 'AreaID', 'x' and 'y' must be numbers:", areaID, x, y)
|
||||
return nil, nil
|
||||
end
|
||||
if type(data.id) ~= "number" or type(data.id) ~= "number" then
|
||||
error("Questie" .. "Data.id must be set to the NPC or object ID!")
|
||||
if type(data.id) ~= "number" then
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawManualIcon] Data.id must be set to the NPC or object ID!")
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- this needs to be refactored. Fix the capitalization. Who made this id instead of Id?
|
||||
data.Id = data.id
|
||||
|
||||
local uiMapId = ZoneDB:GetUiMapIdByAreaId(areaID)
|
||||
@@ -510,10 +450,8 @@ function QuestieMap:DrawManualIcon(data, areaID, x, y, typ)
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawManualIcon] No UiMapID for areaId:", areaID, tostring(data.Name))
|
||||
return nil, nil
|
||||
end
|
||||
-- set the icon
|
||||
|
||||
local texture = data.Icon or "Interface\\WorldMap\\WorldMapPartyIcon"
|
||||
-- Save new zone ID format, used in QuestieFramePool
|
||||
-- create a list for all frames belonging to a NPC (id > 0) or an object (id < 0)
|
||||
typ = typ or "any"
|
||||
if not QuestieMap.manualFrames[typ] then
|
||||
QuestieMap.manualFrames[typ] = {}
|
||||
@@ -522,23 +460,20 @@ function QuestieMap:DrawManualIcon(data, areaID, x, y, typ)
|
||||
QuestieMap.manualFrames[typ][data.id] = {}
|
||||
end
|
||||
|
||||
-- create the map icon
|
||||
local icon = QuestieFramePool:GetFrame()
|
||||
icon.data = data
|
||||
icon.x = x
|
||||
icon.y = y
|
||||
icon.AreaID = areaID -- used by QuestieFramePool
|
||||
icon.AreaID = areaID
|
||||
icon.UiMapID = uiMapId
|
||||
icon.miniMapIcon = false;
|
||||
icon.texture:SetTexture(texture)
|
||||
icon:SetWidth(16 * (data:GetIconScale() or 0.7))
|
||||
icon:SetHeight(16 * (data:GetIconScale() or 0.7))
|
||||
|
||||
-- add the map icon
|
||||
QuestieMap:QueueDraw(QuestieMap.ICON_MAP_TYPE, Questie, icon, icon.UiMapID, x / 100, y / 100, 3) -- showFlag)
|
||||
QuestieMap:QueueDraw(QuestieMap.ICON_MAP_TYPE, Questie, icon, icon.UiMapID, x / 100, y / 100, 3)
|
||||
tinsert(QuestieMap.manualFrames[typ][data.id], icon:GetName())
|
||||
|
||||
-- create the minimap icon
|
||||
local iconMinimap = QuestieFramePool:GetFrame()
|
||||
local colorsMinimap = { 1, 1, 1 }
|
||||
if data.IconColor ~= nil and Questie.db.profile.questMinimapObjectiveColors then
|
||||
@@ -549,18 +484,16 @@ function QuestieMap:DrawManualIcon(data, areaID, x, y, typ)
|
||||
iconMinimap.data = data
|
||||
iconMinimap.x = x
|
||||
iconMinimap.y = y
|
||||
iconMinimap.AreaID = areaID -- used by QuestieFramePool
|
||||
iconMinimap.AreaID = areaID
|
||||
iconMinimap.UiMapID = uiMapId
|
||||
iconMinimap.texture:SetTexture(texture)
|
||||
iconMinimap.texture:SetVertexColor(colorsMinimap[1], colorsMinimap[2], colorsMinimap[3], 1);
|
||||
iconMinimap.miniMapIcon = true;
|
||||
|
||||
-- add the minimap icon
|
||||
QuestieMap:QueueDraw(QuestieMap.ICON_MINIMAP_TYPE, Questie, iconMinimap, iconMinimap.UiMapID, x / 100, y / 100, true, true);
|
||||
tinsert(QuestieMap.manualFrames[typ][data.id], iconMinimap:GetName())
|
||||
|
||||
-- make sure notes are only shown when they are supposed to
|
||||
if (not Questie.db.profile.enabled) then -- TODO: or (not Questie.db.profile.manualNotes)
|
||||
if (not Questie.db.profile.enabled) then
|
||||
icon:FakeHide()
|
||||
iconMinimap:FakeHide()
|
||||
else
|
||||
@@ -572,31 +505,26 @@ function QuestieMap:DrawManualIcon(data, areaID, x, y, typ)
|
||||
end
|
||||
end
|
||||
|
||||
QuestieMap.utils:RescaleIcon(icon)
|
||||
if QuestieMap.utils and QuestieMap.utils.RescaleIcon then
|
||||
QuestieMap.utils:RescaleIcon(icon)
|
||||
end
|
||||
|
||||
-- return the frames in case they need to be stored seperately from QuestieMap.manualFrames
|
||||
return icon, iconMinimap;
|
||||
end
|
||||
|
||||
--A layer to keep the area convertion away from the other parts of the code
|
||||
--A layer to keep the area conversion away from the other parts of the code
|
||||
--coordinates need to be 0-1 instead of 0-100
|
||||
--showFlag isn't required but may want to be Modified
|
||||
---@return IconFrame, IconFrame
|
||||
function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
if type(data) ~= "table" then
|
||||
error("Questie" .. ": AddWorldMapIconMap: must have some data")
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawWorldIcon] must have some data")
|
||||
return nil, nil
|
||||
end
|
||||
--if type(areaID) ~= "number" or type(x) ~= "number" or type(y) ~= "number" then
|
||||
-- error("Questie"..": AddWorldMapIconMap: 'AreaID', 'x' and 'y' must be numbers "..areaID.." "..x.." "..y.." "..tostring(showFlag))
|
||||
--end
|
||||
--if type(data.Id) ~= "number" or type(data.Id) ~= "number"then
|
||||
-- error("Questie".."Data.Id must be set to the quests ID!")
|
||||
--end
|
||||
|
||||
local uiMapId = ZoneDB:GetUiMapIdByAreaId(areaID)
|
||||
if (not uiMapId) then
|
||||
local parentMapId
|
||||
local mapInfo = C_Map.GetMapInfo(areaID)
|
||||
local mapInfo = C_Map and C_Map.GetMapInfo and C_Map.GetMapInfo(areaID)
|
||||
if mapInfo then
|
||||
parentMapId = mapInfo.parentMapID
|
||||
else
|
||||
@@ -604,14 +532,11 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
end
|
||||
|
||||
if (not parentMapId) then
|
||||
error("No UiMapID or fitting parentAreaId for areaId : " .. areaID .. " - " .. tostring(data.Name))
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieMap:DrawWorldIcon] No UiMapID or fitting parentAreaId for areaId:", areaID, tostring(data.Name))
|
||||
return nil, nil
|
||||
else
|
||||
areaID = parentMapId
|
||||
uiMapId = ZoneDB:GetUiMapIdByAreaId(areaID)
|
||||
if not uiMapId then
|
||||
return nil, nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -619,10 +544,8 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
showFlag = HBD_PINS_WORLDMAP_SHOW_WORLD
|
||||
end
|
||||
|
||||
--print("UIMAPID: " .. tostring(uiMapId))
|
||||
if not uiMapId then
|
||||
--ZoneDB:GetUiMapIdByAreaId
|
||||
error("No UiMapID or fitting uiMapId for areaId : " .. areaID .. " - " .. tostring(data.Name))
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieMap:DrawWorldIcon] No UiMapID or fitting uiMapId for areaId:", areaID, tostring(data.Name))
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
@@ -645,8 +568,6 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
iconMinimap.y = y
|
||||
iconMinimap.AreaID = areaID
|
||||
iconMinimap.UiMapID = uiMapId
|
||||
--data.refMiniMap = iconMinimap -- used for removing
|
||||
--Are we a minimap note?
|
||||
iconMinimap.miniMapIcon = true;
|
||||
iconMinimap:UpdateTexture(Questie.usedIcons[data.Icon]);
|
||||
|
||||
@@ -676,7 +597,6 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
y = self.worldY
|
||||
end
|
||||
if (x and y) then
|
||||
--Very small value before, hard to work with.
|
||||
local distance = QuestieLib:Euclid(QuestieMap.playerX, QuestieMap.playerY, x, y) / 10;
|
||||
|
||||
if (distance > profile.fadeLevel) then
|
||||
@@ -684,7 +604,6 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
self:SetFade(fade)
|
||||
elseif (distance < profile.fadeOverPlayerDistance) and profile.fadeOverPlayer then
|
||||
local fadeAmount = profile.fadeOverPlayerLevel + distance * (1 - profile.fadeOverPlayerLevel) / profile.fadeOverPlayerDistance
|
||||
-- local fadeAmount = math.max(fadeAmount, 0.5);
|
||||
if self.faded and fadeAmount > profile.iconFadeLevel then
|
||||
fadeAmount = profile.iconFadeLevel
|
||||
end
|
||||
@@ -722,8 +641,6 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag)
|
||||
QuestieMap.questIdFrames[data.Id] = {}
|
||||
end
|
||||
|
||||
-- tinsert(QuestieMap.questIdFrames[data.Id], iconMap:GetName())
|
||||
-- tinsert(QuestieMap.questIdFrames[data.Id], iconMinimap:GetName())
|
||||
QuestieMap.questIdFrames[data.Id][iconMap:GetName()] = iconMap:GetName()
|
||||
QuestieMap.questIdFrames[data.Id][iconMinimap:GetName()] = iconMinimap:GetName()
|
||||
|
||||
@@ -744,7 +661,7 @@ local closestStarter = {}
|
||||
function QuestieMap:FindClosestStarter()
|
||||
local playerX, playerY, _ = HBD:GetPlayerWorldPosition();
|
||||
local playerZone = HBD:GetPlayerWorldPosition();
|
||||
for questId in next, QuestiePlayer.currentQuestlog do
|
||||
for questId in pairs(QuestiePlayer.currentQuestlog) do
|
||||
if (not closestStarter[questId]) then
|
||||
local quest = QuestieDB.GetQuest(questId);
|
||||
if quest then
|
||||
@@ -755,24 +672,18 @@ function QuestieMap:FindClosestStarter()
|
||||
zone = -1,
|
||||
type = "",
|
||||
}
|
||||
for starterType, starters in next, quest.Starts do
|
||||
for starterType, starters in pairs(quest.Starts) do
|
||||
if (starterType == "GameObject") then
|
||||
local stIndex = 1
|
||||
while starters and starters[stIndex] do
|
||||
local ObjectID = starters[stIndex]
|
||||
for _, ObjectID in ipairs(starters or {}) do
|
||||
local obj = QuestieDB:GetObject(ObjectID)
|
||||
if (obj ~= nil and obj.spawns ~= nil) then
|
||||
for Zone, Spawns in next, obj.spawns do
|
||||
for Zone, Spawns in pairs(obj.spawns) do
|
||||
if (Zone ~= nil and Spawns ~= nil) then
|
||||
local spIndex = 1
|
||||
while Spawns[spIndex] do
|
||||
local coords = Spawns[spIndex]
|
||||
if (coords[1] == -1 or coords[2] == -1) then -- instace locations
|
||||
for _, coords in ipairs(Spawns) do
|
||||
if (coords[1] == -1 or coords[2] == -1) then
|
||||
local dungeonLocation = ZoneDB:GetDungeonLocation(Zone)
|
||||
if dungeonLocation ~= nil then
|
||||
local dlIndex = 1
|
||||
while dungeonLocation[dlIndex] do
|
||||
local value = dungeonLocation[dlIndex]
|
||||
for _, value in ipairs(dungeonLocation) do
|
||||
if (value[1] and value[2]) then
|
||||
local x, y, _ = HBD:GetWorldCoordinatesFromZone(value[1] / 100, value[2] / 100, ZoneDB:GetUiMapIdByAreaId(value[3]))
|
||||
if (x and y) then
|
||||
@@ -786,7 +697,6 @@ function QuestieMap:FindClosestStarter()
|
||||
end
|
||||
end
|
||||
end
|
||||
dlIndex = dlIndex + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -803,30 +713,22 @@ function QuestieMap:FindClosestStarter()
|
||||
end
|
||||
end
|
||||
end
|
||||
spIndex = spIndex + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
stIndex = stIndex + 1
|
||||
end
|
||||
elseif (starterType == "NPC") then
|
||||
local stIndex = 1
|
||||
while starters and starters[stIndex] do
|
||||
local NPCID = starters[stIndex]
|
||||
for _, NPCID in ipairs(starters or {}) do
|
||||
local NPC = QuestieDB:GetNPC(NPCID)
|
||||
if (NPC ~= nil and NPC.spawns ~= nil and NPC.friendly) then
|
||||
for Zone, Spawns in next, NPC.spawns do
|
||||
for Zone, Spawns in pairs(NPC.spawns) do
|
||||
if (Zone ~= nil and Spawns ~= nil) then
|
||||
local spIndex = 1
|
||||
while Spawns[spIndex] do
|
||||
local coords = Spawns[spIndex]
|
||||
for _, coords in ipairs(Spawns) do
|
||||
if (coords[1] == -1 or coords[2] == -1) then
|
||||
local dungeonLocation = ZoneDB:GetDungeonLocation(Zone)
|
||||
if dungeonLocation ~= nil then
|
||||
local dlIndex = 1
|
||||
while dungeonLocation[dlIndex] do
|
||||
local value = dungeonLocation[dlIndex]
|
||||
for _, value in ipairs(dungeonLocation) do
|
||||
if (value[1] and value[2]) then
|
||||
local uiMapId = ZoneDB:GetUiMapIdByAreaId(value[3])
|
||||
local x, y, _ = HBD:GetWorldCoordinatesFromZone(value[1] / 100, value[2] / 100, uiMapId)
|
||||
@@ -841,7 +743,6 @@ function QuestieMap:FindClosestStarter()
|
||||
end
|
||||
end
|
||||
end
|
||||
dlIndex = dlIndex + 1
|
||||
end
|
||||
end
|
||||
elseif (coords[1] and coords[2]) then
|
||||
@@ -858,12 +759,10 @@ function QuestieMap:FindClosestStarter()
|
||||
end
|
||||
end
|
||||
end
|
||||
spIndex = spIndex + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
stIndex = stIndex + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -887,17 +786,16 @@ function QuestieMap:GetNearestSpawn(objective)
|
||||
local playerX, playerY, playerI = HBD:GetPlayerWorldPosition()
|
||||
local bestDistance = 999999999
|
||||
local bestSpawn, bestSpawnZone, bestSpawnId, bestSpawnType, bestSpawnName
|
||||
-- TODO: This is just a temporary workaround - We have to find out why "objective.spawnList" can be nil
|
||||
if objective and objective.spawnList and next(objective.spawnList) then
|
||||
for id, spawnData in next, objective.spawnList do
|
||||
for zone, spawns in next, spawnData.Spawns do
|
||||
for _, spawn in next, spawns do
|
||||
for id, spawnData in pairs(objective.spawnList) do
|
||||
for zone, spawns in pairs(spawnData.Spawns) do
|
||||
for _, spawn in pairs(spawns) do
|
||||
local uiMapId = ZoneDB:GetUiMapIdByAreaId(zone)
|
||||
local dX, dY, dInstance = HBD:GetWorldCoordinatesFromZone(spawn[1] / 100.0, spawn[2] / 100.0, uiMapId)
|
||||
local dist = HBD:GetWorldDistance(dInstance, playerX, playerY, dX, dY)
|
||||
if dist then
|
||||
if dInstance ~= playerI then
|
||||
dist = 500000 + dist * 100 -- hack
|
||||
dist = 500000 + dist * 100
|
||||
end
|
||||
if dist < bestDistance then
|
||||
bestDistance = dist
|
||||
@@ -925,25 +823,23 @@ function QuestieMap:GetNearestQuestSpawn(quest)
|
||||
local finisherName
|
||||
if quest.Finisher ~= nil then
|
||||
if quest.Finisher.Type == "monster" then
|
||||
--finisher = QuestieDB:GetNPC(quest.Finisher.Id)
|
||||
finisherSpawns, finisherName = QuestieDB.QueryNPCSingle(quest.Finisher.Id, "spawns"), QuestieDB.QueryNPCSingle(quest.Finisher.Id, "name")
|
||||
elseif quest.Finisher.Type == "object" then
|
||||
--finisher = QuestieDB:GetObject(quest.Finisher.Id)
|
||||
finisherSpawns, finisherName = QuestieDB.QueryObjectSingle(quest.Finisher.Id, "spawns"), QuestieDB.QueryObjectSingle(quest.Finisher.Id, "name")
|
||||
end
|
||||
end
|
||||
if finisherSpawns then -- redundant code
|
||||
if finisherSpawns then
|
||||
local bestDistance = 999999999
|
||||
local playerX, playerY, playerI = HBD:GetPlayerWorldPosition()
|
||||
local bestSpawn, bestSpawnZone, bestSpawnType, bestSpawnName
|
||||
for zone, spawns in next, finisherSpawns do
|
||||
for _, spawn in next, spawns do
|
||||
for zone, spawns in pairs(finisherSpawns) do
|
||||
for _, spawn in pairs(spawns) do
|
||||
local uiMapId = ZoneDB:GetUiMapIdByAreaId(zone)
|
||||
local dX, dY, dInstance = HBD:GetWorldCoordinatesFromZone(spawn[1] / 100.0, spawn[2] / 100.0, uiMapId)
|
||||
local dist = HBD:GetWorldDistance(dInstance, playerX, playerY, dX, dY)
|
||||
if dist then
|
||||
if dInstance ~= playerI then
|
||||
dist = 500000 + dist * 100 -- hack
|
||||
dist = 500000 + dist * 100
|
||||
end
|
||||
if dist < bestDistance then
|
||||
bestDistance = dist
|
||||
@@ -963,7 +859,7 @@ function QuestieMap:GetNearestQuestSpawn(quest)
|
||||
local bestDistance = 999999999
|
||||
local bestSpawn, bestSpawnZone, bestSpawnId, bestSpawnType, bestSpawnName
|
||||
|
||||
for _, objective in next, quest.Objectives do
|
||||
for _, objective in pairs(quest.Objectives) do
|
||||
local spawn, zone, Name, id, Type, dist = QuestieMap:GetNearestSpawn(objective)
|
||||
if spawn and dist < bestDistance and ((not objective.Needed) or objective.Needed ~= objective.Collected) then
|
||||
bestDistance = dist
|
||||
@@ -975,7 +871,7 @@ function QuestieMap:GetNearestQuestSpawn(quest)
|
||||
end
|
||||
end
|
||||
|
||||
for _, objective in next, quest.SpecialObjectives do
|
||||
for _, objective in pairs(quest.SpecialObjectives) do
|
||||
local spawn, zone, Name, id, Type, dist = QuestieMap:GetNearestSpawn(objective)
|
||||
if spawn and dist < bestDistance and ((not objective.Needed) or objective.Needed ~= objective.Collected) then
|
||||
bestDistance = dist
|
||||
@@ -989,7 +885,7 @@ function QuestieMap:GetNearestQuestSpawn(quest)
|
||||
return bestSpawn, bestSpawnZone, bestSpawnName, bestSpawnId, bestSpawnType, bestDistance
|
||||
end
|
||||
|
||||
QuestieMap.zoneWaypointColorOverrides = { -- this is used when the default orange color doesn't work well in specific zones. Not needed after 769ea832ff772b10c57351eb199348393625a99b
|
||||
QuestieMap.zoneWaypointColorOverrides = {
|
||||
-- [14] = {0,0.1,0.9,0.7}, -- durotar
|
||||
-- [38] = {0,0.1,0.9,0.7} -- loch modan
|
||||
}
|
||||
@@ -1000,13 +896,12 @@ QuestieMap.zoneWaypointHoverColorOverrides = {
|
||||
}
|
||||
|
||||
function QuestieMap:DrawWaypoints(icon, waypoints, zone, color)
|
||||
if waypoints and waypoints[1] and waypoints[1][1] and waypoints[1][1][1] then -- check that waypoint data actually exists
|
||||
if waypoints and waypoints[1] and waypoints[1][1] and waypoints[1][1][1] then
|
||||
local lineFrames = QuestieFramePool:CreateWaypoints(icon, waypoints, nil, color or QuestieMap.zoneWaypointColorOverrides[zone], zone)
|
||||
local lIndex = 1
|
||||
while lineFrames[lIndex] do
|
||||
local lineFrame = lineFrames[lIndex]
|
||||
for _, lineFrame in ipairs(lineFrames) do
|
||||
QuestieMap:DrawLineIcon(lineFrame, zone, waypoints[1][1][1], waypoints[1][1][2])
|
||||
lIndex = lIndex + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return QuestieMap
|
||||
|
||||
+51
-35
@@ -2,6 +2,8 @@
|
||||
|
||||
local QuestieInit = QuestieLoader:CreateModule("QuestieInit")
|
||||
local _QuestieInit = QuestieInit.private
|
||||
local compilationDeferred = false
|
||||
local needsCompilation = false
|
||||
|
||||
---@type ThreadLib
|
||||
local ThreadLib = QuestieLoader:ImportModule("ThreadLib")
|
||||
@@ -58,6 +60,9 @@ local QuestieValidateGameCache = QuestieLoader:ImportModule("QuestieValidateGame
|
||||
local MinimapIcon = QuestieLoader:ImportModule("MinimapIcon")
|
||||
---@type QuestieComms
|
||||
local QuestieComms = QuestieLoader:ImportModule("QuestieComms");
|
||||
|
||||
local WOW_PROJECT_ID = QuestieCompat.WOW_PROJECT_ID
|
||||
local WOW_PROJECT_CLASSIC = QuestieCompat.WOW_PROJECT_CLASSIC
|
||||
---@type QuestieCompat
|
||||
local QuestieCompat = QuestieLoader:ImportModule("QuestieCompat")
|
||||
---@type QuestieOptions
|
||||
@@ -196,7 +201,6 @@ end
|
||||
|
||||
-- ********************************************************************************
|
||||
-- Start of QuestieInit.Stages ******************************************************
|
||||
|
||||
-- stage worker functions. Most are coroutines.
|
||||
QuestieInit.Stages = {}
|
||||
|
||||
@@ -268,12 +272,19 @@ QuestieInit.Stages[1] = function() -- run as a coroutine
|
||||
end
|
||||
|
||||
-- Check if the DB needs to be recompiled
|
||||
do
|
||||
local addonV = QuestieLib:GetAddonVersionString()
|
||||
local uiLoc = l10n:GetUILocale()
|
||||
local storedExp = Questie.db.global.dbCompiledExpansion
|
||||
end
|
||||
if (not dbIsCompiled) or (QuestieLib:GetAddonVersionString() ~= dbCompiledOnVersion) or (l10n:GetUILocale() ~= dbCompiledLang) or (Questie.db.global.dbCompiledExpansion ~= WOW_PROJECT_ID) then
|
||||
local dbCompiledOnVersion = Questie.db.global.dbCompiledOnVersion
|
||||
local dbCompiledLang = Questie.db.global.dbCompiledLang
|
||||
local dbIsCompiled = Questie.db.global.dbIsCompiled
|
||||
|
||||
needsCompilation = (not dbIsCompiled) or (QuestieLib:GetAddonVersionString() ~= dbCompiledOnVersion) or (l10n:GetUILocale() ~= dbCompiledLang) or (Questie.db.global.dbCompiledExpansion ~= WOW_PROJECT_ID)
|
||||
|
||||
-- Custom servers or presence of DB plugins: always defer to Stage3 to wait for plugin data injection
|
||||
if Questie.IsAscension or Questie.IsEbonhold or Questie.IsValanior or Questie.IsTurtle or QuestieServer:IsAnyDBPluginEnabled() then
|
||||
compilationDeferred = true
|
||||
l10n:Initialize()
|
||||
coYield()
|
||||
QuestieCorrections:MinimalInit() -- Needed for Stage2 which runs before Stage3
|
||||
elseif needsCompilation then
|
||||
print("|cFFAAEEFF" ..
|
||||
l10n("Questie DB has updated!") ..
|
||||
"|r|cFFFF6F22 " .. l10n("Data is being processed, this may take a few moments and cause some lag..."))
|
||||
@@ -287,7 +298,6 @@ QuestieInit.Stages[1] = function() -- run as a coroutine
|
||||
l10n:Initialize()
|
||||
coYield()
|
||||
QuestieCorrections:MinimalInit()
|
||||
-- DB is cached — LoadBaseDB never runs, so the plugin loader will handle stats ingestion.
|
||||
end
|
||||
|
||||
local dbCompiledCount = Questie.IsSoD and Questie.db.global.sod.dbCompiledCount or Questie.db.global.dbCompiledCount
|
||||
@@ -361,9 +371,39 @@ end
|
||||
QuestieInit.Stages[3] = function() -- run as a coroutine
|
||||
Questie:Debug(Questie.DEBUG_INFO, "[QuestieInit:Stage3] Stage 3 start.")
|
||||
|
||||
-- Wait for registered plugins to finish loading (ensure data injection is complete)
|
||||
local QuestiePluginAPI = QuestieLoader:ImportModule("QuestiePluginAPI")
|
||||
local waitStart = GetTime()
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] Waiting for plugins to register/finish. Initial pending: " .. QuestiePluginAPI.pendingPluginsCount)
|
||||
|
||||
-- Give other addons/scripts a moment to fire and register if they were waiting for PLAYER_LOGIN
|
||||
while (GetTime() - waitStart < 1.0) do
|
||||
coYield()
|
||||
end
|
||||
|
||||
local timeout = 10
|
||||
local elapsed = GetTime() - waitStart
|
||||
while QuestiePluginAPI:HasPendingPlugins() and (elapsed < timeout) do
|
||||
coYield()
|
||||
elapsed = GetTime() - waitStart
|
||||
end
|
||||
|
||||
-- Always re-compile on custom servers to pick up QuestieLearner changes from SavedVariables,
|
||||
-- or if compilation was explicitly deferred/needed.
|
||||
local isCustomServer = Questie.IsAscension or Questie.IsEbonhold or Questie.IsValanior or Questie.IsTurtle or QuestieServer:IsAnyDBPluginEnabled()
|
||||
if isCustomServer or needsCompilation or (not Questie.db.global.dbIsCompiled) then
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] Starting compilation (Server=" .. tostring(isCustomServer) .. ", Needed=" .. tostring(needsCompilation) .. ")")
|
||||
if not QuestieDB.questData then
|
||||
loadFullDatabase()
|
||||
end
|
||||
QuestieDBCompiler:Compile()
|
||||
QuestieDB:Initialize()
|
||||
end
|
||||
|
||||
-- register events that rely on questie being initialized
|
||||
QuestieEventHandler:RegisterLateEvents()
|
||||
|
||||
|
||||
-- ** OLD ** Questie:ContinueInit() ** START **
|
||||
QuestieTooltips:Initialize()
|
||||
QuestieCoords:Initialize()
|
||||
@@ -447,31 +487,7 @@ QuestieInit.Stages[3] = function() -- run as a coroutine
|
||||
end
|
||||
end
|
||||
|
||||
-- Wait for registered plugins to finish loading (ensure data injection is complete)
|
||||
local QuestiePluginAPI = QuestieLoader:ImportModule("QuestiePluginAPI")
|
||||
local waitStart = GetTime()
|
||||
-- Give other addons/scripts a moment to fire and register if they were waiting for PLAYER_LOGIN
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] Waiting for plugins to register/finish. Initial pending: " .. QuestiePluginAPI.pendingPluginsCount)
|
||||
|
||||
while (GetTime() - waitStart < 2.0) do
|
||||
coYield()
|
||||
end
|
||||
|
||||
local timeout = 10
|
||||
local elapsed = GetTime() - waitStart
|
||||
while QuestiePluginAPI:HasPendingPlugins() and (elapsed < timeout) do
|
||||
if ((math.floor(elapsed * 10) % 10) == 0) then -- log every second
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] Still waiting for plugins... Pending: " .. QuestiePluginAPI.pendingPluginsCount .. " Elapsed: " .. string.format("%.1f", elapsed))
|
||||
end
|
||||
coYield()
|
||||
elapsed = GetTime() - waitStart
|
||||
end
|
||||
|
||||
if QuestiePluginAPI:HasPendingPlugins() then
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] TIMEOUT waiting for plugins! Proceeding anyway. Pending: " .. QuestiePluginAPI.pendingPluginsCount)
|
||||
else
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieInit:Stage3] All registered plugins finished loading.")
|
||||
end
|
||||
|
||||
|
||||
-- We do this last because it will run for a while and we don't want to block the rest of the init
|
||||
coYield()
|
||||
@@ -565,7 +581,7 @@ end
|
||||
-- called by the PLAYER_LOGIN event handler
|
||||
function QuestieInit:Init()
|
||||
QuestieInit.Thread = coroutine.create(_QuestieInit.StartStageCoroutine)
|
||||
|
||||
|
||||
local function resumeInit()
|
||||
if not QuestieInit.Thread or coroutine.status(QuestieInit.Thread) == "dead" then
|
||||
return -- coroutine finished or failed
|
||||
@@ -580,7 +596,7 @@ function QuestieInit:Init()
|
||||
C_Timer.After(0.02, resumeInit) -- continue yielding using the timer shim
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
resumeInit()
|
||||
|
||||
if Questie.db.profile.trackerEnabled then
|
||||
|
||||
+147
-23
@@ -649,6 +649,8 @@ function QuestieLearner:LearnQuest(questId, data)
|
||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] LearnQuest blocked: learner not enabled")
|
||||
return
|
||||
end
|
||||
questId = tonumber(questId)
|
||||
if not questId or questId <= 0 then return end
|
||||
if not Questie.dbLearner.global.settings.learnQuests then
|
||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] LearnQuest blocked: learnQuests=", tostring(Questie.dbLearner.global.settings.learnQuests))
|
||||
return
|
||||
@@ -695,6 +697,7 @@ end
|
||||
function QuestieLearner:LearnQuestGiver(questId, entityId, entityType, isStart)
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnQuests then return end
|
||||
questId, entityId = tonumber(questId), tonumber(entityId)
|
||||
if not questId or questId <= 0 or not entityId or entityId <= 0 then return end
|
||||
|
||||
local existing = Questie.dbLearner.global.quests[questId]
|
||||
@@ -740,9 +743,11 @@ end
|
||||
-- Adds npcId as a creatureObjective for questId ([10][1] in questKeys schema).
|
||||
-- If the NPC already exists in the base DB the spawn data is already there;
|
||||
-- we only need the quest to reference it so tooltips/map-pins get registered.
|
||||
function QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText)
|
||||
function QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText, objectiveIndex)
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnQuests then return end
|
||||
questId, npcId = tonumber(questId), tonumber(npcId)
|
||||
objectiveIndex = tonumber(objectiveIndex)
|
||||
if not questId or questId <= 0 or not npcId or npcId <= 0 then return end
|
||||
|
||||
-- 1. Persist to SavedVariables
|
||||
@@ -758,6 +763,27 @@ function QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText)
|
||||
table.insert(existing[10][1], { npcId, objText or "" })
|
||||
end
|
||||
|
||||
-- 1.1 Persist exact objective index mapping
|
||||
if objectiveIndex then
|
||||
existing.objIndex = existing.objIndex or {}
|
||||
local entry = existing.objIndex[objectiveIndex]
|
||||
if not entry then
|
||||
existing.objIndex[objectiveIndex] = { type = "monster", id = npcId, text = objText or "" }
|
||||
elseif entry.id ~= npcId then
|
||||
-- Handle Kill Credit (multiple NPCs for one objective)
|
||||
if type(entry.id) == "number" then
|
||||
entry.id = { entry.id, npcId }
|
||||
entry.type = "killcredit"
|
||||
else
|
||||
local found = false
|
||||
for _, id in ipairs(entry.id) do
|
||||
if id == npcId then found = true; break end
|
||||
end
|
||||
if not found then table.insert(entry.id, npcId) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 2. Apply to live questDataOverrides immediately (no reload needed)
|
||||
if QuestieDB and QuestieDB.questDataOverrides then
|
||||
local ovr = QuestieDB.questDataOverrides[questId] or {}
|
||||
@@ -771,6 +797,12 @@ function QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText)
|
||||
if not alreadyPresent then
|
||||
table.insert(ovr[10][1], { npcId, objText or "" })
|
||||
end
|
||||
|
||||
-- Update live objIndex override
|
||||
if objectiveIndex then
|
||||
ovr.objIndex = ovr.objIndex or {}
|
||||
ovr.objIndex[objectiveIndex] = existing.objIndex[objectiveIndex]
|
||||
end
|
||||
end
|
||||
|
||||
-- 3. Re-process the quest so PopulateObjective registers tooltips & map pins
|
||||
@@ -797,6 +829,7 @@ end
|
||||
function QuestieLearner:LearnItem(itemId, name, itemLevel, requiredLevel, itemClass, itemSubClass)
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnItems then return end
|
||||
itemId = tonumber(itemId)
|
||||
if not itemId or itemId <= 0 then return end
|
||||
|
||||
local existing = Questie.dbLearner.global.items[itemId]
|
||||
@@ -836,6 +869,7 @@ end
|
||||
function QuestieLearner:LearnItemDrop(itemId, npcId)
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnItems then return end
|
||||
itemId, npcId = tonumber(itemId), tonumber(npcId)
|
||||
if not itemId or itemId <= 0 or not npcId or npcId <= 0 then return end
|
||||
|
||||
local existing = Questie.dbLearner.global.items[itemId]
|
||||
@@ -875,6 +909,7 @@ end
|
||||
function QuestieLearner:LearnObject(objectId, name)
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnObjects then return end
|
||||
objectId = tonumber(objectId)
|
||||
if not objectId or objectId <= 0 then return end
|
||||
|
||||
local zoneId = GetZoneId()
|
||||
@@ -970,13 +1005,18 @@ function QuestieLearner:InjectLearnedData()
|
||||
local npcCount, questCount, itemCount, objectCount = 0, 0, 0, 0
|
||||
|
||||
-- 1. NPCs
|
||||
local npcIdsToFix = {}
|
||||
for npcId, data in pairs(learned.npcs) do
|
||||
local nid = tonumber(npcId)
|
||||
if type(npcId) == "string" and nid then
|
||||
npcIdsToFix[npcId] = nid
|
||||
end
|
||||
self:Sanitize(data)
|
||||
if not QuestieDB.npcDataOverrides[npcId] then
|
||||
QuestieDB.npcDataOverrides[npcId] = data
|
||||
if not QuestieDB.npcDataOverrides[nid or npcId] then
|
||||
QuestieDB.npcDataOverrides[nid or npcId] = data
|
||||
npcCount = npcCount + 1
|
||||
else
|
||||
local existing = QuestieDB.npcDataOverrides[npcId]
|
||||
local existing = QuestieDB.npcDataOverrides[nid or npcId]
|
||||
if data[7] then
|
||||
existing[7] = existing[7] or {}
|
||||
for zoneId, coords in pairs(data[7]) do
|
||||
@@ -994,9 +1034,18 @@ function QuestieLearner:InjectLearnedData()
|
||||
end
|
||||
end
|
||||
end
|
||||
for old, new in pairs(npcIdsToFix) do
|
||||
learned.npcs[new] = learned.npcs[old]
|
||||
learned.npcs[old] = nil
|
||||
end
|
||||
|
||||
-- 2. Quests
|
||||
local questIdsToFix = {}
|
||||
for questId, data in pairs(learned.quests) do
|
||||
local qid = tonumber(questId)
|
||||
if type(questId) == "string" and qid then
|
||||
questIdsToFix[questId] = qid
|
||||
end
|
||||
self:Sanitize(data)
|
||||
-- Legacy cleanup for malformed objective data
|
||||
if data[10] ~= nil then
|
||||
@@ -1012,11 +1061,11 @@ function QuestieLearner:InjectLearnedData()
|
||||
data[8] = nil
|
||||
end
|
||||
|
||||
if not QuestieDB.questDataOverrides[questId] then
|
||||
QuestieDB.questDataOverrides[questId] = data
|
||||
if not QuestieDB.questDataOverrides[qid or questId] then
|
||||
QuestieDB.questDataOverrides[qid or questId] = data
|
||||
questCount = questCount + 1
|
||||
else
|
||||
local existing = QuestieDB.questDataOverrides[questId]
|
||||
local existing = QuestieDB.questDataOverrides[qid or questId]
|
||||
for k, v in pairs(data) do
|
||||
if k ~= "mc" then
|
||||
if k == 10 then
|
||||
@@ -1041,23 +1090,41 @@ function QuestieLearner:InjectLearnedData()
|
||||
end
|
||||
end
|
||||
end
|
||||
for old, new in pairs(questIdsToFix) do
|
||||
learned.quests[new] = learned.quests[old]
|
||||
learned.quests[old] = nil
|
||||
end
|
||||
|
||||
-- 3. Items
|
||||
local itemIdsToFix = {}
|
||||
for itemId, data in pairs(learned.items) do
|
||||
if not QuestieDB.itemDataOverrides[itemId] then
|
||||
QuestieDB.itemDataOverrides[itemId] = data
|
||||
local iid = tonumber(itemId)
|
||||
if type(itemId) == "string" and iid then
|
||||
itemIdsToFix[itemId] = iid
|
||||
end
|
||||
if not QuestieDB.itemDataOverrides[iid or itemId] then
|
||||
QuestieDB.itemDataOverrides[iid or itemId] = data
|
||||
itemCount = itemCount + 1
|
||||
end
|
||||
end
|
||||
for old, new in pairs(itemIdsToFix) do
|
||||
learned.items[new] = learned.items[old]
|
||||
learned.items[old] = nil
|
||||
end
|
||||
|
||||
-- 4. Objects
|
||||
local objectIdsToFix = {}
|
||||
for objectId, data in pairs(learned.objects) do
|
||||
local oid = tonumber(objectId)
|
||||
if type(objectId) == "string" and oid then
|
||||
objectIdsToFix[objectId] = oid
|
||||
end
|
||||
self:Sanitize(data)
|
||||
if not QuestieDB.objectDataOverrides[objectId] then
|
||||
QuestieDB.objectDataOverrides[objectId] = data
|
||||
if not QuestieDB.objectDataOverrides[oid or objectId] then
|
||||
QuestieDB.objectDataOverrides[oid or objectId] = data
|
||||
objectCount = objectCount + 1
|
||||
else
|
||||
local existing = QuestieDB.objectDataOverrides[objectId]
|
||||
local existing = QuestieDB.objectDataOverrides[oid or objectId]
|
||||
if data[4] then
|
||||
existing[4] = existing[4] or {}
|
||||
for zoneId, coords in pairs(data[4]) do
|
||||
@@ -1075,6 +1142,10 @@ function QuestieLearner:InjectLearnedData()
|
||||
end
|
||||
end
|
||||
end
|
||||
for old, new in pairs(objectIdsToFix) do
|
||||
learned.objects[new] = learned.objects[old]
|
||||
learned.objects[old] = nil
|
||||
end
|
||||
|
||||
if npcCount > 0 or questCount > 0 or itemCount > 0 or objectCount > 0 then
|
||||
Questie:Debug(Questie.DEBUG_INFO, "[QuestieLearner] Injected learned data:",
|
||||
@@ -1377,9 +1448,33 @@ function QuestieLearner:OnQuestComplete()
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper to find an NPC ID by name (case-insensitive substring match)
|
||||
-- Used for proactive objective mapping when a quest is first accepted.
|
||||
function QuestieLearner:GetNPCIdByName(npcName)
|
||||
if not npcName or npcName == "" then return nil end
|
||||
local lowerName = string.lower(npcName)
|
||||
|
||||
-- Check overrides first (most likely for custom servers)
|
||||
if QuestieDB.npcDataOverrides then
|
||||
for id, data in pairs(QuestieDB.npcDataOverrides) do
|
||||
if data and data[1] and string.lower(data[1]) == lowerName then
|
||||
return id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check base NPC database
|
||||
-- Note: QueryNPCSingle is a dummy until initialization, but we can fallback to raw access
|
||||
local npcData = QuestieDB.npcData or {}
|
||||
for id, data in pairs(npcData) do
|
||||
if data and data[1] and string.lower(data[1]) == lowerName then
|
||||
return id
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Fires when a quest is accepted.
|
||||
-- Ascension 3.3.5 passes the quest log index as the first arg; some builds pass questID directly.
|
||||
-- We detect which by checking if the value could be a log index and resolving via GetQuestLogTitle.
|
||||
function QuestieLearner:OnQuestAccepted(firstArg, secondArg)
|
||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] OnQuestAccepted raw args: first=" .. tostring(firstArg) .. " second=" .. tostring(secondArg))
|
||||
local questId
|
||||
@@ -1429,19 +1524,47 @@ function QuestieLearner:OnQuestAccepted(firstArg, secondArg)
|
||||
-- Zone: reverse-lookup from GetRealZoneText() which is always accurate on 3.3.5.
|
||||
local zoneText = GetRealZoneText()
|
||||
if zoneText and zoneText ~= "" then
|
||||
for _, zoneTable in pairs(l10n.zoneLookup) do
|
||||
for areaId, name in pairs(zoneTable) do
|
||||
if name == zoneText then
|
||||
data[17] = areaId
|
||||
break
|
||||
end
|
||||
end
|
||||
if data[17] then break end
|
||||
local areaId = l10n:GetAreaIdByLocalName(zoneText)
|
||||
if areaId and areaId > 0 then
|
||||
data[17] = areaId
|
||||
end
|
||||
end
|
||||
|
||||
self:LearnQuest(questId, data)
|
||||
|
||||
-- Proactively map objectives based on quest log text
|
||||
local logIdx = 0
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local _, _, _, isHeader, _, _, _, id = QuestieCompat.GetQuestLogTitle(i)
|
||||
if not isHeader and id == questId then
|
||||
logIdx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if logIdx > 0 then
|
||||
local numObj = GetNumQuestLeaderBoards and GetNumQuestLeaderBoards(logIdx) or 0
|
||||
for j = 1, numObj do
|
||||
local objText, objType, finished = GetQuestLogLeaderBoard(j, logIdx)
|
||||
if objText and not finished and objType == "monster" then
|
||||
-- Try to extract "Boar" from "0/10 Boar Slain"
|
||||
local targetName = objText:match("^%d+/%d+%s+(.+)%s*") or objText:match("^(.+):%s*%d+/%d+")
|
||||
if not targetName then
|
||||
-- Fallback: strip everything that looks like a count or punctuation
|
||||
targetName = objText:gsub("%d+/%d+", ""):gsub("%d+", ""):gsub("[:!?,.%(%)]", ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
|
||||
if targetName and targetName ~= "" then
|
||||
local npcId = self:GetNPCIdByName(targetName)
|
||||
if npcId then
|
||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] Proactively mapped objective", j, "to NPC", npcId, "(" .. targetName .. ")")
|
||||
self:LearnQuestObjectiveNPC(questId, npcId, objText, j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Associate the quest giver: prefer live UnitGUID("npc"), fall back to last gossip entity
|
||||
-- (for Objectives Board quests, GOSSIP_CLOSED fires before QUEST_ACCEPTED so "npc" is nil)
|
||||
local npcGuid = UnitGUID("npc")
|
||||
@@ -1471,6 +1594,7 @@ end
|
||||
function QuestieLearner:OnQuestTurnedIn(questId, xpReward, moneyReward)
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnQuests then return end
|
||||
questId = tonumber(questId)
|
||||
if not questId or questId <= 0 then return end
|
||||
|
||||
local data = {}
|
||||
@@ -1745,7 +1869,7 @@ function QuestieLearner:OnQuestLogUpdate()
|
||||
"progressed — learning kill NPC:", bestKill.npcId, bestKill.name)
|
||||
-- Pass exact kill coordinates so spawn list reflects NPC location, not player location
|
||||
self:LearnNPC(bestKill.npcId, bestKill.name, nil, nil, nil, nil, bestKill.x, bestKill.y, bestKill.zoneId)
|
||||
self:LearnQuestObjectiveNPC(questId, bestKill.npcId, cleanText)
|
||||
self:LearnQuestObjectiveNPC(questId, bestKill.npcId, cleanText, j)
|
||||
_Learner.recentKills[bestGuid] = nil
|
||||
end
|
||||
_Learner.prevObjCounts[questId][j] = count
|
||||
|
||||
@@ -125,13 +125,9 @@ function QuestiePlayer:GetCurrentContinentId()
|
||||
return 1 -- Default to Eastern Kingdom
|
||||
end
|
||||
|
||||
local currentContinentId = 1 -- Default to Eastern Kingdom
|
||||
for cId, cont in pairs(l10n.zoneLookup) do
|
||||
for id, _ in pairs(cont) do
|
||||
if id == currentZoneId then
|
||||
currentContinentId = cId
|
||||
end
|
||||
end
|
||||
local currentContinentId = l10n:GetContinentIdByAreaId(currentZoneId)
|
||||
if currentContinentId == 0 then
|
||||
return 1 -- Default to Eastern Kingdom
|
||||
end
|
||||
|
||||
return currentContinentId
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---@class QuestieServer
|
||||
local QuestieServer = QuestieLoader:CreateModule("QuestieServer")
|
||||
|
||||
---@type string
|
||||
local realmName = GetRealmName() or ""
|
||||
local realmName = GetRealmName() or (GetCVar and GetCVar("realmName")) or ""
|
||||
local lRealmName = realmName:lower()
|
||||
|
||||
-- Client flavor detection from WoW globals
|
||||
local WOW_PROJECT_ID = WOW_PROJECT_ID or -1
|
||||
@@ -27,12 +27,12 @@ Questie.IsEbonhold = false
|
||||
Questie.IsAscension = false
|
||||
Questie.IsValanior = false
|
||||
|
||||
if _G.IsAscensionServer or realmName:find("Ascension") or realmName:find("Area 52") or
|
||||
realmName:find("Al'ar") or realmName:find("Thrall") then
|
||||
if _G.IsAscensionServer or lRealmName:find("ascension") or lRealmName:find("area 52") or
|
||||
lRealmName:find("al'ar") or lRealmName:find("thrall") or lRealmName:find("bronzebeard") or lRealmName:find("warcraft reborn") then
|
||||
Questie.IsAscension = true
|
||||
elseif realmName == "Ebonhold" or realmName == "Test Ebonhold" then
|
||||
elseif lRealmName:find("ebonhold") then
|
||||
Questie.IsEbonhold = true
|
||||
elseif realmName == "Valanior" then
|
||||
elseif lRealmName:find("valanior") then
|
||||
Questie.IsValanior = true
|
||||
end
|
||||
|
||||
@@ -57,6 +57,31 @@ function QuestieServer:Init()
|
||||
"IsAscension:", tostring(Questie.IsAscension), "IsEbonhold:", tostring(Questie.IsEbonhold))
|
||||
end
|
||||
|
||||
--- Returns true if any Questie-X Database plugin addon is present and enabled.
|
||||
--- This check is used to defer compilation even if server detection fails.
|
||||
function QuestieServer:IsAnyDBPluginEnabled()
|
||||
local dbPlugins = {
|
||||
"Questie-X-WotLKDB",
|
||||
"Questie-X-ClassicDB",
|
||||
"Questie-X-TBCDB",
|
||||
"Questie-X-TurtleDB",
|
||||
"Questie-X-AscensionDB",
|
||||
"Questie-X-EbonholdDB",
|
||||
"Questie-X-ValaniorDB",
|
||||
"Questie-X-RetailDB"
|
||||
}
|
||||
|
||||
for _, addonName in ipairs(dbPlugins) do
|
||||
-- GetAddOnInfo(index or name)
|
||||
-- returns name, title, notes, loadable, reason, security
|
||||
local name, title, notes, loadable, reason, security = GetAddOnInfo(addonName)
|
||||
if name and loadable then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Checks if the correct DB plugin is loaded for the detected client/server.
|
||||
--- Prints a friendly actionable warning to the chat frame if none is found.
|
||||
function QuestieServer:WarnIfMissingPlugin()
|
||||
|
||||
@@ -701,13 +701,7 @@ TrackerUtils._fallbackQuests = TrackerUtils._fallbackQuests or {}
|
||||
|
||||
-- Reverse-lookup: given a localized zone name string, find the area ID from l10n.zoneLookup.
|
||||
local function GetAreaIdByZoneName(zoneName)
|
||||
if not zoneName or zoneName == "" then return 0 end
|
||||
for _, zoneTable in pairs(l10n.zoneLookup) do
|
||||
for areaId, name in pairs(zoneTable) do
|
||||
if name == zoneName then return areaId end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
return l10n:GetAreaIdByLocalName(zoneName)
|
||||
end
|
||||
|
||||
-- Walk the quest log to find the zone header for a given questId.
|
||||
|
||||
Reference in New Issue
Block a user