- Refactor button positioning and visibility into _PositionMapButton and
_RefreshMapButtonVisibility helpers. Single source of truth for both.
- WorldMapButton.Initialize now hooks OnShow/OnHide on WorldMapFrame so
the button self-updates when the user opens/closes the world map.
No more manual show/hide calls required from elsewhere in the code.
- Toggle(shouldShow) now persists to Questie.db.profile.mapShowHideEnabled
and calls _RefreshMapButtonVisibility, so the user's choice survives
/reload and is consistent with the world map's current visibility.
- Special-case Mapster offset: -50, -72.3 vs default -50, -40. Without
this, the button overlapped Mapster's UI elements when both addons
were loaded.
- Frame level set to 99 to ensure the button draws above other map
elements.
All helpers are no-op-safe when mapButton is nil. IsAddOnLoaded
('Mapster') is optional — falls through to default offset if Mapster
is not loaded.
- Add local _CountUniqueSpawnPositions helper (counts unique {x, y}
across all zone buckets in a spawn table).
- Gate the prioritizeMyData check on ld.settings.enabled, matching the
Database/QuestieDB.lua fix — when the learner is fully disabled,
the override lookup is skipped entirely.
- Replace the NPC-15274-specific debug print in the monster() spawn
selector with a universal reliability check: the learner data is
only used when CountUniqueSpawnPositions(learnedSpawns) > 1.
This is the consumer-side enforcement of the QuestieLearner cleanup
(commit e71e072). Singleton-position spawn entries are typically the
player's current position captured during quest dialog (accept/turn-in)
rather than real kill evidence. By requiring at least 2 unique
positions, we avoid letting that pollution clobber AscensionDB curated
spawn data.
The NPC-15274-specific debug print was removed — the universal check
replaces it, and the diagnostic it provided is now expressed through
the Questie DEBUG_DEVELOP log line (which still emits when the new
condition is met).
In _CalculateAvailableQuests, when a quest is found in the quest log
(IsComplete ~= -1), explicitly unload any available-quest pin frame
for that questId, remove its tooltip, and clear the cached entry
before returning.
Previously the early-return left stale state behind: the pin frame
and tooltip would persist until the next sweep, causing a brief window
where the same quest was visible as both 'available' (yellow !) on
the map and 'in log' (yellow ?) in the quest log.
The cleanup order is correct: unload frame, then remove tooltip,
then clear cache. narrow scope (only 'available' data type) — does
not affect other pin categories.
Adds a print() block in QuestieMap.utils:SetDrawOrder, gated by
_G.QuestieDebugPins. Dumps frame name, parent (QuestieFrameGroup or
Minimap), frame level, strata, shown/hidden state, and anchor point.
Used to diagnose why minimap pins appear on the wrong layer (behind
border or terrain texture) — the trace fires before SetParent /
SetFrameStrata / SetFrameLevel so the post-call result can be compared
to the pre-call printed values.
Default state: GATE IS OFF. Zero runtime cost in production. All API
calls defensive with tostring() and parent.GetName fallbacks.
Adds a print() block in _Qframe:FakeHide and _Qframe:FakeShow, gated by
_G.QuestieDebugPins. Dumps frame name, shown/hidden state, parent, and
anchor point. Used to diagnose why minimap pins reappear or fail to hide
after zone transitions or anchor re-registration.
Default state: GATE IS OFF. The block is a no-op unless the user
explicitly sets _G.QuestieDebugPins = true. Zero runtime cost in
production. All API calls defensive with tostring() and parent.GetName
fallbacks.
Real bug fixes (no behavior additions that risk regressions):
- LearnNPC: require both spawnX/spawnY AND x/y before accepting a spawn
entry. Previously quest-giver/finisher dialog polluted spawn lists
with the player's current position.
- _MergeSpawnEvidence: respect IsAscensionProtected ownership gate
unconditionally. The 'test mode' comment-out is removed; AscensionDB
curated spawn data is now protected from learner overwrite (fixes
the Sunstrider Mana Wyrm pin regression).
- _PruneSpawnOutliers and PruneLearnedSpawnOutliers: fix broken
next() iteration. The old code used 'coord = next(spawnTable); while
coord do ... coord = next(spawnTable, coord)' which treated the
KEY as the value. For numeric spawn arrays this returned indices
(1, 2, ...) but the code did coord[1] and coord[2] on those indices,
which are nil. The outlier pruning was effectively dead code. Now
iterates with 'coordKey, coord = next(spawnTable)' correctly, sorts
toRemove keys descending for safe table.remove, and compacts numeric
arrays.
- InjectLearnedData: early-exit when learner disabled, strip [7] on
initial npcDataOverrides creation (only _MergeSpawnEvidence should
populate spawns), versioned cleanup that retroactively clears
singleton-position spawn entries (≤1 unique position) from pollution
caused by the prior LearnNPC bug.
Supporting utilities added: CopyWithoutField, CountUniqueSpawnPositions,
NormalizeCoordPair reuse (replaces inline floor(x*100+0.5)/100).
Previously DrawWorldIcon and DrawManualIcon both set floatOnEdge=false
for uiMapId 1241, forcing icons to always show regardless of distance.
This caused pins to render at minimap center when near a spawn, making
them appear to orbit/follow the player as they rotated.
The zone->world coordinate pipeline was verified correct: zoneW matches
dataW exactly for 1241 icons, and playerW is in the same coordinate
space. The problem was purely the floatOnEdge=false behavior combined
with the icon being essentially at the minimap center (1% of radius).
Enabling floatOnEdge=true lets the standard edge-floating path handle
1241 pins like every other zone: icons clamp to the edge when far,
slide along the rim when approaching, and occupy their correct angular
bearing from the player rather than sitting at dead center.
Error: attempt to index field '?' (a number value) at line 1932.
waypoints[1] was a bare number instead of a coordinate pair {x,y}.
Added type(waypoints[1]) ~= 'table' guard alongside existing type(waypoints)
check. Also simplified firstWaypoint access since the guard ensures it's a table.
The underlying data issue (flat numbers vs coordinate pairs) is in the DB
and should be tracked separately — this fix prevents the crash only.
- Fix: isSunstrider block in _MergeSpawnEvidence now checks IsAscensionProtected
before writing learner data to npcDataOverrides. Without this guard, each Mana
Wyrm kill overwrote AscensionDB's z1241=5 data with learner z3431 coords.
- Fix: Clustering disabled for zone 1241 (Sunstrider Isle) in _DrawObjectiveIcons
so all 5 AscensionDB spawn pins display individually instead of collapsing to 2.
- Fix: Ghost pin loop in AvailableQuests.lua -- 'while frames[i]' was iterating a
string-keyed table with a numeric index (never iterated). Changed to pairs().
- Fix: NormalizeSpawnZoneKey now uses ZoneDB.areaIdToUiMapId for all zones so
learner evidence is stored under map IDs (e.g. 1241) not area IDs (e.g. 3431),
matching AscensionDB's key space. Applied in LearnNPC and _StoreGuidSpawnEvidence.
- Fix: isSunstrider detection in _MergeSpawnEvidence updated from hardcoded
zoneId==3431 check to IsSunstriderNativeZone() since zone IDs are now normalized
to map IDs at storage time.
- PrintTargetCoords: add world coords computed from zone coord + uiMapId
- _CollectObjective debug: show spawnList[id] zone, firstCoord, isLearned per entry
- _ResolveArrowUiMapId: stop redirecting 1241→1941. With zoneDB now
mapping areaId 3431→uiMapId 1241, and player on uiMapId 1241, keeping
1241 as 1241 ensures both player and target world coords are computed
through the same ASCENSION_ZONE_BOUNDS[1241] calibration (1600x1066.67,
-2721, 8433.94). This gives correct ~169yd distance and bearing.
- zoneDB: remap areaId 3431 → uiMapId 1241 (Sunstrider Isle) so quest 8335
pins render on correct map instead of Eversong Woods (1941)
- compat/HBD: remove 1241→1941 redirect (Sunstrider pins now handled by zoneDB)
- compiler: QuerySingle override check skips empty tables (next() guard) so
spawn data overrides don't fall through to nil base DB values
- MapIconTooltip: nil guards for minLevel/maxLevel to prevent crashes
- Map/QuestieMap: remove stale 1241→1941 redirect comment
AlreadySpawned kept stale entries after UnloadQuestFrames ran.
The draw path for incomplete objectives never cleared it — only
completed objectives called _UnloadAlreadySpawnedIcons. Added
clear at the start of the spawnList draw path so _DetermineIconsToDraw
sees an empty AlreadySpawned and recreates icons each update cycle.
The monster handler replaced spawns with learned data when
prioritizeMyData was enabled but didn't set isLearned=true on
monsterData. The prioritizeMyData filter in PopulateObjective
checked 'if not spawnData.isLearned' and suppressed the spawn,
causing pins to not render after quest abandon+re-accept cycle.
QuestieDB:GetNPC and GetObject cache results in _QuestieDB.npcCache /
_QuestieDB.objectCache. When LearnNPC / LearnObject injects new spawn
coordinates into npcDataOverrides / objectDataOverrides, the cached
objects still hold stale (empty) spawn data. PopulateObjective rebuilds
spawnList by calling GetNPC/GetObject, but gets the cached empty data.
Fix: clear the relevant cache entry immediately after live injection so
the next GetNPC/GetObject call rebuilds with fresh override data.
This is the root cause of 'pins not showing on kill' — the spawnList
was rebuilt, but from a stale cache that didn't include the new learned
coordinates.
1. _InvalidateSpawnListsForNPC was setting objective.AlreadySpawned = nil,
causing _DetermineIconsToDraw to crash on nil-index when trying to draw
updated pins. The xpcall wrapper in UpdateObjectiveNotes swallowed the
error silently, so no icons appeared.
Fix: unload existing map/minimap icons explicitly, then set
AlreadySpawned = {} (empty table) instead of nil. Also clear
hasRegisteredTooltips so tooltips refresh.
2. Added GetAchievementNumCriteria defensive shim for Ascension servers
with incomplete achievement databases. Returns 0 for invalid IDs
instead of hard-erroring in Blizzard's WorldMapFrame.
Two fixes for QuestieLearner real-time pin rendering:
1. _MergeOverride now skips empty tables (IsEmptyTable guard).
QuestieLearner stores spawns={} before any coords are captured.
Previously _MergeOverride treated {} as valid data and replaced the
base DB's real spawn coordinates with an empty table, breaking pins
for all NPCs that had been seen but not killed (mouseovers, etc.).
2. _InvalidateSpawnListsForNPC triggers after LearnNPC adds new spawn data.
The quest objective system caches spawnList once per objective.
When a kill adds new coordinates, the cached list is stale.
The new helper:
- Scans all active quest objectives for references to the NPC
- Clears objective.spawnList and objective.AlreadySpawned
- Calls QuestieQuest:UpdateQuest to rebuild pins with fresh data
This makes learned pins appear in real-time without /reload.
Root cause: creatureLevels entry for 'Uneasy Citizen' was {} (empty table)
instead of {minLevel, maxLevel, rank}, so creatureLevels[name][1] was nil.
Added early-return guard in _GetLevelString: if creatureLevels[name] is
falsy or has no numeric level at index [1], return name unchanged.
Also updated CHANGELOG.md [Unreleased] section with this fix.
- 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.
Demote GetQuest/GetQuestObjectives 'quest doesn't exist in QuestLogCache'
messages from Questie:Error to Questie:Debug(DEBUG_DEVELOP). These were
firing repeatedly in chat for quest IDs like 595, 959, 254048 during normal
play. Messages are now only visible when developer debug mode is active.
- SetObjectivesDirty: Add missing loop to reset isUpdated on SpecialObjectives
- PopulateObjective: Add completion guard for objectives without Update fn
- Bump version to 1.6.1 across TOC and changelogs
Previous guard only checked LSM30 ~= nil; if Fetch method is absent on
the stub object the call still crashes. Now uses full 3-part check:
(LSM30 and LSM30.Fetch and LSM30:Fetch(...)) or fallback
Matches the pattern already used in QuestieArrow.lua.
- 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