When UpdateNearestTargets uses ZoneDB:GetAreaIdByUiMapId(946) to verify
player zone, it returns 668 (Northshire) instead of 3430 (Sunstrider).
UiMapIdOverrides[946] was set to 668 for Northshire, so 946 always
resolves to Northshire's areaId even on Sunstrider Isle.
Added direct cache entries for both 1241→3430 and 946→3430 in the
uiMapIdToAreaIdCache (the fast-path lookup table) so GetAreaIdByUiMapId
returns the correct Sunstrider Isle areaId regardless of which map ID
the game returns.
- zoneDB.lua: Add [946] = 3430 to UiMapIdOverrides so GetCurrentZoneId()
returns 3430 (Sunstrider Isle areaId) even when the game returns uiMapId 946
(ghost/loading map). Previously 946 had no override, causing zone lookups to
fall through and return 946 instead of the real zone, breaking arrow distance
calculation and target filtering.
- QuestieArrow.lua: UpdateNearestTargets uses QuestiePlayer:GetCurrentUiMapId()
(backed by C_Map.GetBestMapForUnit) for player position. When that returns an
invalid/ghost map (946/947/0), fall back to ZoneDB lookup via the actual
zoneId. This ensures the arrow gets real world coordinates regardless of
whether the world map is open or closed.
Also includes per-frame debug output when debugArrow profile is enabled.
- Tooltip.lua: Add type guard 'if type(objList) ~= table then break end'
before iterating learnedNpc[10] and learnedObj[10] in both m_/NPC and o_/object
paths. Prevents 'attempt to index field questData (a string value)' error
when the questData field is unexpectedly a string instead of a table.
The original loop used 'for questId, objList in next, learnedNpc[10]' which
iterates key-value pairs in insertion order. The _AddToArray helper stores
values as sequential array elements (tbl[key]=value via table.insert), but
the iteration was treating it as a questId->objList map. Fixed to use
ipairs-style iteration with a type check for robustness.
- HBD.lua: Cache GetPlayerWorldPosition/GetPlayerZonePosition at 50ms
intervals instead of hammering GetPlayerMapPosition() every frame.
Invalidate cache on PLAYER_ENTERING_WORLD and ZONE_CHANGED_* events.
Expected ~97% reduction in C API position calls (45,570 -> ~1,200 / 10min).
- zoneDB.lua: Replace O(n) linear scan in GetAreaIdByUiMapId with an
O(1) reverse lookup cache (uiMapIdToAreaIdCache) built at Initialize().
Cache is kept in sync by ApplyCustomZones and name-match fallback now
caches its result so subsequent calls are also O(1).
- Tooltip.lua: Throttle GameTooltip OnUpdate hook to 100ms intervals
(was firing every frame at 60-144 Hz). Added _tooltipLastText cache
to avoid redundant GetText() + CountTooltip() calls when nothing changed.
- QuestieArrow.lua: Hoist _HasMissingCompletedFlag, _GetCompleteIconType,
_CollectFinisherSpawns, and _CollectObjective out of _CollectQuestTargets
to module-level functions. These were re-created as closures on every
UpdateNearestTargets call (1 Hz). Shared per-cycle context is published
via _arrow_* module upvalues to avoid closure capture overhead.
- QuestieLearnerComms.lua: Four micro-optimizations:
(1) Reduce ProcessQueues ticker 0.2s -> 0.5s (still 7x faster than
minChatInterval of 3.5s).
(2) Cache hidden channel ID at init; lazy refresh on disconnect.
(3) Drop LibDeflate compress level 9 -> 1 (fraction of CPU cost).
(4) O(1) messageCacheCount counter replaces O(n) pairs() size scan.