Files
Questie-X/Modules/QuestieCoordinates.lua
Xurkon bf6ecaedbe v1.6.3-unreleased: Sunstrider zone fixes, QuestieLearner icon preservation, migration, and Busted test suite
Code changes (13 files, +916/-171):
- ZoneDB integration: GetZoneId() converts uiMapId→areaId via ZoneDB reverse lookup
- MIN_CONFIDENCE_PINS reduced to 1 for Ascension (incomplete NPC DBs)
- QuestieLearner icon preservation: objective Icon passed to RegisterObjectiveTooltip
- InjectLearnedData zone migration: converts old uiMapId spawn keys to areaId
- areaId passed through all LearnNPC call sites (OnMouseoverUnit, OnQuestDetail,
  OnQuestComplete, OnQuestAccepted, OnQuestTurnedIn, OnGossipShow)
- Compat/Compat.lua: C_Map.GetPlayerMapPosition UiMapData support
- Compat/HBD.lua: Sunstrider mapData aliases and fallback loading
- Sunstrider arrow fix (QuestieArrow.lua), resolved pin rendering (QuestieMap.lua)
- _MergeOverride helper for string/numeric key compatibility
- Northshire Valley UiMapData registration

Testing infrastructure:
- .busted config pointing to tests/ directory
- Tests/wow_api_mock.lua: WoW API mocks (ZoneDB, C_Map, QuestLogCache, etc.)
- Tests/QuestieLearner_spec.lua: 12 tests covering coordinate scaling, spell cast
  learning, zone migration (NPC/objects), icon preservation, settings defaults,
  and LearnNPC spawn zone tracking
- selene.toml + wow_classic.yml: linter configuration

Documentation:
- Makefile with test/lint/ci targets
- sunstrider-coordinate-collection.md: coordinate data reference
- sunstrider-pin-fix.md: root cause analysis and fix documentation
2026-05-16 07:32:47 -05:00

184 lines
6.2 KiB
Lua

