feat: lock item from being sold or deleted
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+12
-1
@@ -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
|
||||
|
||||
+183
-1
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
+21
-8
@@ -565,29 +565,42 @@
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
|
||||
<!-- Mark Equipment Sets Checkbox -->
|
||||
<CheckButton name="Guda_SettingsPopup_MarkEquipmentSetsCheckbox" inherits="OptionsCheckButtonTemplate">
|
||||
<!-- White Items as Junk Checkbox -->
|
||||
<CheckButton name="Guda_SettingsPopup_WhiteItemsJunkCheckbox" inherits="OptionsCheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativePoint="RIGHT" relativeTo="Guda_SettingsPopup_MarkUnusableCheckbox">
|
||||
<Offset><AbsDimension x="200" y="0"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnLoad(this)</OnLoad>
|
||||
<OnClick>Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnClick(this)</OnClick>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
|
||||
<!-- Mark Equipment Sets Checkbox -->
|
||||
<CheckButton name="Guda_SettingsPopup_MarkEquipmentSetsCheckbox" inherits="OptionsCheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeTo="Guda_SettingsPopup_MarkUnusableCheckbox">
|
||||
<Offset><AbsDimension x="0" y="-8"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>Guda_SettingsPopup_MarkEquipmentSetsCheckbox_OnLoad(this)</OnLoad>
|
||||
<OnClick>Guda_SettingsPopup_MarkEquipmentSetsCheckbox_OnClick(this)</OnClick>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
|
||||
<!-- White Items as Junk Checkbox -->
|
||||
<CheckButton name="Guda_SettingsPopup_WhiteItemsJunkCheckbox" inherits="OptionsCheckButtonTemplate">
|
||||
<!-- Auto Lock Set Items Checkbox -->
|
||||
<CheckButton name="Guda_SettingsPopup_AutoLockSetItemsCheckbox" inherits="OptionsCheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeTo="Guda_SettingsPopup_MarkUnusableCheckbox">
|
||||
<Offset><AbsDimension x="0" y="-8"/></Offset>
|
||||
<Anchor point="LEFT" relativePoint="RIGHT" relativeTo="Guda_SettingsPopup_MarkEquipmentSetsCheckbox">
|
||||
<Offset><AbsDimension x="200" y="0"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnLoad(this)</OnLoad>
|
||||
<OnClick>Guda_SettingsPopup_WhiteItemsJunkCheckbox_OnClick(this)</OnClick>
|
||||
<OnLoad>Guda_SettingsPopup_AutoLockSetItemsCheckbox_OnLoad(this)</OnLoad>
|
||||
<OnClick>Guda_SettingsPopup_AutoLockSetItemsCheckbox_OnClick(this)</OnClick>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user