From 9590bcd676dc536f5c5433ae8b798e529e3bcf14 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Fri, 23 Jan 2026 23:35:26 +0400 Subject: [PATCH] permanently enchant items --- Core/Utils.lua | 45 +++++++++++++++++++- Sorting/SortEngine.lua | 93 +++++++++++++++++++++++++++++++++++------- UI/ItemButton.lua | 21 ++++++++++ UI/QuestItemBar.lua | 11 +++++ 4 files changed, 154 insertions(+), 16 deletions(-) diff --git a/Core/Utils.lua b/Core/Utils.lua index 05e1edc..a0a2b28 100644 --- a/Core/Utils.lua +++ b/Core/Utils.lua @@ -732,6 +732,34 @@ local function ScanTooltipForQuest(tooltip, tooltipName) return isQuestItem, isQuestStarter end +-- Check if item has "Permanently..." text (enchanting scrolls/vellums) +-- These should NOT be considered quest items even if they have Quest category +local function IsPermanentEnchantItem(tooltip, tooltipName) + if not tooltip then + addon:Debug("IsPermanentEnchantItem: tooltip is nil") + return false + end + local numLines = tooltip:NumLines() or 0 + addon:Debug("IsPermanentEnchantItem: scanning %d lines", numLines) + for i = 1, numLines do + local line = getglobal(tooltipName .. "TextLeft" .. i) + if line then + local text = line:GetText() + if text then + local tl = string.lower(text) + addon:Debug("IsPermanentEnchantItem line %d: %s", i, tl) + -- Just check for "permanently" anywhere (green text doesn't have "Use:" prefix) + if string.find(tl, "permanently") then + addon:Debug("IsPermanentEnchantItem: FOUND permanently!") + return true + end + end + end + end + addon:Debug("IsPermanentEnchantItem: NOT found") + return false +end + -- Consolidated quest item detection function -- Handles tooltip scanning, category checks, equipment filtering, and QuestItemsDB lookup -- Parameters: @@ -772,8 +800,23 @@ function Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, isBank) itemType == "Weapon" or itemType == "Armor") local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest") - -- Priority 1: If explicitly categorized as Quest, it's a quest item + -- Priority 1: If explicitly categorized as Quest, check if it's actually an enchant item first if isQuestCategory then + addon:Debug("IsQuestItem: Quest category detected, checking for permanent enchant...") + -- Check tooltip for "Permanently" (enchanting scrolls should not be quest items) + if not isOtherChar and bagID and slotID then + local tooltip = GetScanTooltip() + tooltip:ClearLines() + tooltip:SetBagItem(bagID, slotID) + local isPermanent = IsPermanentEnchantItem(tooltip, "GudaBagScanTooltip") + addon:Debug("IsQuestItem: IsPermanentEnchantItem returned %s", tostring(isPermanent)) + if isPermanent then + return false, false -- Not a quest item, it's an enchant scroll + end + else + addon:Debug("IsQuestItem: Skipping permanent enchant check (otherChar=%s, bagID=%s, slotID=%s)", + tostring(isOtherChar), tostring(bagID), tostring(slotID)) + end return true, false end diff --git a/Sorting/SortEngine.lua b/Sorting/SortEngine.lua index 0409c16..c662bd0 100644 --- a/Sorting/SortEngine.lua +++ b/Sorting/SortEngine.lua @@ -130,19 +130,27 @@ end local function GetItemProperties(bagID, slotID, itemLink) if not itemLink then return nil end - + -- Extract base item link for stable caching (item:ID:Enchant:...) local _, _, baseLink = string.find(itemLink, "(item:%d+:%d+:%d+:%d+:%d+:%d+:%d+:%d+)") local cacheKey = baseLink or itemLink - + if propertyCache[cacheKey] then + if addon.DEBUG then + addon:Print("GetItemProperties CACHE HIT for %s", cacheKey or "unknown") + end return propertyCache[cacheKey] end + if addon.DEBUG then + addon:Print("GetItemProperties CACHE MISS - scanning bagID=%s slotID=%s", tostring(bagID), tostring(slotID)) + end + local props = { isQuest = false, isQuestStarter = false, isQuestUsable = false, + isPermanentEnchant = false, -- "Use: Permanently adds" items isGray = false, restoreTag = nil } @@ -182,13 +190,22 @@ local function GetItemProperties(bagID, slotID, itemLink) end -- Usable check - if string.find(tl, "use:") or - string.find(tl, "right%-click") or - string.find(tl, "right click") or + if string.find(tl, "use:") or + string.find(tl, "right%-click") or + string.find(tl, "right click") or string.find(tl, "click to") then props.isQuestUsable = true end - + + -- Permanent enchant check (scrolls with "Permanently...") + -- Check for "permanently" anywhere in the line (green text doesn't have "Use:" prefix) + if string.find(tl, "permanently") then + props.isPermanentEnchant = true + if addon.DEBUG then + addon:Print("Detected permanent enchant: %s", tl) + end + end + -- Restore tag check (higher priority tags override lower ones) if string.find(tl, "while eating") then props.restoreTag = "eat" @@ -212,6 +229,10 @@ local function GetItemProperties(bagID, slotID, itemLink) end end + if addon.DEBUG and props.isPermanentEnchant then + addon:Print("GetItemProperties RETURNING isPermanentEnchant=true for %s", cacheKey or "unknown") + end + propertyCache[cacheKey] = props return props end @@ -670,9 +691,29 @@ local function AddSortKeys(items) item.equipSlotOrder = EQUIP_SLOT_ORDER[itemSubType] or 999 else item.sortedClass = CATEGORY_ORDER[itemCategory] or 99 + + -- Check if item is a permanent enchant (should NOT be Quest) + local itemProps = GetItemProperties(item.bagID, item.slot, item.data.link) + local isPermanentEnchant = itemProps and itemProps.isPermanentEnchant or false + + -- Debug: trace isPermanentEnchant value + if addon.DEBUG and (itemCategory == "Quest" or itemType == "Quest") then + addon:Print("GetItemProperties for %s: props=%s, isPermanentEnchant=%s", + item.itemName or "unknown", + itemProps and "exists" or "NIL", + itemProps and tostring(itemProps.isPermanentEnchant) or "N/A") + end + + -- Permanent enchant items always sort BEFORE quest items (category 6) + -- regardless of their itemCategory or itemType + if isPermanentEnchant then + item.sortedClass = 6 -- Same as Tools, comes before Quest (7) + if addon.DEBUG then + addon:Print("PERMANENT ENCHANT: %s -> sortedClass=6", item.itemName or "unknown") + end -- Heuristic: Detect items that should be in the Quest category (priority 7) -- but aren't categorized as such by the game (e.g. some "Manual" items) - if item.sortedClass ~= (CATEGORY_ORDER["Quest"] or 7) then + elseif item.sortedClass ~= (CATEGORY_ORDER["Quest"] or 7) then local nameLower = string.lower(item.itemName) if string.find(nameLower, "manual") or string.find(nameLower, "quest") then item.sortedClass = CATEGORY_ORDER["Quest"] or 7 @@ -687,6 +728,12 @@ local function AddSortKeys(items) end end item.equipSlotOrder = 999 + + -- Debug: Show final sortedClass for quest-related items + if addon.DEBUG and (itemCategory == "Quest" or itemType == "Quest" or isPermanentEnchant) then + addon:Print("SORT CLASS: %s -> sortedClass=%d, isPermanentEnchant=%s", + item.itemName or "unknown", item.sortedClass, tostring(isPermanentEnchant)) + end end -- Subclass ordering @@ -704,19 +751,23 @@ local function AddSortKeys(items) item.subclass = itemSubType or "" -- Quest flags: mark quest items and detect starter/usable states + -- Note: permanent enchant items should NOT be marked as quest items item.isQuest = false item.isQuestStarter = false item.isQuestUsable = false + item.isPermanentEnchant = isPermanentEnchant -- Store for potential use in sorting local nameLower = string.lower(item.itemName) - if itemCategory == "Quest" or string.find(nameLower, "quest") or item.data.class == "Quest" or IsQuestItemTooltip(item.bagID, item.slot) then - item.isQuest = true - if IsQuestItemStarter(item.bagID, item.slot) then item.isQuestStarter = true end - if IsQuestItemUsable(item.bagID, item.slot) then item.isQuestUsable = true end - elseif addon.IsQuestItemByID then - -- Check QuestItemsDB for faction-specific quest items - local playerFaction = UnitFactionGroup("player") - if addon:IsQuestItemByID(itemID, playerFaction) then + if not isPermanentEnchant then + if itemCategory == "Quest" or string.find(nameLower, "quest") or item.data.class == "Quest" or IsQuestItemTooltip(item.bagID, item.slot) then item.isQuest = true + if IsQuestItemStarter(item.bagID, item.slot) then item.isQuestStarter = true end + if IsQuestItemUsable(item.bagID, item.slot) then item.isQuestUsable = true end + elseif addon.IsQuestItemByID then + -- Check QuestItemsDB for faction-specific quest items + local playerFaction = UnitFactionGroup("player") + if addon:IsQuestItemByID(itemID, playerFaction) then + item.isQuest = true + end end end @@ -1292,6 +1343,9 @@ end -- Execute exactly ONE full sorting pass over bags (used by safety wrapper) function SortEngine:SortBagsPass() + -- Clear property cache to ensure fresh detection + propertyCache = {} + local bagIDs = addon.Constants.BAGS -- Phase 1: Detect specialized bags @@ -1346,6 +1400,9 @@ function SortEngine:SortBagsPass() end function SortEngine:SortBags() + -- Clear property cache to ensure fresh detection + propertyCache = {} + local bagIDs = addon.Constants.BAGS -- Phase 1: Detect specialized bags @@ -1412,6 +1469,9 @@ end -- Execute exactly ONE full sorting pass over bank (used by safety wrapper) function SortEngine:SortBankPass() + -- Clear property cache to ensure fresh detection + propertyCache = {} + if not addon.Modules.BankScanner:IsBankOpen() then addon:Print("Bank must be open to sort!") return 0 @@ -1468,6 +1528,9 @@ function SortEngine:SortBankPass() end function SortEngine:SortBank() + -- Clear property cache to ensure fresh detection + propertyCache = {} + if not addon.Modules.BankScanner:IsBankOpen() then addon:Print("Bank must be open to sort!") return 0 diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index c1bd9c7..899c5ad 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -1338,6 +1338,27 @@ function Guda_ItemButton_OnEnter(self) GameTooltip:Show() -- Debug: Print item info including equipSlot when debug mode is enabled + if addon.DEBUG and self.hasItem and self.bagID and self.slotID then + local link = GetContainerItemLink(self.bagID, self.slotID) + if link then + local itemID = addon.Modules.Utils:ExtractItemID(link) + if itemID then + local name, _, itemQuality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture, itemEquipLoc, itemSellPrice = GetItemInfo(itemID) + addon:Print("=== Item Debug Info ===") + addon:Print("name: %s", tostring(name)) + addon:Print("itemQuality: %s", tostring(itemQuality)) + addon:Print("iLevel: %s", tostring(iLevel)) + addon:Print("itemCategory: %s", tostring(itemCategory)) + addon:Print("itemType: %s", tostring(itemType)) + addon:Print("itemStackCount: %s", tostring(itemStackCount)) + addon:Print("itemSubType: %s", tostring(itemSubType)) + addon:Print("itemTexture: %s", tostring(itemTexture)) + addon:Print("itemEquipLoc: %s", tostring(itemEquipLoc)) + addon:Print("itemSellPrice: %s", tostring(itemSellPrice)) + end + end + end + -- Handle merchant sell cursor (same approach as BagShui) if MerchantFrame:IsShown() and not self.isBank and not self.otherChar and self.hasItem then ShowContainerSellCursor(self.bagID, self.slotID) diff --git a/UI/QuestItemBar.lua b/UI/QuestItemBar.lua index 4db9dd4..13740d9 100644 --- a/UI/QuestItemBar.lua +++ b/UI/QuestItemBar.lua @@ -36,6 +36,7 @@ function QuestItemBar:CheckQuestItemUsable(bagID, slotID) local isQuestItem = false local isQuestStarter = false local isUsable = false + local isPermanentEnchant = false -- Track enchanting scrolls/vellums for i = 1, tooltip:NumLines() do local line = getglobal("Guda_QuestBarScanTooltipTextLeft" .. i) @@ -57,10 +58,20 @@ function QuestItemBar:CheckQuestItemUsable(bagID, slotID) if string.find(tl, "use:") or string.find(tl, "begins a quest") or string.find(tl, "starts a quest") then isUsable = true end + -- Check for permanent enchant items (should NOT be quest items) + -- Just check for "permanently" anywhere (green text doesn't have "Use:" prefix) + if string.find(tl, "permanently") then + isPermanentEnchant = true + end end end end + -- Permanent enchant items are NOT quest items, even if categorized as Quest + if isPermanentEnchant then + return false, false, false + end + -- Fallback check for quest category if not detected from tooltip local link = GetContainerItemLink(bagID, slotID) local itemID