---@class QuestieCoords
local QuestieCoords = QuestieLoader:CreateModule("QuestieCoords");
---@type l10n
local l10n = QuestieLoader:ImportModule("l10n")
--- COMPATIBILITY ---
local C_Timer = QuestieCompat.C_Timer
local C_Map = QuestieCompat.C_Map
local WorldMapFrame = QuestieCompat.WorldMapFrame
local posX = 0;
local posY = 0;
QuestieCoords.updateInterval = QuestieCompat.Is335 and 0.1 or 0.5;
-- Placing the functions locally to save time when spamming the updateInterval
local GetBestMapForUnit = C_Map.GetBestMapForUnit;
local GetPlayerMapPosition = C_Map.GetPlayerMapPosition;
local GetCursorPosition = GetCursorPosition;
local GetMinimapZoneText = GetMinimapZoneText;
local IsInInstance = IsInInstance;
local format = format;
local function GetMapTitleText()
if QuestieCompat.Is335 then return WorldMapFrameTitle end
local regions = {WorldMapFrame.BorderFrame:GetRegions()}
for i = 1, #regions do
if (regions[i].SetText) then
return regions[i]
end
end
end
local function GetMiniWorldMapTitleText()
if QuestieCompat.Is335 then return end
local regions = {WorldMapFrame.MiniBorderFrame:GetRegions()}
for i = 1, #regions do
if regions[i].SetText then
return regions[i]
end
end
end
function QuestieCoords:WriteCoords()
if not ((Questie.db.profile.mapCoordinatesEnabled and WorldMapFrame:IsVisible()) or (Questie.db.profile.minimapCoordinatesEnabled and Minimap:IsVisible())) then
return -- no need to write coords
end
local isInInstance, instanceType = IsInInstance()
if isInInstance and "pvp" ~= instanceType then
return -- dont write coords in raids
end
local position = QuestieCoords.GetPlayerMapPosition()
if (not position) then
return
end
if position.x ~= 0 and position.y ~= 0 and (position.x ~= QuestieCoords._lastX or position.y ~= QuestieCoords._lastY) then
QuestieCoords._lastX = position.x
QuestieCoords._lastY = position.y
posX = position.x * 100;
posY = position.y * 100;
-- if minimap
if Questie.db.profile.minimapCoordinatesEnabled and Minimap:IsVisible() then
MinimapZoneText:SetText(format("(%d, %d) ", posX, posY) .. GetMinimapZoneText());
end
end
-- if main map
local mapTitleText = GetMapTitleText()
if Questie.db.profile.mapCoordinatesEnabled and WorldMapFrame:IsVisible() and mapTitleText then
-- get cursor position
local curX, curY = GetCursorPosition();
local canvas = WorldMapFrame:GetCanvas()
local scale = canvas:GetEffectiveScale();
curX = curX / scale;
curY = curY / scale;
local width = canvas:GetWidth();
local height = canvas:GetHeight();
local left = canvas:GetLeft();
local top = canvas:GetTop();
curX = (curX - left) / width * 100;
curY = (top - curY) / height * 100;
local precision = "%.".. Questie.db.profile.mapCoordinatePrecision .."f";
if QuestieCompat.Is335 and (not canvas:IsMouseOver()) or (position.uiMapID == 946)then
curX, curY = 0, 0
end
local worldmapCoordsText = "Cursor: "..format(precision.. " X, ".. precision .." Y ", curX, curY);
worldmapCoordsText = worldmapCoordsText.."| Player: "..format(precision.. " X , ".. precision .." Y", posX, posY);
-- Add text to world map
mapTitleText:SetText(worldmapCoordsText)
-- Adding text to mini world map
local miniWorldMapTitleText = GetMiniWorldMapTitleText()
if miniWorldMapTitleText then
miniWorldMapTitleText:SetText(worldmapCoordsText)
end
end
end
---@return table<{x: number, y: number}>, number | nil
function QuestieCoords.GetPlayerMapPosition()
-- If the world map is open, use the map currently being displayed instead of
-- GetBestMapForUnit("player"). On legacy clients our compat shim may call
-- SetMapToCurrentZone()/SetMapByID() while resolving the player's map, which
-- fights the open world map and causes the title/coordinate text to flicker.
if WorldMapFrame and WorldMapFrame:IsVisible() then
local currentMapId = WorldMapFrame:GetMapID()
if currentMapId and GetPlayerMapPosition then
local pos = GetPlayerMapPosition(currentMapId, "player")
if pos and pos.x and pos.y then
pos.uiMapID = currentMapId
return pos, currentMapId
end
end
end
local mapID = GetBestMapForUnit("player")
if (not mapID) then
return nil, nil
end
return GetPlayerMapPosition(mapID, "player"), mapID
end
function QuestieCoords:Initialize()
-- Do not fight with Coordinates addon
if IsAddOnLoaded("Coordinates") and ((Questie.db.profile.minimapCoordinatesEnabled) or (Questie.db.profile.mapCoordinatesEnabled)) then
Questie:Print("|cFFFF0000", l10n("WARNING!"), "|r", l10n("Coordinates addon is enabled and will cause buggy behavior. Disabling global map and mini map coordinates. These can be re-enabled in settings"))
Questie.db.profile.minimapCoordinatesEnabled = false
Questie.db.profile.mapCoordinatesEnabled = false
end
C_Timer.NewTicker(QuestieCoords.updateInterval, QuestieCoords.Update)
end
function QuestieCoords:Update()
if (Questie.db.profile.minimapCoordinatesEnabled) or (Questie.db.profile.mapCoordinatesEnabled) then
QuestieCoords.WriteCoords();
end
end
function QuestieCoords:ResetMinimapText()
MinimapZoneText:SetText(GetMinimapZoneText());
end
function QuestieCoords:ResetMapText()
local mapTitleText = GetMapTitleText()
if not mapTitleText then return end
local currentMapId = WorldMapFrame and WorldMapFrame.GetMapID and WorldMapFrame:GetMapID()
if currentMapId then
local info = C_Map.GetMapInfo(currentMapId)
if info and info.name then
mapTitleText:SetText(info.name)
return
end
end
mapTitleText:SetText(WORLD_MAP);
end
function QuestieCoords:ResetMiniWorldMapText()
local currentMapId = WorldMapFrame:GetMapID();
if currentMapId then
local info = C_Map.GetMapInfo(currentMapId);
if info then
GetMiniWorldMapTitleText():SetText(info.name);
end
end
end