Commit Graph

506 Commits

Author SHA1 Message Date
Xurkon e755416f8a fix(map): align minimap pins at non-1 UI scales 2026-06-11 06:49:51 -05:00
Xurkon d2c36a4a95 fix(learner): kills never recorded coords (credited used before assignment)
The kill handler's position capture ran inside 'if credited then', but
credited was read before its 'local credited = ...' assignment a few
lines below, so it was always nil and px,py were never captured -- every
killed NPC stayed spawnSource='fallback' with no [7] and drew no learner
pins regardless of kill count. Move the credited computation above the
capture and nil-guard a 0,0 position. GetPlayerCoords now uses the robust
GetCurrentPlayerPosition (Sunstrider-corrected), returning nil when
invalid so no 0,0 pins are recorded. Reverts the earlier HBD GetPlayerCoords
approach which treated the wrong symptom and caused spurious pins.
2026-06-10 21:46:18 -05:00
Xurkon 5e4ac2d64c fix(map): proactively hide filtered quest icons on the minimap (#11)
FadeLogic only re-checked ShouldBeHidden when deciding whether to
re-show an already-hidden icon, so a filtered quest (e.g. a dungeon
quest) that was already visible -- or that HBD's pin renderer showed on
coming into range -- was never hidden on the minimap while the world map
hid it. FadeLogic now proactively FakeHides any in-range icon whose
ShouldBeHidden is true, in both minimap fade paths; the second path also
gains the #17 minimap-radius cutoff gating it was missing.
2026-06-10 21:31:41 -05:00
Xurkon 0ebfb51ded fix(learner): capture spawn coords via HBD so learner pins persist
GetPlayerCoords used raw GetPlayerMapPosition('player'), which returns
0,0 when the world map isn't on the player's zone (the usual case) and
mis-reports on Ascension subzones like Sunstrider. Learn events captured
no position, so NPCs were saved spawnSource='fallback' with no [7]
spawns and their learner-only pins never persisted -- showing briefly
after a live kill then vanishing. Read from HBD:GetPlayerZonePosition()
(robust SetMapToCurrentZone/Sunstrider-corrected, cached), falling back
to the old API only if HBD is unavailable.
2026-06-10 21:04:01 -05:00
Xurkon 49bc586c15 fix(tooltip): make Ascension quest-line stripping opt-in (#16)
HideAscensionQuestLines ran on every tooltip and removed any line
matching a quest-objective pattern (N/M, [N] ...), clobbering other
tooltip addons' lines (durability, stack counts, etc.). Gate it behind a
new General-tab option 'Hide Ascension quest progress lines' (default
off) so Questie no longer modifies tooltip lines it does not own by
default. Existing installs read the unset option as off automatically.
2026-06-10 20:39:21 -05:00
Xurkon cee005fc63 fix(map): pixel-snap minimap icons to align with native blips at fractional scales (#6)
At fractional UI scales (e.g. 0.71 game scale + Windows display scaling)
the raw fractional pin offset landed icons between physical pixels,
rendering blurry and offset from the engine-drawn native quest blips.
Snap the offset to a whole physical pixel via the minimap's effective
scale so Questie's icons sit on the same grid as native markers.
2026-06-10 20:34:20 -05:00
Xurkon 6ad4308940 fix(map): robustly hide Callboard/Contract Board quests below 60 (#10)
Ascension board bounties (e.g. NPC 24 'Outlaw's Contract Board') aren't
reliably flagged repeatable, so IsRepeatable missed them and their ! kept
showing below 60. Add QuestieDB.IsBoardQuest (detects starter NPC/object
names containing 'board', cached per quest); the hide-below-60 option now
hides repeatable OR board quests in both the draw path and ShouldBeHidden.
2026-06-10 20:31:22 -05:00
Xurkon e852e02d32 fix(tooltip): make ElvUI tooltip style opt-in, reset for existing installs (#16)
The ElvUI tooltip style shipped on by default, so Questie restyled every
default WoW tooltip (stripping the border) for users who never asked for
it and don't run ElvUI. Default is now false, plus migration [8] resets
it off once for existing profiles.
2026-06-10 20:26:41 -05:00
Xurkon f9b19218cb fix(quest): snapshot completion at QUEST_REMOVED so turn-ins are not misclassified as abandoned (#9)
Some Ascension turn-ins (crafting/auto-complete) fire QUEST_REMOVED with
no preceding QUEST_TURNED_IN, so the 1s abandon timer runs after
QuestLogCache.RemoveQuest has cleared the quest, making IsComplete return
0 and the turned-in quest get marked abandoned -- its objective pins and
turn-in ? then linger. Capture IsComplete at QUEST_REMOVED time and use
that snapshot in MarkQuestAsAbandoned.
2026-06-10 19:27:56 -05:00
Xurkon 1c0a891efd fix(map): minimap range cutoff no longer clips visible icons (#17)
The cutoff hid icons beyond its yard value before checking the minimap's
visible radius, so the default 100 hid icons that were clearly on the
minimap (view radius is 133-466 yd by zoom). Now it only clips icons
outside the visible circle: HBD's pin renderer gates the cutoff on
dist > 1, and QuestieMap FadeLogic raises the effective cutoff to at
least the current minimap view radius via new HBDPins:GetMinimapRadius().
2026-06-10 19:24:39 -05:00
Xurkon ff08c38fa0 bump: version 1.6.4 with incomplete performance refactor caveat 2026-06-10 06:53:35 -05:00
Xurkon ebba10ed75 docs(changelog): add NPC purge fix and spawn-coordinate count entries 2026-06-10 06:51:44 -05:00
Xurkon 9d515987f6 feat(learner): report spawn coordinate count in import completion message
Count actual [7]/[4] coordinates before and after merge so the
completion message shows how many pins were really added (e.g. Added
2026-06-10 06:50:03 -05:00
Xurkon 611e61e9c9 fix(tooltip): stop hiding ID lines and other addons' tooltip text
HideAscensionQuestLines kept a questBlockActive flag and hid every
following non-indented line after an objective line until an indented
one. Questie's own Item/NPC/Object ID lines and other addons' additions
(e.g. an item-count overlay) are appended at the bottom of the tooltip,
so they were wiped too. Now only lines matching an Ascension objective
pattern are hidden; trailing lines are left intact.
2026-06-10 06:17:39 -05:00
Xurkon 9ea82fcdb5 docs(changelog): document zone export, live import refresh, 200k cap
Changelog entries for the import/export work landed in 2c695c4: Export
Current Zone, live map+stats refresh after import (no reload), and the
200k-entry import guard.
2026-06-10 05:46:33 -05:00
Xurkon 2c695c45fe feat(learner): zone-scoped export; 200k entry cap; SmoothReset after import
QuestieLearnerExport:
- Extract _Encode helper to consolidate serialize+deflate+encode
- Add ExportZone(serverKey, zoneId) to export only the learned data
  for a specific zone (NPCs/objects with spawns in that zone, plus
  their dropped items and related quests) for smaller, faster exports
- Cap ValidateImport at 200k entries to guard against pathological
  payloads that could stall the client during merge
- Call SmoothReset after MergeImport so newly imported spawns appear
  on the map without requiring /reload

QuestieOptionsDatabase:
- Refresh AceConfigRegistry after import so Database tab stats update
  immediately (previously required /reload or opening Options)
- Add Export Current Zone button tied to ExportZone()
2026-06-10 05:39:39 -05:00
Xurkon aa50404484 fix(learner): robust safety fallbacks for all learner types
Hardens the learner data-ingestion paths against malformed/partial data
across NPC/object/item/quest types:

- _ValidateLearnedSpawnData now validates object spawn coordinates (key 4)
  in addition to NPC spawns (key 7); object coords were previously
  unvalidated. Extracted a shared _ValidateCoordTable helper.
- _ApplyIncomingNetworkMerge coordinate merge skips malformed zone keys and
  coordinates (non-number / out-of-range) per entry instead of erroring,
  and guards the item drop-list merge against non-number NPC ids.
- The live comms-merge flush isolates each broadcast in a pcall so one
  malformed entry can't abort the batch or break the live-update loop;
  the post-merge InjectLearnedData is also guarded.

Live Learn* entry points already validate id/type. selene 0 errors;
busted 145 successes / same 7 pre-existing failures.
2026-06-10 05:16:51 -05:00
Xurkon 2c2e4a24a7 fix(learner): harden import/merge for clean multi-player data merge
Importing learned data merged from several players must not corrupt the
store. Fixes:

- ValidateImport prefix check was a no-op:  parsed
  as  (always false), so non-Questie strings were
  never rejected. Now a proper inequality check.
- MergeImport now ensures the learner stores exist (fresh profile can
  import), merges each entry synchronously via _ApplyIncomingNetworkMerge
  (validates structure, only adopts missing fields, never overwrites good
  local data), wraps each entry in pcall so one malformed entry is
  skipped/counted rather than aborting the whole import, and returns
  accurate merged/skipped/rejected counts.
- Synchronous merge also fixes InjectLearnedData previously running before
  the async-queued merges landed.

selene 0 errors; busted 145 successes / same 7 pre-existing failures.
2026-06-10 05:12:04 -05:00
Xurkon 1b06808655 feat(map): raise Minimap Icon Range Cutoff max to 1000
Players who run fully zoomed-out minimaps need a larger visibility radius
since quest objective spawns can be far apart. Raise the slider max from
200 to 1000 (yards). Default (100) and minimum (25) unchanged.
2026-06-10 05:07:30 -05:00
Xurkon dfdad2cbc3 fix(quest/learner): draw turn-in '?' for in-log quests; learn turn-in NPC
Reverts the previous approach of stripping char.complete (which mutated
completed-quest data). Correct rule: a quest in the player's log is active
and should show its turn-in location regardless of the completed flag.

- AddFinisher now trusts the live quest log: if the quest is in
  currentQuestlog and not failed, the '?' finisher draws regardless of the
  (possibly stale, e.g. prior-prestige) char.complete flag. No completion
  data is modified. Reverts the AcceptQuest and QUEST_QUERY_COMPLETE strips.

- Learner: OnTargetChanged now learns quest-giver/turn-in NPC spawns
  (it previously only cached the GUID), so targeting a turn-in NPC records
  its location. OnQuestComplete/OnQuestTurnedIn fall back to the 'target'
  unit when the 'npc' gossip unit is already cleared, so the finisher NPC
  is reliably learned on turn-in.

selene 0 errors; busted 145 successes / same 7 pre-existing failures.
2026-06-10 05:05:28 -05:00
Xurkon f9ff77c088 fix(quest): clear stale char.complete for re-accepted (prestige) quests
A quest completed in a prior Ascension prestige stays in char.complete.
On re-accept it is active in the log again, but the stale completion flag
was never cleared — and AddFinisher only draws the turn-in '?' when
not char.complete[questId]. So the finisher was suppressed and the quest
showed '(Complete)' with stale objective text while standing at the
turn-in NPC (e.g. Aggression 8334 at Lanthan Perilon 15281).

An active in-log quest is now kept out of char.complete:
- QuestieQuest:AcceptQuest clears char.complete[questId] on accept.
- The async QUEST_QUERY_COMPLETE handler also strips any quest currently
  in QuestiePlayer.currentQuestlog, so the server's completed list can't
  re-flag a re-accepted quest.

Restores the turn-in '?' and normal objective tracking. selene 0 errors;
busted 145 successes / same 7 pre-existing failures.
2026-06-10 04:58:44 -05:00
Xurkon ab560d5711 fix(map): completed/accepted/repeatable available-quest pins (#7 #9 #10)
#7: Completed quests kept showing as available '!'. The server completed-
quest list arrives async via QUEST_QUERY_COMPLETE, often after available
quests were first drawn. Recalculate available quests once char.complete is
populated by that event so completed quests are removed (also clears the
already-completed subset of #8).

#9: An accepted quest's available '!' could linger on the minimap until
/reload. UnloadQuestFramesByDataType unloaded the frame but left its name in
questIdFrames and _G; it now removes those references so the icon is torn
down immediately on both map and minimap.

#10: Added 'Hide repeatable quests below level 60' (Icons tab, off by
default) to hide repeatable available quests (e.g. the Ascension Callboard)
from map/minimap until level 60. Enforced in _DrawQuestIfAvailable and
ShouldBeHidden so existing minimap pins are removed too; reappear at 60.

Lint: selene 0 errors. Tests: 145 successes / same 7 pre-existing failures.
2026-06-09 23:18:38 -05:00
Xurkon 4827a3131e fix(tooltip): copy ElvUI's exact tooltip border (pixel-perfect, even)
The previous ElvUI-style border was too thick and uneven (thicker at the
top): it used Interface\ChatFrame\ChatFrameBackground with edgeSize = 1 UI
*unit*, which renders several physical pixels thick at the user's UI scale,
and a 1x1 texture that samples unevenly along edges.

Now copies ElvUI/Core/Toolkit.lua SetTemplate exactly: the blank texture
E.media.blankTex (Interface\Buttons\WHITE8X8) for bg + edge, and edgeSize =
E.mult (one physical pixel = (768 / screenHeight) / uiScale) so the border
is a true even 1px. Colors unchanged (ElvUI defaults the user runs):
backdropfadecolor {0.06,0.06,0.06} @ colorAlpha 0.8, bordercolor black.

Factored into QuestieTooltips:ApplyElvUISkin so the secondary learner frame
and the default tooltips share one implementation.
2026-06-09 22:06:46 -05:00
Xurkon 86e37857c0 fix(learner): don't purge NPCs with name/zone/kills/quests but no spawns
The InjectLearnedData purge loop was deleting entire NPC entries from
Questie.dbLearner.global.npcs whenever data[7] (spawns) was empty,
even if the entry still had a name, home zone, recorded kills, or
quest references. This caused learner data to silently disappear on
/reload for NPCs that had been learned but never killed in a position
the learner could record (e.g. party-kill position attribution fix
now passes nil coords for uncredited kills).

Now only purges when ALL of name, zone, kills, and quest references
are missing. Entries with other useful state get data[7] set to nil
instead so the row stays.
2026-06-09 22:02:39 -05:00
Xurkon fa37ab8eb5 feat(tooltip): ElvUI style without ElvUI; source in secondary only; fix learner source label
- ElvUI tooltip style: new 'ElvUI tooltip style' option (General tab, on by
  default) skins GameTooltip/WorldMapTooltip/ItemRefTooltip/shopping tooltips
  and the secondary learner frame with ElvUI's transparent flat look (dark bg
  + thin 1px border) when ElvUI is not installed. Corrected the secondary
  frame fallback that used the chunky WoW border. No-op when ElvUI is loaded.

- Source attribution now shows ONLY inside the secondary learner tooltip when
  'Use secondary learner tooltip' is enabled; removed from the main NPC/object/
  item tooltip and gated off entirely when the secondary tooltip is disabled
  (map-pin source gated the same way).

- Fixed learner-learned pins mislabelled 'AscensionDB': GetPinDataSource is now
  mode-aware and returns 'Learner' in learner mode when the entity has a learner
  record, even if AscensionDB also curates it (curated coords are discarded by
  GetNPC/GetObject in learner mode anyway).

Test-neutral (145 successes / same 7 pre-existing failures + 1 error).
2026-06-09 21:52:55 -05:00
Xurkon ee67653414 fix(learner): learner-only mode shows only learner-recorded spawns
In learner-only mode the map/minimap drew curated AscensionDB (and static)
spawns for every quest NPC/object the player had not personally recorded.
GetNPC/GetObject fall back to the npcDataOverrides/objectDataOverrides
entry for metadata when there is no learner record, and that entry's
spawns (AscensionDB-curated for un-recorded entities) were drawn as pins.

Both now track whether a real learner record exists and, in learner mode,
strip spawns when it does not -- so learner-only shows exclusively what the
learner recorded (metadata fallback for names/tooltips kept). GetObject
also now nils the override in learner mode to match GetNPC. Recorded
entities still show their learner spawns; auto/static/none unchanged.

Verified test-neutral on the full suite (144 successes / same 7 pre-existing
failures) with a local regression test that passes with the fix and fails
without it.
2026-06-09 21:25:02 -05:00
Xurkon 364b455b00 fix(map): apply quest-type filters to the minimap (#11)
Quest-type filters (Available Dungeon/Raid/PvP/Repeatable/Event quests and
other ShouldBeHidden rules) were honored on the world map but not the
minimap. At toggle time HideQuestIcons FakeHides both map and minimap
filtered icons, but the minimap icon's per-frame FadeLogic re-showed any
hidden icon once in range via 'elseif self.hidden then self:FakeShow()',
undoing the filter on the minimap only (the world icon has no FadeLogic).

FadeLogic now re-checks ShouldBeHidden before re-showing, in both the
world-icon and manual-icon minimap fade paths, so filters apply to both.
2026-06-09 21:03:36 -05:00
Xurkon 728066d067 fix(libs): degrade gracefully on over-long AceComm prefix (#12)
A third-party addon (e.g. AtlasLoot) registers an AceComm prefix longer
than the client's 16-character limit. Because Questie's bundled AceComm is
the LibStub winner and its xpcall polyfill wraps AceAddon's OnEnable, the
upstream hard error() in RegisterComm surfaced as a recurring Lua error at
every login (seen on ChromieCraft).

The prefix can never work on the client regardless, so RegisterComm now
warns once and skips the registration for over-long prefixes instead of
throwing, leaving the calling addon's OnEnable intact and removing the
error popup.
2026-06-09 20:48:07 -05:00
Xurkon 69a9a4a7a3 fix(tracker): guard VoiceOver questPlayButtons and AceGUI tree tooltip (#15)
Two crashes reported in #15 on Elune (1.6.3):

- TrackerUtils:IsVoiceOverLoaded only verified the VoiceOver addons were
  loaded, not that VoiceOver.QuestOverlayUI.questPlayButtons exists. Some
  VoiceOver builds expose a QuestOverlayUI without that table, so
  UpdateVoiceOverPlayButtons and SetAllPlayButtonAlpha crashed with
  'attempt to index field questPlayButtons (a nil value)'. Now verified in
  IsVoiceOverLoaded, which all play-button call sites gate on.

- AceGUIContainer-TreeGroup crashed indexing a nil AceGUI.tooltip when a
  conflicting addon registered a broken AceGUI-3.0 core (version 1.#INF)
  that won LibStub but never created the shared tooltip frame. The tree
  button handlers now lazily recreate it; widget version bumped 47->48 so
  the fixed widget wins registration.
2026-06-09 20:41:37 -05:00
Xurkon 08b266d7ba fix(tooltip): never add learner spawn data to the main tooltip
The 'Use secondary learner tooltip' option's off-path appended learner
spawn/kill lines directly to the main GameTooltip. The toggle now gates
the data entirely: on = shown in the separate secondary frame, off = not
shown anywhere. Learner spawn/kill data is never inline in the main NPC
tooltip under any setting.

Removed the now-unused _AddTooltipSeparator helper and updated the option
description.
2026-06-09 18:20:39 -05:00
Xurkon 0c4631be6e feat(tooltip): accurate per-pin data source attribution
Re-adds the 'Show data source' tooltip option (General tab, default off),
but accurate this time. The previous version was removed because it
guessed the source from the global data-source mode (showing Learner when
data was AscensionDB, etc.). This tags each pin with its real provenance
at creation and reads that tag.

- QuestieDB.GetPinDataSource(entityType, id, spawnData): resolves
  Learner (per-spawn isLearned or learner record), AscensionDB (curated
  ascensionOverrideKeys override), or base Questie DB. Never guesses.
- Objective pins tagged per-spawn in _DetermineIconsToDraw; available/
  finisher pins defaulted by quest in DrawWorldIcon; manual notes tagged
  Townsfolk in DrawManualIcon.
- World-map pins read the per-pin tag (MapIconTooltip); unit/object/item
  hovers derive per-id at the render layer (TooltipHandler via
  QuestieTooltips:GetDataSourceLine). Comms appended via KeyExists.
- No line shown when source is genuinely unknown (never misleading).
2026-06-09 17:57:58 -05:00
Xurkon d207feb3db fix(tooltip): skip objective lookup for quests not in the log
Hovering an NPC right after turning in one of its associated quests
spammed a debugstack trace in DEVELOP mode: the learner objective-
correlation block in GetTooltip called QuestLogCache.GetQuestObjectives
for a quest no longer in QuestLogCache (which gracefully returns {} but
logs the stack at line 306).

Guard the NPC and object correlation loops on
QuestiePlayer.currentQuestlog[questId] so live objective progress is only
looked up for quests the player is currently on. Not a crash and no
functional change for active quests.
2026-06-09 17:17:12 -05:00
Xurkon 5a9dce0f13 fix(tooltip): keep learner lines out of main NPC tooltip when using secondary
Two parallel learner-tooltip systems both rendered on unit hover, so with
the 'Use secondary learner tooltip' option on, learner spawn/kill lines
still leaked into the main NPC tooltip instead of going solely to the
secondary frame.

QuestieLearner's OnTooltipSetUnit hook (toggle-aware, secondary-capable)
already owns unit-hover learner display, routing it to the main tooltip
(secondary off) or the separate secondary frame (secondary on). But
Tooltip.lua's _GetLearnerTooltipLines also injected learner lines into the
m_<npcId> tooltip data, redundantly.

Add a suppressLearnerLines flag to QuestieTooltips:GetTooltip and pass it
from the NPC unit-hover call in TooltipHandler so the inline lines are
omitted there. Map-pin and object tooltips (no secondary frame) keep their
learner lines. Also removes the latent duplicate present even with the
secondary frame disabled.
2026-06-09 17:11:51 -05:00
Xurkon 9d5d4a947e feat(map): remove looted object node pins on loot
When a quest object node is opened/looted, that specific node's
map/minimap pin now disappears immediately even before the objective is
fully collected, instead of all node pins staying until turn-in.

On LOOT_OPENED, QuestieQuest:RemoveLootedObjectivePins finds the active
object-objective spawn nearest the player's world position (<=12 yd),
marks it consumed in Questie.db.char.lootedObjectSpawns, and repopulates
just that objective so clustering recomputes with the node skipped.
_DetermineIconsToDraw skips consumed object spawns, so removal persists
across redraws and /reload. ClearLootedSpawns runs on AcceptQuest, so
abandoning and re-doing the quest (including after an Ascension prestige)
shows every node again.

Works in every data source mode (auto/learner/static/none): suppression
is centralized in the shared _DetermineIconsToDraw that all redraw routes
funnel through, operating on the mode-resolved objective.spawnList.
Matching is radius-based (1.5 zone units) so a learner spawn re-added at
the player's position for the just-looted node is suppressed too.
Scoped to object-type objectives; monster/kill pins are unchanged.
2026-06-09 17:01:57 -05:00
Xurkon 8137d90ff6 fix(map): unload completed-objective pins deterministically
Completed objectives could leave their map/minimap pins on screen until
turn-in. Pin removal relied solely on _UnloadAlreadySpawnedIcons, which
walks objective.AlreadySpawned; that table desyncs from the live frames
after learner spawn-list invalidation or complete->abandon->reaccept
cycles, so when reset to {} the frames leaked.

Add QuestieMap:UnloadQuestFramesForObjective(questId, objectiveIndex)
which unloads frames off the map's own questIdFrames registry by matching
frame.data.ObjectiveIndex, and call it from every completion path in
PopulateObjective. Restricted to positive standard-objective indices;
SpecialObjectives (sentinel index 0) keep using AlreadySpawned.
2026-06-09 17:01:34 -05:00
Xurkon bd16c59681 fix(tooltip): move _GetLearnerTooltipLines before GetTooltip to fix nil call
Lua requires local functions to be defined before they are called.
_GetLearnerTooltipLines was defined after GetTooltip, causing
'attempt to call global _GetLearnerTooltipLines (a nil value)'.
Moved the function definition above GetTooltip.
2026-06-09 06:30:14 -05:00
Xurkon b0152b8a12 feat(tooltip): show learner spawn data in NPC tooltips
Adds learned spawn position (most-visited GUID), total distinct spawns
learned, and total kills recorded to both unit hover tooltips and
world-map pin tooltips. Replaces the removed Source: attribution line
with factual learner data that was actually recorded.
2026-06-09 02:48:38 -05:00
Xurkon 53dc151cc0 docs(changelog): add 2026-06-09 bug fix entries 2026-06-08 21:57:04 -05:00
Xurkon 1629352eb6 fix(learner): don't record other players' kill positions
On PARTY_KILL and unengaged kills, the learner was recording the local
player's position (GetCurrentPlayerPosition) as the spawn location.
This polluted the learner's spawn map with the wrong coords. Now only
records position for credited kills (your own kills or mobs you damaged
within the last 60 seconds).
2026-06-08 21:51:40 -05:00
Xurkon edbb64fb3d fix(learner): learner-only mode shows only learner data
Three-mode spawn-merge semantics:
- Learner: discard npcDataOverrides (which AscensionDB's _Asc_MergeInto
  populates with curated coords). GetNPC returns learnerRecord only.
  _ApplyNpcLiveUpdate and object-spawn injection use IsAscensionProtected
  (mode-dependent) so learner coords pass through in learner mode.
- Auto: static DB primary, learner fills gaps. Curated spawns protected.
- Static: base DB only.

Also removes the allowSpawnMerge bypass that was the original hole
letting learner coords leak into curated spawns in auto mode.
2026-06-08 21:48:02 -05:00
Xurkon f9a3301584 fix(tooltip): remove misleading data-source attribution line
The Source: line cannot accurately reflect per-pin provenance without
changing every spawn registration site. Until that infrastructure
exists, the label is misleading — showing Questie DB even when data
came from the learner, or Learner when it came from AscensionDB.
Remove from both MapIconTooltip (world-map pins) and Tooltip
(unit/object hover).
2026-06-08 21:40:10 -05:00
Xurkon 6a4f715b76 fix(learner): guard HasQuestReferences against flat-number entries on Area 52
Some Area 52 quest records store qData[2][3] as a flat number instead of
a table-of-tables. The inner loop's entry[1] index then crashes with
'attempt to index local entry (a number value)'. Normalize with a
type check before comparing.
2026-06-08 21:36:10 -05:00
Xurkon 011cdb58a9 fix(learner): defer to AscensionDB curated spawns for owned NPCs
Mana Wyrm 15274 (quest 8325) pins rendered in the NE corner because the
learner's Sunstrider spawns leaked into the curated AscensionDB coords.
Root causes:
- In learner mode GetNPC uses rawdata = learner record, and _MergeOverride
  deep-merges spawn tables, so the learner's bad zone (1445 @ 54,12)
  survived since AscensionDB had no entry to displace it.
- The injection guards used IsAscensionProtected(), which returns false in
  learner mode, so the learner overrode AscensionDB-owned spawns. Commit
  0f20ea8 had also added a (not learnerLiveMode) bypass to the
  _MergeSpawnEvidence Sunstrider guard, re-enabling the e80a008 regression.

Fixes:
- QuestieDB.GetNPC: for AscensionDB-owned NPC spawns (direct
  ascensionOverrideKeys check), use ONLY the curated override spawns and
  discard the merged learner zones.
- New mode-independent AscensionOwnsNpcSpawns() helper; used by the
  InjectLearnedData spawn restore and the _MergeSpawnEvidence guard
  (removed the (not learnerLiveMode) bypass).

Regression test asserts GetNPC returns only the curated 1241 coords and
drops the learner 1445 zone.
2026-06-08 17:53:16 -05:00
Xurkon a525813f12 fix(learner): restore learned spawns under native uiMapId, not areaId
Follow-up to da5546f: the previous restore read the POST-migration data[7]
keys and ran them through NormalizeSpawnZoneKey. On Sunstrider that round-
tripped uiMapId 1241 -> parent areaId 3430 -> Eversong map 1941, placing
learner pins in the wrong (NE) corner of the map.

Now snapshot each NPC's [7] spawns in their NATIVE uiMapId space BEFORE the
uiMapId->areaId migration runs, and restore from that snapshot with no key
conversion. This keys the override exactly like the live _MergeSpawnEvidence
kill path (e.g. 1241 for Sunstrider), so pins render on the correct map and
HBD isSameZoneSpace handles Eversong<->Sunstrider visibility. Still gated by
IsAscensionProtected. Regression test extended to assert spawns land on 1241
and NOT 1941/3430/3431.
2026-06-08 17:36:35 -05:00
Xurkon 6634074096 docs(learner): add regression-guard note at NPC spawn [7] strip
Document why stripping [7] in InjectLearnedData is only safe because the
guarded restore block below re-merges saved spawns (normalized to uiMapId).
Prevents re-introducing bug 7ce0cdc (learner quests losing pins on /reload
until the mob is re-killed).
2026-06-08 16:56:50 -05:00
Xurkon da5546fde0 fix(learner): restore persisted NPC spawns into override on load
InjectLearnedData stripped each learned NPC's spawn field [7] when injecting
into npcDataOverrides (CopyWithoutField(data,7) / k~=7), deferring to
_MergeSpawnEvidence. That promoter only runs on live kill evidence, so spawns
learned in a prior session never returned to the queryable DB on /reload, and
a freshly accepted quest (8325 -> Mana Wyrm 15274 on Sunstrider) had no pins
until the mob was re-killed.

Re-merge the saved [7] spawns into the override at injection time, normalized
to the canonical uiMapId via NormalizeSpawnZoneKey (so an areaId left by the
uiMapId->areaId migration, e.g. 3431/3430, maps back to 1241 for the renderer/
HBD), deep-merged with InsertIfNewBucket, and gated by IsAscensionProtected so
curated AscensionDB coords are never overwritten (always restored in learner
mode; in auto mode only non-curated NPCs). Regression from 7ce0cdc.
2026-06-08 16:51:58 -05:00
Xurkon c7af49d8bd perf(l10n): add zero-arg fast path to translate
_l10n:translate now short-circuits the common no-argument case
(select('#', ...) == 0), returning the translation directly and skipping
the {...} allocation, the tostring loop, and safeFormat. Behavior matches
the slow path: a successful no-arg lookup already returned the raw value,
and missing/invalid entries fall back to the key. Cherry-picked from
phase2-lua50-sweep.
2026-06-08 16:25:09 -05:00
Xurkon 700445c248 fix(options): use {} not { ... } for tab tables
Five Options files seeded their tab tables with { ... } instead of {}.
At chunk scope WoW passes ... = (addonName, addonTable), so each table
got two stray junk entries instead of being empty; { ... } is also a
strict Lua 5.0 parse error. Fixed QuestieOptions.tabs and the Arrow,
General, Keybinds, and Tracker tab tables. Cherry-picked from
phase2-lua50-sweep (the only version-independent fix in that branch).
2026-06-08 16:24:46 -05:00
Xurkon d92ae93870 fix(learner): seed objective pins on accept; handle monster objectives
OnQuestAccepted now seeds objective pins directly from the SavedVariables
learner payload (objIndex) on accept in learner mode, instead of waiting
for quest-log text sync — so quests like 8325 spawn pins immediately when
the objective mapping already exists. The accept-time ID lookup is widened
from killcredit to also cover monster objectives, and falls back to
objData.Id when no IdList is present.
2026-06-08 08:00:15 -05:00
Xurkon bb0edacec0 fix(db): enrich auto-mode static quests with learner objective payload
In 'auto' data-source mode, QuestieDB.GetQuest now overlays the
SavedVariables learner record on top of the static DB quest, filling only
nil fields and deep-merging objectives/objIndex. Fixes quests like 8325
where the shipped DB has only a stub record but QuestieLearner holds the
real objective mapping that the stub was blocking. Learner data never
overwrites a present static value, so it strictly enriches.
2026-06-08 07:59:54 -05:00