Files
Questie-X/Modules/Map/QuestieMapUtils.lua
T

211 lines
8.0 KiB
Lua

---@type QuestieMap
local QuestieMap = QuestieLoader:ImportModule("QuestieMap");
---@class QuestieMapUtils
QuestieMap.utils = QuestieMap.utils or {}
---@type QuestieLib
local QuestieLib = QuestieLoader:ImportModule("QuestieLib");
local HBD = QuestieCompat.HBD or LibStub("HereBeDragonsQuestie-2.0")
local ZOOM_MODIFIER = 1;
-- All the speed we can get is worth it.
local tinsert = table.insert
local next = next
function QuestieMap.utils:SetDrawOrder(frame)
-- This is all fixes to always be on top of HandyNotes notes Let the frame level wars begin.
-- HandyNotes uses GetFrameLevel + 6, so we use +7
if frame.miniMapIcon then
local frameLevel = Minimap:GetFrameLevel() + 7
local frameStrata = Minimap:GetFrameStrata()
frame:SetParent(Minimap)
frame:SetFrameStrata(frameStrata)
frame:SetFrameLevel(frameLevel)
else
-- If Magnify-WotLK is loaded, parent to WorldMapButton instead of WorldMapFrame
local parent = WorldMapFrame
if IsAddOnLoaded and IsAddOnLoaded("Magnify-WotLK") and _G.WorldMapButton then
parent = WorldMapButton
end
local frameLevel = WorldMapFrame:GetFrameLevel() + 7
local frameStrata = WorldMapFrame:GetFrameStrata()
frame:SetParent(parent)
frame:SetFrameStrata(frameStrata)
frame:SetFrameLevel(frameLevel)
end
-- Draw layer is between -8 and 7, please leave some number above so we don't paint ourselves into a corner...
-- These are sorted by order of most common occurrence to reduce if checks; it's less readable but more performant with so many icons
if frame.data then
if frame.data.Icon == Questie.ICON_TYPE_AVAILABLE then
frame.texture:SetDrawLayer("OVERLAY", 5)
elseif frame.data.Icon == Questie.ICON_TYPE_REPEATABLE then
frame.texture:SetDrawLayer("OVERLAY", 4)
elseif frame.data.Icon == Questie.ICON_TYPE_EVENTQUEST then
frame.texture:SetDrawLayer("OVERLAY", 4)
elseif frame.data.Icon == Questie.ICON_TYPE_PVPQUEST then
frame.texture:SetDrawLayer("OVERLAY", 4)
elseif frame.data.Icon == Questie.ICON_TYPE_COMPLETE then
frame.texture:SetDrawLayer("OVERLAY", 6)
elseif frame.data.Icon == Questie.ICON_TYPE_REPEATABLE_COMPLETE then
frame.texture:SetDrawLayer("OVERLAY", 6)
elseif frame.data.Icon == Questie.ICON_TYPE_EVENTQUEST_COMPLETE then
frame.texture:SetDrawLayer("OVERLAY", 6)
elseif frame.data.Icon == Questie.ICON_TYPE_PVPQUEST_COMPLETE then
frame.texture:SetDrawLayer("OVERLAY", 6)
elseif frame.data.Icon == Questie.ICON_TYPE_SODRUNE then
frame.texture:SetDrawLayer("OVERLAY", 6)
else
frame.texture:SetDrawLayer("OVERLAY", 0)
end
else
frame.texture:SetDrawLayer("OVERLAY", 0)
end
end
---@param points table<number, Point> @{{x=0, y=0}, ...}
---@return number x, number y @Center coordinates
function QuestieMap.utils.CenterPoint(points)
local x, y = 0, 0
local count = table.getn(points)
for i=1, count do
local point = points[i]
x = x + point.x
y = y + point.y
end
x = x / count
y = y / count
return x, y
end
---@param points table<number, Point> @A pointlist with {worldX=0, worldY=0, UiMapID=0, distance=0}
---@param rangeR number @Range of the hotzones.
---@param count number @Optional, used to allow more notes if far away from the quest giver.
---@return table<number, table<number, Point>> @A table of hotzones
function QuestieMap.utils:CalcHotzones(points, rangeR, count)
-- if(points == nil) then return nil; end
local hotzones = {}
local pointsCount = table.getn(points)
if pointsCount == 1 then
-- This is execution shortcut to skip loop in case table size == 1
hotzones = { { points[1] } }
return hotzones
end
--If count isn't set we want to distance clustering to still work,
--to simplify the logic we just use a big number.
if not count then
count = 99999;
end
local useMovingRange = (count > 100)
local range = rangeR or 100;
for j=1, pointsCount do
local point = points[j]
if not point.touched then
point.touched = true
local notes = { point }
--We want things further away to be clustered more
local movingRange = range
if useMovingRange and (point.distance > 1000) then
movingRange = movingRange * (point.distance/1000);
end
local aX, aY, aUiMapID = point.worldX, point.worldY, point.UiMapID
for i=j+1, pointsCount do
local point2 = points[i]
--We only want to cluster icons that are on the same map.
if (not point2.touched) and (aUiMapID == point2.UiMapID)
-- Do not cluster icons if they have no coordinates
and aX ~= 0 and aY ~= 0 and point2.worldX ~= 0 and point2.worldY ~= 0 then
local distance = QuestieLib:Euclid(aX, aY, point2.worldX, point2.worldY)
if (distance < movingRange) then
point2.touched = true
tinsert(notes, point2)
end
end
end
tinsert(hotzones, notes)
end
end
return hotzones
end
function QuestieMap.utils:IsExplored(uiMapId, x, y)
local IsExplored = false
if uiMapId then
local exploredAreaIDs =
C_MapExplorationInfo.GetExploredAreaIDsAtPosition(uiMapId,
CreateVector2D(
x / 100,
y / 100))
if exploredAreaIDs then
IsExplored = true -- Explored
elseif (uiMapId == 1453) then
IsExplored = true -- Stormwind
elseif (uiMapId == 1455) then
IsExplored = true -- Ironforge
elseif (uiMapId == 1457) then
IsExplored = true -- Darnassus
elseif (uiMapId == 1458) then
IsExplored = true -- Undercity
elseif (uiMapId == 1454) then
IsExplored = true -- Orgrimmar
elseif (uiMapId == 1456) then
IsExplored = true -- Thunder Bluff
end
end
return IsExplored
end
function QuestieMap.utils:MapExplorationUpdate()
for _, frameList in next, QuestieMap.questIdFrames do
for _, frameName in next, frameList do
local frame = _G[frameName]
if (frame and frame.x and frame.y and frame.UiMapID and frame.hidden) then
if (QuestieMap.utils:IsExplored(frame.UiMapID, frame.x, frame.y)) then
frame:FakeShow()
end
end
end
end
end
--- Rescale a single icon
---@param frameRef string|IconFrame @The global name/iconRef of the icon frame, e.g. "QuestieFrame1"
---@param mapScale number? @Scale value for the final size of the Icon
function QuestieMap.utils:RescaleIcon(frameRef, mapScale)
local frame = frameRef;
local iconScale = mapScale or 1
if type(frameRef) == "string" then
frame = _G[frameRef];
end
if frame and frame.data then
if frame.data.GetIconScale then
frame.data.IconScale = frame.data:GetIconScale();
local scale
if frame.miniMapIcon then
scale = 16 * (frame.data.IconScale or 1) * (Questie.db.profile.globalMiniMapScale or 0.7);
else
--? If you ever chanage this logic, make sure you change the logic in QuestieMap:ProcessQueue() too!
scale = (16 * (frame.data.IconScale or 1) * (Questie.db.profile.globalScale or 0.7)) * iconScale;
end
if scale > 1 then
frame:SetSize(scale * ZOOM_MODIFIER, scale * ZOOM_MODIFIER);
end
else
Questie:Error("A frame is lacking the GetIconScale function for resizing!", frame.data.Id);
end
end
end