c4d003e58f
- Repoint remote to Xurkon/Questie-X - Add QuestiePluginAPI, QuestieServer, QuestieLearnerComms modules - Add Questie-X.toc, Questie-X-Classic.toc, Questie-X-TBC.toc, Questie-X-Turtle.toc - Remove embedded Database/Ascension and Database/Ebonhold (migrated to plugins) - Add Plugins/ directory with NTFS junctions for Questie-X-AscensionDB and Questie-X-EbonholdDB - Add LibDeflate, XXH_Lua_Lib, LibDBIcon-1.0, LibDataBroker-1.1 - Update README: Questie-X branding, logo, plugin install guide, plugin API docs - Update CHANGELOG: v1.1.4 entry documenting all architectural changes - Update .gitignore: exclude plugin junctions, __pycache__, .agents, debug files - Various module updates: corrections, map, tracker, tooltips, quest, options
399 lines
14 KiB
Lua
399 lines
14 KiB
Lua
---@class QuestieFramePool
|
|
local QuestieFramePool = QuestieLoader:CreateModule("QuestieFramePool")
|
|
-------------------------
|
|
--Import modules.
|
|
-------------------------
|
|
---@type QuestieQuest
|
|
local QuestieQuest = QuestieLoader:ImportModule("QuestieQuest")
|
|
---@type QuestieMap
|
|
local QuestieMap = QuestieLoader:ImportModule("QuestieMap")
|
|
---@type MapIconTooltip
|
|
local MapIconTooltip = QuestieLoader:ImportModule("MapIconTooltip")
|
|
---@type QuestieLib
|
|
local QuestieLib = QuestieLoader:ImportModule("QuestieLib")
|
|
---@type l10n
|
|
local l10n = QuestieLoader:ImportModule("l10n")
|
|
|
|
--- COMPATIBILITY ---
|
|
local C_Timer = QuestieCompat.C_Timer
|
|
local WorldMapFrame = QuestieCompat.WorldMapFrame
|
|
|
|
local HBDPins = QuestieCompat.HBDPins or LibStub("HereBeDragonsQuestie-Pins-2.0")
|
|
|
|
-- set pins parent to QuestieFrameGroup for easier compatibility with other addons
|
|
-- cant use this because it fucks with everything, but we gotta stick with HereBeDragonsQuestie anyway
|
|
HBDPins.MinimapGroup = CreateFrame("Frame", "QuestieFrameGroup", Minimap)
|
|
local WAYPOINT_COLOR = { 1, 0.72, 0, 0.5}
|
|
|
|
local _QuestieFramePool = {}
|
|
local numberOfFrames = 0
|
|
|
|
local unusedFrames = {}
|
|
local usedFrames = {};
|
|
local allFrames = {}
|
|
|
|
StaticPopupDialogs["QUESTIE_CONFIRMHIDE"] = {
|
|
text = "", -- set before showing
|
|
questID = 0, -- set before showing
|
|
button1 = l10n("Yes"),
|
|
button2 = l10n("No"),
|
|
OnAccept = function()
|
|
QuestieQuest:HideQuest(StaticPopupDialogs["QUESTIE_CONFIRMHIDE"].questID)
|
|
end,
|
|
SetQuest = function(self, id)
|
|
self.questID = id
|
|
self.text = l10n("Are you sure you want to hide the quest '%s'?\nIf this quest isn't actually available, please report it to us!", QuestieLib:GetColoredQuestName(id, Questie.db.profile.enableTooltipsQuestLevel, false, true))
|
|
|
|
-- locale might not be loaded when this is first created (this does happen almost always)
|
|
self.button1 = l10n("Yes")
|
|
self.button2 = l10n("No")
|
|
end,
|
|
OnShow = function(self)
|
|
self:SetFrameStrata("TOOLTIP")
|
|
end,
|
|
timeout = 0,
|
|
whileDead = true,
|
|
hideOnEscape = true,
|
|
preferredIndex = 3
|
|
}
|
|
|
|
-- Global Functions --
|
|
---@return IconFrame
|
|
function QuestieFramePool:GetFrame()
|
|
--Questie:Debug(Questie.DEBUG_SPAM, "[QuestieFramePool:GetFrame]")
|
|
|
|
---@type IconFrame
|
|
local returnFrame = next(unusedFrames)
|
|
returnFrame = returnFrame and unusedFrames[returnFrame]
|
|
|
|
if returnFrame and returnFrame.frameId and usedFrames[returnFrame.frameId] then
|
|
-- something went horribly wrong (desync bug?) don't use this frame since its already in use
|
|
Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieFramePool:GetFrame] Tried to reuse frame, but that frame is already in use. frameId:", returnFrame.frameId)
|
|
returnFrame = nil
|
|
end
|
|
if not returnFrame then
|
|
returnFrame = _QuestieFramePool:QuestieCreateFrame()
|
|
else
|
|
--Questie:Debug(Questie.DEBUG_SPAM, "[QuestieFramePool:GetFrame] Reusing frame")
|
|
unusedFrames[returnFrame.frameId] = nil
|
|
end
|
|
if returnFrame ~= nil and returnFrame.hidden and returnFrame._show ~= nil and returnFrame._hide ~= nil then -- restore state to normal (toggle questie)
|
|
returnFrame.hidden = false
|
|
returnFrame.Show = returnFrame._show;
|
|
returnFrame.Hide = returnFrame._hide;
|
|
returnFrame._show = nil
|
|
returnFrame._hide = nil
|
|
end
|
|
returnFrame.FadeLogic = nil
|
|
returnFrame.faded = nil
|
|
returnFrame.miniMapIcon = nil
|
|
|
|
returnFrame.data = nil
|
|
returnFrame.x = nil;
|
|
returnFrame.y = nil;
|
|
returnFrame.AreaID = nil;
|
|
returnFrame.UiMapID = nil
|
|
|
|
if returnFrame.texture then
|
|
returnFrame.texture:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
returnFrame.loaded = true
|
|
returnFrame.shouldBeShowing = nil
|
|
returnFrame.hidden = nil
|
|
returnFrame._needsUnload = nil
|
|
|
|
if returnFrame.BaseOnShow then
|
|
returnFrame:SetScript("OnShow", returnFrame.BaseOnShow)
|
|
end
|
|
|
|
if returnFrame.BaseOnUpdate then
|
|
returnFrame.glowLogicTimer = C_Timer.NewTicker(1, returnFrame.BaseOnUpdate);
|
|
else
|
|
returnFrame:SetScript("OnUpdate", nil)
|
|
end
|
|
|
|
if returnFrame.BaseOnHide then
|
|
returnFrame:SetScript("OnHide", returnFrame.BaseOnHide)
|
|
end
|
|
|
|
usedFrames[returnFrame.frameId] = returnFrame
|
|
return returnFrame
|
|
end
|
|
|
|
function QuestieFramePool:UpdateGlowConfig(mini, mode)
|
|
if mode then
|
|
for _, icon in pairs(usedFrames) do
|
|
if (((mini and icon.miniMapIcon) or not mini) and icon.glow) and icon.IsShown and icon:IsShown() then
|
|
icon:GetScript("OnShow")(icon) -- forces a glow update
|
|
end
|
|
end
|
|
else
|
|
for _, icon in pairs(usedFrames) do
|
|
if ((mini and icon.miniMapIcon) or (not mini and not icon.miniMapIcon)) and icon.glow then
|
|
icon.glow:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function QuestieFramePool:UpdateColorConfig(mini, enable)
|
|
if enable then
|
|
for _, icon in pairs(usedFrames) do
|
|
if (mini and icon.miniMapIcon) or (not mini and not icon.miniMapIcon) then
|
|
local colors = {1, 1, 1}
|
|
if icon.data.IconColor ~= nil then
|
|
colors = icon.data.IconColor
|
|
end
|
|
icon.texture:SetVertexColor(colors[1], colors[2], colors[3], 1)
|
|
end
|
|
end
|
|
else
|
|
for _, icon in pairs(usedFrames) do
|
|
if (mini and icon.miniMapIcon) or (not mini and not icon.miniMapIcon) then
|
|
icon.texture:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function QuestieFramePool:RecycleFrame(frame)
|
|
--Questie:Debug(Questie.DEBUG_SPAM, "[QuestieFramePool:RecycleFrame]")
|
|
usedFrames[frame.frameId] = nil
|
|
unusedFrames[frame.frameId] = frame
|
|
end
|
|
|
|
function _QuestieFramePool:QuestieCreateFrame()
|
|
--Questie:Debug(Questie.DEBUG_SPAM, "[QuestieFramePool:QuestieCreateFrame]")
|
|
numberOfFrames = numberOfFrames + 1
|
|
local newFrame = QuestieFramePool.Qframe:New(numberOfFrames, MapIconTooltip.Show)
|
|
|
|
tinsert(allFrames, newFrame)
|
|
return newFrame
|
|
end
|
|
|
|
|
|
---@param iconFrame IconFrame @The parent frame for the current line.
|
|
---@param waypointTable table<number, Point> @A table containing waypoints {{X, Y}, ...}
|
|
---@param lineWidth number @Width of the line.
|
|
---@param color number[] @A table consisting of 4 variable {1, 1, 1, 1} RGB-Opacity
|
|
---@return LineFrame[]
|
|
function QuestieFramePool:CreateWaypoints(iconFrame, waypointTable, lineWidth, color, areaId)
|
|
local lineFrameList = {}
|
|
local lastPos
|
|
--Set defaults if needed.
|
|
local lWidth = lineWidth or 1.5;
|
|
local col = color or WAYPOINT_COLOR
|
|
|
|
for _, waypointSubTable in pairs(waypointTable) do
|
|
lastPos = nil
|
|
for _, waypoint in pairs(waypointSubTable) do
|
|
if lastPos then
|
|
local lineFrame = QuestieFramePool:CreateLine(iconFrame, lastPos[1], lastPos[2], waypoint[1], waypoint[2], lWidth, col, areaId)
|
|
tinsert(lineFrameList, lineFrame);
|
|
end
|
|
lastPos = waypoint
|
|
end
|
|
end
|
|
return lineFrameList;
|
|
end
|
|
|
|
local lineFrameCount = 1
|
|
|
|
---@param iconFrame IconFrame @The parent frame for the current line.
|
|
---@param startX number @A value between 0-100
|
|
---@param startY number @A value between 0-100
|
|
---@param endX number @A value between 0-100
|
|
---@param endY number @A value between 0-100
|
|
---@param lineWidth number @Width of the line.
|
|
---@param color number[] @A table consisting of 4 variable {1, 1, 1, 1} RGB-Opacity
|
|
---@return LineFrame
|
|
---@class LineFrame @A frame that contains the line used in waypoints.
|
|
function QuestieFramePool:CreateLine(iconFrame, startX, startY, endX, endY, lineWidth, color, areaId)
|
|
|
|
--Create the framepool for lines if it does not already exist.
|
|
if not QuestieFramePool.Routes_Lines then
|
|
QuestieFramePool.Routes_Lines={}
|
|
end
|
|
--Names are not stricktly needed, but it is nice for debugging.
|
|
local frameName = "questieLineFrame".. lineFrameCount;
|
|
|
|
--tremove default always picks the last element, however counting arrays is kinda bugged? So just get index 1 instead.
|
|
local lineFrame = tremove(QuestieFramePool.Routes_Lines, 1) or CreateFrame("Button", frameName, iconFrame);
|
|
if not lineFrame.frameId then
|
|
lineFrame.frameId = lineFrameCount;
|
|
end
|
|
|
|
local canvas = WorldMapFrame:GetCanvas() or WorldMapDetailFrame or WorldMapFrame
|
|
|
|
local width = canvas and canvas.GetWidth and canvas:GetWidth() or 1002;
|
|
local height = canvas and canvas.GetHeight and canvas:GetHeight() or 668;
|
|
|
|
--Setting the parent is required to get the correct frame levels.
|
|
|
|
lineFrame:SetParent(canvas) --This fixes the pan and zoom for lines
|
|
if QuestieCompat.Is335 then
|
|
lineFrame.CreateLine = QuestieCompat.CreateLine
|
|
else
|
|
lineFrame:SetFrameLevel(2015) -- This needs to be high, because of the regular WorldMapFrame.ScrollContainer
|
|
lineFrame:SetFrameStrata("FULLSCREEN")
|
|
end
|
|
|
|
--How to identify what the frame actually contains, this is not used atm could easily be changed.
|
|
lineFrame.type = "line"
|
|
|
|
--Include the line in the iconFrame.
|
|
if not iconFrame.data.lineFrames then
|
|
iconFrame.data.lineFrames = {};
|
|
end
|
|
tinsert(iconFrame.data.lineFrames, lineFrame);
|
|
lineFrame.iconFrame = iconFrame;
|
|
lineFrame.data = iconFrame.data
|
|
lineFrame.x = (startX + endX) / 2
|
|
lineFrame.y = (startY + endY) / 2
|
|
lineFrame.AreaID = areaId or iconFrame.AreaID
|
|
lineFrame.texture = iconFrame.texture
|
|
|
|
function lineFrame:Unload()
|
|
if not self.iconFrame then
|
|
return -- already unloaded
|
|
end
|
|
self:Hide();
|
|
self.iconFrame = nil;
|
|
self.x = nil
|
|
self.y = nil
|
|
self.data = nil
|
|
self.texture = nil
|
|
self.AreaID = nil
|
|
HBDPins:RemoveWorldMapIcon(Questie, self)
|
|
tinsert(QuestieFramePool.Routes_Lines, self);
|
|
end
|
|
local line = lineFrame.line or lineFrame:CreateLine();
|
|
lineFrame.line = line;
|
|
|
|
line.dR = color[1];
|
|
line.dG = color[2];
|
|
line.dB = color[3];
|
|
line.dA = color[4];
|
|
line:SetColorTexture(color[1],color[2],color[3],color[4]);
|
|
|
|
local lineBorder = lineFrame.lineBorder or lineFrame:CreateLine();
|
|
lineFrame.lineBorder = lineBorder;
|
|
|
|
lineBorder.dR = color[1];
|
|
lineBorder.dG = color[2];
|
|
lineBorder.dB = color[3];
|
|
lineBorder.dA = color[4];
|
|
lineBorder:SetColorTexture(0,0,0,color[4]/2);
|
|
|
|
-- Set texture coordinates and anchors
|
|
--line:ClearAllPoints();
|
|
|
|
startX = startX * width / 100
|
|
startY = startY * height / -100 -- We do by / -100 due to using the top left point
|
|
endX = endX * width / 100
|
|
endY = endY * height / -100
|
|
|
|
width = abs(startX - endX) + lineWidth * 4
|
|
height = abs(startY - endY) + lineWidth * 4
|
|
|
|
local framePosX = max(startX, endX) - lineWidth * 2 - width / 2
|
|
local framePosY = min(startY, endY) + lineWidth * 2 + height / 2
|
|
|
|
lineFrame:SetHeight(height);
|
|
lineFrame:SetWidth(width);
|
|
lineFrame:SetPoint("TOPLEFT", canvas, "TOPLEFT", framePosX, framePosY)
|
|
|
|
line:SetDrawLayer("OVERLAY", -5)
|
|
line:SetStartPoint("TOPLEFT", startX - framePosX, startY - framePosY)
|
|
line:SetEndPoint("TOPLEFT", endX - framePosX, endY - framePosY)
|
|
line:SetThickness(lineWidth);
|
|
|
|
lineBorder:SetDrawLayer("OVERLAY", -6)
|
|
lineBorder:SetStartPoint("TOPLEFT", startX - framePosX, startY - framePosY)
|
|
lineBorder:SetEndPoint("TOPLEFT", endX - framePosX, endY - framePosY)
|
|
lineBorder:SetThickness(lineWidth+2);
|
|
|
|
lineFrame:EnableMouse(true)
|
|
|
|
--- This is needed because HBD will show the icons again after switching zones and stuff like that
|
|
function lineFrame:FakeHide()
|
|
if not self.hidden then
|
|
self.shouldBeShowing = self:IsShown();
|
|
self._show = self.Show;
|
|
self.Show = function()
|
|
self.shouldBeShowing = true;
|
|
end
|
|
self:Hide();
|
|
self._hide = self.Hide;
|
|
self.Hide = function()
|
|
self.shouldBeShowing = false;
|
|
end
|
|
self.hidden = true
|
|
end
|
|
end
|
|
|
|
--- This is needed because HBD will show the icons again after switching zones and stuff like that
|
|
function lineFrame:FakeShow()
|
|
if self.hidden then
|
|
self.hidden = false
|
|
self.Show = self._show;
|
|
self.Hide = self._hide;
|
|
self._show = nil
|
|
self._hide = nil
|
|
if self.shouldBeShowing then
|
|
self:Show();
|
|
end
|
|
end
|
|
end
|
|
|
|
--lineFrame:SetBackdrop({ -- mouseover debugging
|
|
-- bgFile = "Interface/Tooltips/UI-Tooltip-Background",
|
|
-- edgeFile = nil,
|
|
-- edgeSize = 0,
|
|
-- insets = { left = 0, right = 0, top = 0, bottom = 0 },
|
|
--})
|
|
|
|
--lineFrame:SetBackdropColor(1,0,1,1)
|
|
|
|
lineFrame:SetScript("OnEnter", function(self)
|
|
if self and self.iconFrame then
|
|
local script = self.iconFrame:GetScript("OnEnter")
|
|
if script then
|
|
script(self.iconFrame)
|
|
end
|
|
end
|
|
end)
|
|
lineFrame:SetScript("OnLeave", function(self)
|
|
if self and self.iconFrame then
|
|
local script = self.iconFrame:GetScript("OnLeave")
|
|
if script then
|
|
script(self.iconFrame)
|
|
end
|
|
end
|
|
end)
|
|
lineFrame:RegisterForClicks("RightButtonUp", "LeftButtonUp")
|
|
lineFrame:SetScript("OnClick", function(self, button)
|
|
if self and self.iconFrame then
|
|
local script = self.iconFrame:GetScript("OnClick")
|
|
if script then
|
|
script(self.iconFrame, button)
|
|
end
|
|
end
|
|
end)
|
|
|
|
lineFrame:Hide();
|
|
|
|
|
|
--Should we keep these frames in the questIdFrames? Currently it is also a child of the icon.
|
|
--Maybe the unload of the parent should just unload the children.
|
|
--For safety we check this here too.
|
|
--if (QuestieMap.questIdFrames[lineFrame.iconFrame.data.Id] == nil) then
|
|
-- QuestieMap.questIdFrames[lineFrame.iconFrame.data.Id] = {}
|
|
--end
|
|
--tinsert(QuestieMap.questIdFrames[lineFrame.iconFrame.data.Id], lineFrame:GetName());
|
|
|
|
--Keep a total lineFrame count for names.
|
|
lineFrameCount = lineFrameCount + 1;
|
|
return lineFrame
|
|
end
|