Files
Questie-X/docs/sunstrider-coordinate-collection.md
T
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

8.9 KiB

Sunstrider Isle Pin Fix — Coordinate Collection Guide

Architecture

On Ascension, Sunstrider Isle (uiMapId 1241) shares Eversong Woods' (1941) coordinate space — it's a child map within Eversong. Pins for zone 3430 (Eversong Woods) render on the Eversong map (uiMapId 1941) and appear on the Sunstrider sub-map (1241) via ZONE_REDIRECT visibility in HBD.lua.

Key mappings

  • areaId 3430 (Eversong Woods) → uiMapId 1941 (Eversong map)
  • uiMapId 1241 (Sunstrider Isle) → areaId 3430 → pins redirected to 1941 via _ResolveMapUiMapId
  • ZONE_REDIRECT: 1241→1941, 946→1941 (cross-visibility)
  • HBD bounds: mapData[1241] uses Eversong's calibrated bounds for player position tracking
  • QuestieLearner: GetZoneId() returns areaId 3430; HighConfidity set to 1 kill

Files modified

  • Database/Zones/zoneDB.lua — areaIdToUiMapId[3430] = 1941 (was 1241)
  • Modules/Map/QuestieMap.lua — _ResolveMapUiMapId redirects 1241→1941
  • Modules/Arrow/QuestieArrow.lua — _ResolveArrowUiMapId redirects 1241/946→1941
  • Modules/QuestieLearner.lua — GetZoneId() returns areaId via ZoneDB; InjectLearnedData migrates uiMapId keys; MIN_CONFIDENCE_PINS = 1
  • Compat/HBD.lua — ZONE_REDIRECT[1241]=1941, ASCENSION_ZONE_BOUNDS for mapData[1241]

Diagnostic /run Commands

These must be run in-game after Questie has fully loaded (wait 5+ seconds after login). If output is empty, the DB may not be initialized yet.

Check ZoneDB mappings

/run print("3430→uiMapId:", QuestieLoader:ImportModule("ZoneDB"):GetUiMapIdByAreaId(3430), "  1241→areaId:", QuestieLoader:ImportModule("ZoneDB"):GetAreaIdByUiMapId(1241))

Expected: 3430→uiMapId: 1941 1241→areaId: 3430

Check known NPC spawns for Sunstrider (zone 3430)

/run local ZoneDB=QuestieLoader:ImportModule("ZoneDB"); local ids={15271,15273,15274,15278,15279,15280,15281,15283,15284,15285,15287,15289,15291,15292,15294,15295,15297,15298,15301,15366,15367,15371,15372}; for _,id in ipairs(ids) do local n=QuestieDB:GetNPC(id); if n and n.spawns then for z,c in pairs(n.spawns) do if z==3430 or z=="3430" then for i,pt in ipairs(c) do print(id..":"..(n.name or "?").." zone="..z.." ["..i.."]="..string.format("%.2f,%.2f",pt[1],pt[2])) end end end end end

Check QuestieLearner overrides for zone 3430

/run local ov=QuestieDB and QuestieDB.npcDataOverrides; if ov then for id,d in pairs(ov) do if d[7] then for z,c in pairs(d[7]) do if z==3430 or z=="3430" then for i,pt in ipairs(c) do print("override npc="..id.." zone="..z.." ["..i.."]="..string.format("%.2f,%.2f",pt[1],pt[2])) end end end end end else print("npcDataOverrides not loaded") end

Check HBD ZONE_REDIRECT

/run local HBD=LibStub("HereBeDragonsQuestie-2.0"); print("ResolveZone(1241)=", HBD.ResolveZone and HBD.ResolveZone(1241) or "N/A", "ResolveZone(946)=", HBD.ResolveZone and HBD.ResolveZone(946) or "N/A")

Expected: ResolveZone(1241)= 1941 ResolveZone(946)= 1941

Check HBD bounds for map 1241

/run local HBD=LibStub("HereBeDragonsQuestie-2.0"); local d=HBD.mapData[1241]; if d then print("1241 bounds: left="..d.left.." right="..d.right.." top="..d.top.." bottom="..d.bottom.." parentMapID="..(d.parentMapID or "nil")) else print("No mapData for 1241") end

Expected: left=-2721.0066 right=-1120.9934 top=8433.9360 bottom=7367.2693 parentMapID=1941

Check LearnNPC zone tracking (run after killing a mob on Sunstrider)

/run local ld=Questie.dbLearner; if ld and ld.global and ld.global.npcs then local count=0; for id,d in pairs(ld.global.npcs) do if d[7] and (d[7][3430] or d[7]["3430"]) then count=count+1; print("learned npc="..id.." mc="..(d.mc or 0).." zone=3430") end end; if count==0 then print("No learned NPCs in zone 3430 yet") end else print("Learner data not available") end

Verify pin rendering pipeline

/run local ZoneDB=QuestieLoader:ImportModule("ZoneDB"); local uiMapId=ZoneDB:GetUiMapIdByAreaId(3430); print("Zone 3430 → uiMapId "..tostring(uiMapId).." (expected 1941)"); local HBD=LibStub("HereBeDragonsQuestie-2.0"); local wx,wy=HBD:GetWorldCoordinatesFromZone(0.38,0.21,uiMapId); print("World coords for (38%,21%) on map "..uiMapId..": "..string.format("%.1f, %.1f",wx or 0,wy or 0))

