- 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.
- Use LibStub(..., true) silent flag on all LSM acquisition sites
- Guard HashTable() calls in Options files (returns nil if no font type registered)
- Guard all Fetch() call sites with inline 'LSM30 and LSM30:Fetch() or fallback'
- Make font values lazy functions in options dropdowns so LSM has time to register
- Affects: QuestieOptionsTracker, QuestieOptionsArrow, QuestieArrow, QuestieTracker,
TrackerHeaderFrame, TrackerLinePool, TrackerQuestTimers
The previous patch hoisted _CollectFinisherSpawns and _CollectObjective to
module level but failed to remove the original inner function bodies from
_CollectQuestTargets, leaving 100+ lines of orphaned code and a phantom
bare 'end' that caused '<eof> expected near end' on load.
_CollectQuestTargets is now clean: calls the hoisted module-level functions
directly, no inner local function declarations.
- 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.
When QuestLogCache parses incomplete tracker data upon reload,
it leaves quest.isComplete undefined or false. This allows the
Arrow to process SpecialObjectives (like required source item drops)
which do not have Completed/Needed/Collected stats on them, bypassing
our recent arrow logic fix.
Added an explicit check for QuestieDB.IsComplete(quest.Id) == 1
at the very top of the track loop to guarantee the Arrow immediately
stops processing objectives and exclusively targets the finisher.
QuestieArrow._CollectObjective was only checking objective.Completed
to determine if it should skip an objective. In WotLK, the server often
doesn't send the completed flag until turn-in time.
This caused the arrow to still pick up the kill/interaction spawns (like
the 5/5 Goblin Prisoners) because their Completed flag hadn't been
set, overpowering the finisher spawn.
Added a fallback check: if objective.Collected >= objective.Needed,
skip the objective.