diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e5dba3..de35861 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## v1.2.9 — QuestieLearner Cross-Link Engine + Tracker Zone Fix + Untrack Fix + +### QuestieLearner.lua — Universal Cross-Link Engine + +- **[Cross-link engine — full rewrite]** Replaced the narrow NPC↔Quest cross-link stubs with a comprehensive bidirectional relationship engine covering all four entity types. Three shared primitives underpin the whole system: `_AddToArray(tbl, key, value, ovrTable, ovrId)` adds a value to an array field and mirrors it to the live `*DataOverrides` table in the same call; `_AddToNestedArray` does the same for two-level nested arrays (used for quest starters/finishers sub-slots); `_AddToQuestObjective` inserts `{entityId, text}` pairs into `quest[10][slot]` (the objectives array) with the same dual-write pattern. Every write is idempotent (no-op if value already present). +- **[CrossLinkAfterNPC]** Called whenever a new NPC ID is first committed to `learnedData.npcs`. Iterates all learned quests: (a) if quest`[2][1]` contains this npcId → adds questId to `npc[10]` (questStarts); (b) if quest`[3][1]` contains this npcId → adds questId to `npc[11]` (questEnds); (c) walks quest`[10][3]` (item objectives) — for each item whose `item[2]` (npcDrops) already references this NPC, the NPC is injected into `quest[10][1]` as a creature objective so it can receive map pins and tooltip text. +- **[CrossLinkAfterQuest]** Called when a new quest is first committed to `learnedData.quests`. Links all referenced entities: starter NPCs (quest`[2][1]`) → each known NPC's `npc[10]`; starter objects (quest`[2][2]`) → each known object's `obj[2]`; finisher NPCs (quest`[3][1]`) → `npc[11]`; finisher objects (quest`[3][2]`) → `obj[3]`; source item (quest`[11]`) → `item[5]` (startQuest key per itemDB schema); item drop chain (quest`[10][3]` items whose `item[2]` lists known NPCs) → those NPCs are injected into `quest[10][1]` as creature objectives. +- **[CrossLinkAfterObject]** Called when a new object is first committed to `learnedData.objects`. Scans all learned quests: if quest`[2][2]` references this objectId → adds questId to `obj[2]` (questStarts); if quest`[3][2]` references it → adds questId to `obj[3]` (questEnds). +- **[CrossLinkAfterItem]** Called when a new item is first committed to `learnedData.items` AND whenever a new drop-NPC relationship is added via `LearnItemDrop`. Two passes: (1) scan all learned quests for `quest[11] == itemId` → set `item[5] = questId` (item starts this quest); (2) scan all learned quests for `quest[10][3]` entries matching itemId — for each such quest, every NPC in `item[2]` (drop sources) is injected into `quest[10][1]` as a creature objective. This means: learn a quest that needs Wolf Fur → later kill a wolf that drops it → the wolf NPC immediately becomes a tracked creature objective for that quest. +- **[CrossLinkAfterQuestGiver]** Now handles all three entity type slots (1=NPC, 2=Object, 3=Item) instead of only NPCs. For typeSlot=1: stitches `npc[10/11]` ↔ `quest[2/3][1]` bidirectionally. For typeSlot=2: stitches `obj[2/3]` ↔ `quest[2/3][2]`. For typeSlot=3: adds itemId to `quest[2][3]` (item starters slot). All writes go to both `learnedData` (SavedVariables) and live `*DataOverrides` tables simultaneously. +- **[LearnItemDrop hook]** Now calls `CrossLinkAfterItem(itemId)` every time a new NPC is added to an item's drop list, not just on initial item creation. This propagates the drop→quest objective chain retroactively. +- **[LearnQuest hook]** Calls `CrossLinkAfterQuest(questId)` on first creation of a quest record. +- **[LearnObject hook]** Calls `CrossLinkAfterObject(objectId)` on first creation of an object record. +- **[LearnItem hook]** Calls `CrossLinkAfterItem(itemId)` on first creation of an item record. +- **[Item schema correction]** Fixed incorrect use of key `[9]` (itemLevel) for quest-source storage. Now correctly uses `[5]` (`startQuest`) per `QuestieDB.itemKeys` schema. +- **[Mouseover — all NPCs now learned]** Removed the `UnitReaction < 4` early-return guard that blocked hostile/neutral NPC learning via mouseover. All NPCs are now learned on hover. The existing `QUESTGIVER` NPC-flag check remains as the primary filter for quest-relevant NPCs during mouseover. Hostile quest objective NPCs are now captured on any interaction (mouseover, targeting, or kill) and are correctly cross-linked to relevant quests. + +### TrackerUtils.lua — Zone Resolution Rewrite + +- **[GetQuestLogZoneName — canonical 3.3.5 zone lookup]** Added new local function `GetQuestLogZoneName(questId)`. Implements the canonical 3.3.5a zone resolution method: iterates the quest log from index 1 to `GetNumQuestLogEntries()` to locate the quest's log index, then walks backwards from that index checking `isHeader` until the nearest zone header title is found. This is the only fully reliable zone name source in 3.3.5a — the client places zone header entries (where `isHeader=true`) immediately above the quests belonging to that zone in the quest log flat list. `GetRealZoneText()` and `GetCurrentMapAreaID()` are unreliable because they depend on the currently viewed map, not the quest's actual zone. +- **[BuildFallbackQuest — zone via log walk]** Replaced the previous `GetRealZoneText()` call with the quest log header walk. The existing loop that iterates the quest log to find the quest entry now continues backwards from that index to find the header title. `GetAreaIdByZoneName()` is still called to convert the string to an area ID for `zoneOrSort`, but the string itself is always the ground truth used for display. +- **[_isLogFallback patch block — zone fix]** The block that patches QuestLogCache fallback objects (those with `_isLogFallback=true` but no `IsComplete` method) now calls `GetQuestLogZoneName(capturedId)` immediately after patching `Objectives`/`SpecialObjectives`/`ExtraObjectives`. If a zone name is returned, both `quest.zoneName` and `quest.zoneOrSort` are set on the object. Previously this block left `zoneOrSort=0` and `zoneName=nil`, causing every fallback quest to group under "Unknown Zone". +- **[Fallback cache invalidation]** `_fallbackQuests[qid]` is now evicted and rebuilt if the cached entry has no `zoneName`. This handles the case where `BuildFallbackQuest` was called during addon init (before the quest log was fully populated) and cached a zoneless entry. +- **[GetAreaIdByZoneName preserved]** Still available for converting zone header strings to numeric area IDs for `zoneOrSort`. The area ID is used by the proximity sort and zone grouping logic; the string is used for display labels. + +### QuestieTracker.lua — Untrack Fix + +- **[UntrackQuestId — logic was inverted]** `UntrackQuestId` was setting `AutoUntrackedQuests[questId] = nil` when untracking. The tracker filter reads `not Questie.db.char.AutoUntrackedQuests[questId]` — `nil` evaluates as "show this quest", so clearing the entry on untrack immediately re-showed the quest on the next update tick. Fix: when `autoTrackQuests = true`, untrack now sets `AutoUntrackedQuests[questId] = true` (hide); when `autoTrackQuests = false`, it clears `TrackedQuests[questId]` (manual mode). The two modes are now cleanly separated. +- **[AQW_Insert — removed QuestLogFrame gate]** The `IsShiftKeyDown() and QuestLogFrame:IsShown()` guard that was blocking re-tracking quests from outside the quest log frame has been removed. Re-tracking (clearing `AutoUntrackedQuests[questId]`) now works from any context. The shift-click-to-untrack path was already handled by `RemoveQuestWatch` → `UntrackQuestId`; this gate was only blocking the re-track flow. +- **[Quest completion cleanup]** `_RemoveQuestIdFromCharTables` already clears `AutoUntrackedQuests` when a quest leaves the log (completed or abandoned), preventing stale hidden-quest entries in SavedVariables. + +--- + ## v1.2.7 — WotLKDB Plugin Stats Fix + Kill Tracking Refinement ### QuestieInit.lua — LoadBaseDB Count Tracking diff --git a/Modules/Tracker/TrackerUtils.lua b/Modules/Tracker/TrackerUtils.lua index cbaa8e4..4dd3a69 100644 --- a/Modules/Tracker/TrackerUtils.lua +++ b/Modules/Tracker/TrackerUtils.lua @@ -770,6 +770,7 @@ function TrackerUtils:GetSortedQuestIds() if type(quest) == "table" and quest._isLogFallback then -- Patch IsComplete method onto the existing QuestLogCache object so -- QuestieMap and other modules that read currentQuestlog also get it. + -- Also ensure fields QuestieMap iterates exist to avoid pairs(nil) crashes. local capturedId = qid quest.IsComplete = function(self) for i = 1, GetNumQuestLogEntries() do @@ -780,6 +781,9 @@ function TrackerUtils:GetSortedQuestIds() end return 0 end + if not quest.Objectives then quest.Objectives = {} end + if not quest.SpecialObjectives then quest.SpecialObjectives = {} end + if not quest.ExtraObjectives then quest.ExtraObjectives = {} end QuestiePlayer.currentQuestlog[qid] = quest else -- No object at all — build one from the log diff --git a/docs/changelog.html b/docs/changelog.html index 65aec7b..98b5699 100644 --- a/docs/changelog.html +++ b/docs/changelog.html @@ -175,6 +175,39 @@ +
+

v1.2.9 — QuestieLearner Cross-Link Engine + Tracker Zone Fix + Untrack Fix

+

Introduces a universal bidirectional cross-link engine in QuestieLearner that automatically stitches relationships between all four entity types (NPCs, Quests, Objects, Items) as data is learned — no manual wiring needed. Fixes the tracker's persistent "Unknown Zone" header for custom/unknown quests by replacing unreliable map API calls with the canonical 3.3.5a quest log header walk. Fixes a logic inversion in UntrackQuestId that prevented shift-click untacking from working.

+ +

QuestieLearner.lua — Universal Cross-Link Engine

+ + +

TrackerUtils.lua — Zone Resolution Rewrite

+ + +

QuestieTracker.lua — Untrack Fix

+ +
+

v1.2.6 — QuestieLearner Comprehensive Overhaul

Rewrote QuestieLearner from scratch to fix all known data-capture deficiencies. Adds full quest field coverage, grid-based coordinate clustering, quest-giver-only mouseover filtering, async item-info retry, object loot detection, and live inject-on-import so imported data takes effect without a reload.