Expected: uiMapId 1941, world coords around (-2156, 8209)

Testing checklist

  • Load addon on Ascension server
  • Create a Blood Elf character on Sunstrider Isle
  • Verify quest giver pins appear on both the Sunstrider minimap AND the Eversong world map
  • Verify clicking a quest giver pin shows quest info
  • Verify arrow (distance/direction) points correctly to quest targets
  • After visiting/killing NPCs, verify QuestieLearner creates pin overrides (mc≥1)
  • Verify pins do NOT appear in mountains or off-map
  • Verify Eversong Woods (zone 1941) quest givers NOT on Sunstrider show correctly on Eversong map

Adding Townsfolk / NPC Data

There are three ways to add NPC spawn data so that pins appear for Sunstrider Isle NPCs. Choose the one that matches your data source.

1. AscensionDB plugin (numeric-key array)

The AscensionDB companion addon ships NPC data as a numeric-key array. Each entry is keyed by NPC ID and uses numeric indices matching QuestieDB.npcKeys. The spawns field is index 7 (see npcKeys.spawns = 7) and is itself a dict keyed by areaId.

-- AscensionDB.npcData example for a Sunstrider NPC
A.npcData = {
    -- [npcId] = { [1]=name, [4]=minLevel, [5]=maxLevel, [6]=rank, [7]=spawns, ... }
    [15273] = {
        "Arcane Wraith",   -- [1] name
        nil, nil,           -- [2] minLevelHealth, [3] maxLevelHealth
        1, 2,               -- [4] minLevel, [5] maxLevel
        0,                  -- [6] rank
        {                   -- [7] spawns  ← keyed by areaId, NOT uiMapId
            [3430] = {      -- 3430 = Eversong Woods areaId
                {38.4, 21.6},
                {39.2, 20.8},
                {40.0, 22.4},
            },
        },
    },
}

This data is loaded by QuestieDB:LoadAscensionNpcData() which calls _Asc_MergeInto(QuestieDB.npcDataOverrides, data) — it writes each NPC entry directly into npcDataOverrides[npcId].

2. WotLKDB / TBC corrections (string-key dict)

The built-in correction files (wotlkNPCFixes.lua, tbcNPCFixes.lua, classicNPCFixes.lua) use the named-key format via the npcKeys constants. This is the format you should use for patches submitted to Questie-X itself.

-- In Database/Corrections/wotlkNPCFixes.lua or tbcNPCFixes.lua
local npcKeys = QuestieDB.npcKeys

return {
    -- [npcId] = { [npcKeys.field] = value, ... }
    [15273] = {
        [npcKeys.spawns] = {
            [3430] = {          -- areaId 3430 (Eversong Woods), NOT uiMapId 1241
                {38.4, 21.6},
                {39.2, 20.8},
                {40.0, 22.4},
            },
        },
    },
}

These corrections are merged into QuestieDB.npcDataOverrides by the correction loader before overrides are applied.

3. QuestieLearner runtime (automatic)

QuestieLearner learns NPC positions automatically as you play. When you kill or interact with an NPC on Sunstrider Isle, LearnNPC stores the spawn under areaId 3430 (the return value of GetZoneId(), which uses ZoneDB:GetAreaIdByUiMapId(1241) → 3430). Learned data is written to Questie.dbLearner.global.npcs[npcId] as a numeric-key array (identical structure to AscensionDB) and injected into npcDataOverrides once the confidence threshold (mc >= MIN_CONFIDENCE_PINS) is met.

CRITICAL RULE: spawns are keyed by areaId, NOT uiMapId

This bears repeating because it is the #1 source of Sunstrider bugs:

  • CORRECT: [3430] = { {38.4, 21.6}, ... } — areaId for Eversong Woods
  • WRONG: [1241] = { {38.4, 21.6}, ... } — uiMapId for Sunstrider Isle

Questie's internal spawn tables use areaId as the key. The ZoneDB redirect (uiMapId 1241 → areaId 3430) ensures that even when the player is on the Sunstrider sub-map, the correct areaId is used. If you accidentally key spawns by uiMapId (1241), they will never be found by GetNPC and no pins will render.

_MergeOverride fix (historical note)

Prior to the _MergeOverride helper (added as part of the Sunstrider pin fix), the GetNPC function only checked string-keyed override entries (override["spawns"]). AscensionDB and QuestieLearner store overrides with numeric keys (override[7]), so their spawn data was silently ignored. The _MergeOverride function now checks both formats:

-- _MergeOverride checks both override formats:
--   1. override[stringKey]   (string-keyed, e.g. from wotlkNPCFixes)
--   2. override[intKey]      (numeric-keyed, e.g. from QuestieLearner / AscensionDB)
--   3. rawdata[intKey]        (fallback to compiled DB)

This means override data from all three sources is now visible to GetNPC for the first time. If you are debugging and overrides seem ignored, confirm _MergeOverride is being called (line 1923 in QuestieDB.lua as of this writing).