diff --git a/Core/Database.lua b/Core/Database.lua index 0d3bdf5..12e34b0 100644 --- a/Core/Database.lua +++ b/Core/Database.lua @@ -139,6 +139,14 @@ function DB:Initialize() if Guda_CharDB.settings.whiteItemsJunk == nil then Guda_CharDB.settings.whiteItemsJunk = false end + if Guda_CharDB.settings.autoLockSetItems == nil then + Guda_CharDB.settings.autoLockSetItems = true + end + + -- Initialize locked items storage + if not Guda_CharDB.lockedItems then + Guda_CharDB.lockedItems = {} + end -- Initialize CategoryManager for custom categories if addon.Modules.CategoryManager then @@ -444,6 +452,40 @@ function DB:SetSetting(key, value) Guda_CharDB.settings[key] = value end +------------------------------------------------- +-- Locked Items (per-character, itemID-based) +------------------------------------------------- + +function DB:IsItemLocked(itemID) + if not itemID or not Guda_CharDB or not Guda_CharDB.lockedItems then return false end + return Guda_CharDB.lockedItems[itemID] and true or false +end + +function DB:ToggleItemLock(itemID) + if not itemID or not Guda_CharDB then return false end + if not Guda_CharDB.lockedItems then + Guda_CharDB.lockedItems = {} + end + if Guda_CharDB.lockedItems[itemID] then + Guda_CharDB.lockedItems[itemID] = nil + return false + else + Guda_CharDB.lockedItems[itemID] = true + return true + end +end + +-- Check if an item is protected (user-locked or in equipment set with autoLockSetItems) +function DB:IsItemProtected(itemID) + if not itemID then return false end + if self:IsItemLocked(itemID) then return true end + if self:GetSetting("autoLockSetItems") then + local EquipSets = addon.Modules.EquipmentSets + if EquipSets and EquipSets.IsInSet and EquipSets:IsInSet(itemID) then return true end + end + return false +end + -- Cleanup old characters (not updated in 90 days) function DB:CleanupOldCharacters() local cutoff = time() - (90 * 24 * 60 * 60) -- 90 days diff --git a/Guda.toc b/Guda.toc index 2109d22..11dc89a 100644 --- a/Guda.toc +++ b/Guda.toc @@ -2,7 +2,7 @@ ## Title: Guda ## Notes: All-in-one bag and bank addon for World of Warcraft 1.12.1 (Turtle WoW) ## Author: Vati -## Version: 2.1.0 +## Version: 2.1.1 ## SavedVariables: Guda_DB ## SavedVariablesPerCharacter: Guda_CharDB diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 1ea042b..fb5a91a 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -3449,12 +3449,23 @@ function BagFrame:Initialize() if autoVendor == nil then autoVendor = true end if autoVendor then local junkItems = {} + local DB = addon.Modules.DB + local Utils = addon.Modules.Utils for bag = 0, 4 do local numSlots = GetContainerNumSlots(bag) for slot = 1, numSlots do local link = GetContainerItemLink(bag, slot) if link and string.find(link, "|cff9d9d9d") then - table.insert(junkItems, { bag = bag, slot = slot }) + local skip = false + if DB and Utils then + local itemID = Utils:ExtractItemID(link) + if itemID and DB:IsItemProtected(itemID) then + skip = true + end + end + if not skip then + table.insert(junkItems, { bag = bag, slot = slot }) + end end end end diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index bf94c80..1b482d1 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -479,6 +479,149 @@ local function HideJunkIcon(button) end end +--===================================================== +-- Lock Icon Pool (same pattern as junk icon pool) +--===================================================== +local lockIconPool = {} + +local function AcquireLockIcon() + local icon = table.remove(lockIconPool) + if not icon then + icon = CreateFrame("Frame", nil, UIParent) + icon:SetFrameStrata("HIGH") + icon:SetWidth(13) + icon:SetHeight(13) + + -- Shadow (behind) + local shadow = icon:CreateTexture(nil, "BACKGROUND") + shadow:SetWidth(13) + shadow:SetHeight(13) + shadow:SetPoint("CENTER", icon, "CENTER", 1, -1) + shadow:SetTexture("Interface\\Icons\\INV_Misc_Key_04") + shadow:SetVertexColor(0, 0, 0, 1) + icon.shadow = shadow + + -- Icon (front) + local texture = icon:CreateTexture(nil, "OVERLAY") + texture:SetAllPoints(icon) + texture:SetTexture("Interface\\Icons\\INV_Misc_Key_04") + icon.texture = texture + end + return icon +end + +local function ReleaseLockIcon(icon) + if icon then + icon:Hide() + icon:ClearAllPoints() + table.insert(lockIconPool, icon) + end +end + +local function UpdateLockIcon(button, iconSize) + local DB = addon.Modules.DB + if not DB then return end + + local isLocked = false + if button.hasItem and button.itemData and button.itemData.link then + local Utils = addon.Modules.Utils + local itemID = Utils and Utils.ExtractItemID and Utils:ExtractItemID(button.itemData.link) + if itemID and DB:IsItemLocked(itemID) then + isLocked = true + end + end + + if isLocked then + if not button.lockIcon then + button.lockIcon = AcquireLockIcon() + end + local lockSize = math.max(10, math.min(14, iconSize * 0.35)) + button.lockIcon:SetWidth(lockSize) + button.lockIcon:SetHeight(lockSize) + if button.lockIcon.shadow then + button.lockIcon.shadow:SetWidth(lockSize) + button.lockIcon.shadow:SetHeight(lockSize) + end + button.lockIcon:ClearAllPoints() + button.lockIcon:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 1, -1) + button.lockIcon:SetFrameLevel(button:GetFrameLevel() + 5) + button.lockIcon:Show() + else + if button.lockIcon then + ReleaseLockIcon(button.lockIcon) + button.lockIcon = nil + end + end +end + +local function HideLockIcon(button) + if button.lockIcon then + ReleaseLockIcon(button.lockIcon) + button.lockIcon = nil + end +end + +-- Hook UseContainerItem to prevent selling protected items at vendor +local OriginalUseContainerItem = UseContainerItem +UseContainerItem = function(bag, slot, ...) + local DB = addon.Modules.DB + if DB and MerchantFrame and MerchantFrame:IsVisible() then + local link = GetContainerItemLink(bag, slot) + if link then + local Utils = addon.Modules.Utils + local itemID = Utils and Utils.ExtractItemID and Utils:ExtractItemID(link) + if itemID and DB:IsItemProtected(itemID) then + addon:Print("Cannot sell " .. link .. " — item is protected") + return + end + end + end + return OriginalUseContainerItem(bag, slot) +end + +-- Track cursor item for delete protection (GetCursorInfo doesn't exist in 1.12.1) +local cursorProtectedLink = nil + +local OriginalPickupContainerItem = PickupContainerItem +PickupContainerItem = function(bag, slot, ...) + local DB = addon.Modules.DB + if DB then + local link = GetContainerItemLink(bag, slot) + if link then + local Utils = addon.Modules.Utils + local itemID = Utils and Utils.ExtractItemID and Utils:ExtractItemID(link) + if itemID and DB:IsItemProtected(itemID) then + cursorProtectedLink = link + else + cursorProtectedLink = nil + end + else + cursorProtectedLink = nil + end + end + return OriginalPickupContainerItem(bag, slot) +end + +-- Hook delete confirmation popups +local function HookDeletePopup(dialogName) + if not StaticPopupDialogs or not StaticPopupDialogs[dialogName] then return end + local originalOnShow = StaticPopupDialogs[dialogName].OnShow + StaticPopupDialogs[dialogName].OnShow = function() + if cursorProtectedLink then + addon:Print("Cannot delete " .. cursorProtectedLink .. " — item is protected") + ClearCursor() + cursorProtectedLink = nil + this:Hide() + return + end + if originalOnShow then + return originalOnShow() + end + end +end +HookDeletePopup("DELETE_ITEM") +HookDeletePopup("DELETE_GOOD_ITEM") + --===================================================== -- Unusable item detection (pfUI-inspired implementation) -- Adds a red tint overlay to items that your character @@ -761,9 +904,10 @@ function Guda_ItemButton_OnLoad(self) self:RegisterForClicks("LeftButtonUp", "RightButtonUp") end - -- Hide junk icon when button is hidden (since it's parented to UIParent) + -- Hide junk and lock icons when button is hidden (since they're parented to UIParent) self:SetScript("OnHide", function() HideJunkIcon(this) + HideLockIcon(this) end) -- Track cursor item on drag start for category drag-drop @@ -775,6 +919,38 @@ function Guda_ItemButton_OnLoad(self) end) self:SetScript("OnClick", function() + -- Lock/unlock item with Ctrl+Right-Click + if IsControlKeyDown() and arg1 == "RightButton" and this.hasItem and not this.otherChar and not this.isReadOnly then + local link = GetContainerItemLink(this.bagID, this.slotID) + if link and addon and addon.Modules and addon.Modules.Utils and addon.Modules.DB then + local itemID = addon.Modules.Utils:ExtractItemID(link) + if itemID then + -- Skip if already protected by equipment set + if addon.Modules.DB:GetSetting("autoLockSetItems") then + local EquipSets = addon.Modules.EquipmentSets + if EquipSets and EquipSets.IsInSet and EquipSets:IsInSet(itemID) then + addon:Print(link .. " is already protected by equipment set") + return + end + end + local isNowLocked = addon.Modules.DB:ToggleItemLock(itemID) + if isNowLocked then + addon:Print(link .. " locked") + else + addon:Print(link .. " unlocked") + end + -- Refresh bag/bank frames + if addon.Modules.BagFrame and addon.Modules.BagFrame.Update then + addon.Modules.BagFrame:Update() + end + if addon.Modules.BankFrame and addon.Modules.BankFrame.Update then + addon.Modules.BankFrame:Update() + end + end + end + return + end + if IsAltKeyDown() and arg1 == "LeftButton" and this.hasItem and not this.otherChar and not this.isReadOnly then local link = GetContainerItemLink(this.bagID, this.slotID) if link and addon and addon.Modules and addon.Modules.Utils then @@ -1197,6 +1373,9 @@ local function ClearItemButton(self, emptySlotBg, countText, bagID) -- Hide junk icon HideJunkIcon(self) + -- Hide lock icon + HideLockIcon(self) + -- Hide normal texture self:SetNormalTexture("") local normalBorder = getglobal(self:GetName().."NormalTexture") @@ -1337,6 +1516,9 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha end end + -- Update lock icon + UpdateLockIcon(self, iconSize) + -- Extend empty slot bg 9px past button edges for rounded corner coverage if emptySlotBg then emptySlotBg:ClearAllPoints() diff --git a/UI/SettingsPopup.lua b/UI/SettingsPopup.lua index 969512a..33e45f5 100644 --- a/UI/SettingsPopup.lua +++ b/UI/SettingsPopup.lua @@ -284,6 +284,14 @@ function Guda_SettingsPopup_OnShow(self) markEquipmentSetsCheckbox:SetChecked(markEquipmentSets and 1 or 0) end + -- Auto Lock Set Items checkbox + local autoLockSetItemsCheckbox = getglobal("Guda_SettingsPopup_AutoLockSetItemsCheckbox") + local autoLockSetItems = Guda.Modules.DB:GetSetting("autoLockSetItems") + if autoLockSetItems == nil then autoLockSetItems = true end + if autoLockSetItemsCheckbox then + autoLockSetItemsCheckbox:SetChecked(autoLockSetItems and 1 or 0) + end + -- Show Category Count checkbox local showCategoryCountCheckbox = getglobal("Guda_SettingsPopup_ShowCategoryCountCheckbox") local showCategoryCount = Guda.Modules.DB:GetSetting("showCategoryCount") @@ -1318,6 +1326,38 @@ function Guda_SettingsPopup_MarkEquipmentSetsCheckbox_OnClick(self) end end +-- Auto Lock Set Items Checkbox OnLoad +function Guda_SettingsPopup_AutoLockSetItemsCheckbox_OnLoad(self) + local text = getglobal(self:GetName().."Text") + if text then + text:SetText("Auto Lock Set Items") + + local font, _, flags = text:GetFont() + if font then + text:SetFont(font, 13, flags) + end + end + + self.tooltipText = "Prevent selling and deleting items saved in equipment sets." + + local enabled = true + if Guda and Guda.Modules and Guda.Modules.DB then + enabled = Guda.Modules.DB:GetSetting("autoLockSetItems") + if enabled == nil then enabled = true end + end + + self:SetChecked(enabled and 1 or 0) +end + +-- Auto Lock Set Items Checkbox OnClick +function Guda_SettingsPopup_AutoLockSetItemsCheckbox_OnClick(self) + local isChecked = self:GetChecked() == 1 + + if Guda and Guda.Modules and Guda.Modules.DB then + Guda.Modules.DB:SetSetting("autoLockSetItems", isChecked) + end +end + -- Show Category Count Checkbox OnLoad function Guda_SettingsPopup_ShowCategoryCountCheckbox_OnLoad(self) local text = getglobal(self:GetName().."Text") diff --git a/UI/SettingsPopup.xml b/UI/SettingsPopup.xml index 96efb3c..24f2db6 100644 --- a/UI/SettingsPopup.xml +++ b/UI/SettingsPopup.xml @@ -565,29 +565,42 @@ - - + + + + Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnLoad(this) + Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnClick(this) + + + + + + + + + + Guda_SettingsPopup_MarkEquipmentSetsCheckbox_OnLoad(this) Guda_SettingsPopup_MarkEquipmentSetsCheckbox_OnClick(this) - - + + - - + + - Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnLoad(this) - Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnClick(this) + Guda_SettingsPopup_AutoLockSetItemsCheckbox_OnLoad(this) + Guda_SettingsPopup_AutoLockSetItemsCheckbox_OnClick(this)