feat: auto loot and clam opener
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
-- Guda AutoLoot
|
||||
-- TurtleWoW with SuperWoW removes the hardcoded shift-autoloot and exposes:
|
||||
-- * SetAutoloot(0|1) — global client autoloot toggle
|
||||
-- * LootSlot(slot, forceloot) — forceloot=1 is REQUIRED to actually loot
|
||||
-- We prefer SetAutoloot when present (lets the client handle everything),
|
||||
-- and also fall back to a LOOT_OPENED handler that calls LootSlot(slot, 1)
|
||||
-- so this works on clients without SuperWoW too.
|
||||
|
||||
local addon = Guda
|
||||
|
||||
local AutoLoot = {}
|
||||
addon.Modules.AutoLoot = AutoLoot
|
||||
|
||||
local function ApplyClientSetting()
|
||||
local enabled = Guda.Modules.DB and Guda.Modules.DB:GetSetting("autoLoot")
|
||||
if SetAutoloot then
|
||||
SetAutoloot(enabled and 1 or 0)
|
||||
elseif SetAutoLootDefault then
|
||||
SetAutoLootDefault(enabled and true or false)
|
||||
end
|
||||
end
|
||||
|
||||
local function OnLootOpened()
|
||||
if not (Guda.Modules.DB and Guda.Modules.DB:GetSetting("autoLoot")) then
|
||||
return
|
||||
end
|
||||
-- If SuperWoW's SetAutoloot is present and enabled, the client has
|
||||
-- already looted everything before LOOT_OPENED reaches us — this loop
|
||||
-- is a no-op safety net for normal clients and any leftovers (BoP
|
||||
-- confirms, etc.).
|
||||
local n = GetNumLootItems()
|
||||
if not n or n == 0 then return end
|
||||
for slot = n, 1, -1 do
|
||||
LootSlot(slot, 1) -- forceloot=1 for SuperWoW compatibility
|
||||
if ConfirmLootSlot then ConfirmLootSlot(slot) end
|
||||
end
|
||||
end
|
||||
|
||||
function AutoLoot:Initialize()
|
||||
addon.Modules.Events:Register("LOOT_OPENED", OnLootOpened, "AutoLoot")
|
||||
ApplyClientSetting()
|
||||
end
|
||||
|
||||
-- Called by the settings checkbox so the client toggle flips immediately.
|
||||
function AutoLoot:Apply()
|
||||
ApplyClientSetting()
|
||||
end
|
||||
@@ -0,0 +1,155 @@
|
||||
-- Guda ClamOpener
|
||||
-- Walks the player's bags and uses each clam in turn until none remain.
|
||||
-- Triggered by /guda openclams. Stops on UI_ERROR_MESSAGE so a full inventory
|
||||
-- (or any other error from UseContainerItem) cleanly aborts the run.
|
||||
|
||||
local addon = Guda
|
||||
local L = Guda_L
|
||||
|
||||
local ClamOpener = {}
|
||||
addon.Modules.ClamOpener = ClamOpener
|
||||
|
||||
-- Hardcoded vanilla clam item IDs
|
||||
local CLAM_IDS = {
|
||||
[5523] = true, -- Small Barnacled Clam
|
||||
[5524] = true, -- Thick-shelled Clam
|
||||
[7973] = true, -- Big-mouth Clam
|
||||
[15874] = true, -- Soft-shelled Clam
|
||||
}
|
||||
|
||||
local OPEN_DELAY = 0.3 -- seconds between opens; lets loot/bag updates settle
|
||||
local running = false
|
||||
local silentRun = false
|
||||
|
||||
-- Find the next clam in player bags. Returns bagID, slotID, itemLink or nil.
|
||||
local function FindNextClam()
|
||||
for bagID = 0, 4 do
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
if numSlots and numSlots > 0 then
|
||||
for slotID = 1, numSlots do
|
||||
local link = GetContainerItemLink(bagID, slotID)
|
||||
if link then
|
||||
local _, _, idStr = string.find(link, "item:(%d+)")
|
||||
local itemID = idStr and tonumber(idStr)
|
||||
if itemID and CLAM_IDS[itemID] then
|
||||
return bagID, slotID
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function StopRun(reason)
|
||||
if not running then return end
|
||||
running = false
|
||||
-- Don't unregister BAG_UPDATE — it's the auto-trigger. Only drop the
|
||||
-- per-run UI_ERROR_MESSAGE listener.
|
||||
addon.Modules.Events:UnregisterOwner("ClamOpener_Run")
|
||||
if reason and not silentRun then
|
||||
addon:Print(reason)
|
||||
end
|
||||
silentRun = false
|
||||
end
|
||||
|
||||
local function OpenNext()
|
||||
if not running then return end
|
||||
|
||||
if CursorHasItem() then
|
||||
-- Cursor is busy with something else; try again shortly.
|
||||
Guda_ScheduleTimer(OPEN_DELAY, OpenNext)
|
||||
return
|
||||
end
|
||||
|
||||
local bagID, slotID = FindNextClam()
|
||||
if not bagID then
|
||||
StopRun(L["No more clams to open."])
|
||||
return
|
||||
end
|
||||
|
||||
UseContainerItem(bagID, slotID)
|
||||
Guda_ScheduleTimer(OPEN_DELAY, OpenNext)
|
||||
end
|
||||
|
||||
-- Stop on UI_ERROR_MESSAGE (e.g. inventory full). UseContainerItem fires this
|
||||
-- the same frame on failure, so any error after we start counts as an abort.
|
||||
local function OnUIError()
|
||||
if not running then return end
|
||||
local msg = arg1
|
||||
StopRun(string.format(L["Clam opener stopped: %s"], tostring(msg or "error")))
|
||||
end
|
||||
|
||||
-- silent: true to suppress chat messages (used by auto-trigger).
|
||||
function ClamOpener:Open(silent)
|
||||
if running then
|
||||
if not silent then
|
||||
addon:Print(L["Clam opener is already running."])
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Quick sanity check so we don't print "stopped" without ever starting.
|
||||
if not FindNextClam() then
|
||||
if not silent then
|
||||
addon:Print(L["No clams found in your bags."])
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
running = true
|
||||
silentRun = silent and true or false
|
||||
addon.Modules.Events:Register("UI_ERROR_MESSAGE", OnUIError, "ClamOpener_Run")
|
||||
if not silent then
|
||||
addon:Print(L["Opening clams..."])
|
||||
end
|
||||
OpenNext()
|
||||
end
|
||||
|
||||
-- Returns true if any blocking window (loot/mail/trade/merchant/bank/auction)
|
||||
-- is currently open. We don't want to UseContainerItem while these are active —
|
||||
-- it can race the open window or get queued and lost.
|
||||
local function IsBlockingWindowOpen()
|
||||
local frames = {
|
||||
"LootFrame", "MailFrame", "TradeFrame", "MerchantFrame",
|
||||
"BankFrame", "AuctionFrame",
|
||||
-- Guda's own bank/mail frames too
|
||||
"Guda_BankFrame", "Guda_MailboxFrame",
|
||||
}
|
||||
for _, name in ipairs(frames) do
|
||||
local f = getglobal(name)
|
||||
if f and f.IsShown and f:IsShown() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Initialize auto-open. Triggers:
|
||||
-- * BAG_UPDATE — catches clams that arrived without a blocking window.
|
||||
-- * LOOT_CLOSED — the common case: clam looted from a corpse.
|
||||
-- * MAIL_CLOSED — clam received via mail.
|
||||
-- * TRADE_CLOSED — clam received via trade.
|
||||
-- * BANKFRAME_CLOSED — clam withdrawn from bank.
|
||||
-- Each trigger defers ~0.15s and then attempts a silent Open(); the running
|
||||
-- guard, the FindNextClam check, and the IsBlockingWindowOpen guard absorb
|
||||
-- the noise of multiple events firing in quick succession.
|
||||
function ClamOpener:Initialize()
|
||||
local function tryAutoOpen()
|
||||
if running then return end
|
||||
if not (Guda.Modules.DB and Guda.Modules.DB:GetSetting("autoOpenClams")) then
|
||||
return
|
||||
end
|
||||
Guda_ScheduleTimer(0.15, function()
|
||||
if running then return end
|
||||
if IsBlockingWindowOpen() then return end
|
||||
ClamOpener:Open(true)
|
||||
end)
|
||||
end
|
||||
|
||||
addon.Modules.Events:Register("BAG_UPDATE", tryAutoOpen, "ClamOpener")
|
||||
addon.Modules.Events:Register("LOOT_CLOSED", tryAutoOpen, "ClamOpener")
|
||||
addon.Modules.Events:Register("MAIL_CLOSED", tryAutoOpen, "ClamOpener")
|
||||
addon.Modules.Events:Register("TRADE_CLOSED", tryAutoOpen, "ClamOpener")
|
||||
addon.Modules.Events:Register("BANKFRAME_CLOSED", tryAutoOpen, "ClamOpener")
|
||||
end
|
||||
@@ -147,6 +147,12 @@ function DB:Initialize()
|
||||
if Guda_CharDB.settings.autoLockSetItems == nil then
|
||||
Guda_CharDB.settings.autoLockSetItems = true
|
||||
end
|
||||
if Guda_CharDB.settings.autoLoot == nil then
|
||||
Guda_CharDB.settings.autoLoot = false
|
||||
end
|
||||
if Guda_CharDB.settings.autoOpenClams == nil then
|
||||
Guda_CharDB.settings.autoOpenClams = false
|
||||
end
|
||||
|
||||
-- Auto-detect pfUI on first load (theme not yet set)
|
||||
if Guda_CharDB.settings.theme == nil then
|
||||
|
||||
@@ -82,6 +82,16 @@ function Main:Initialize()
|
||||
addon:Error("Tooltip module not loaded!")
|
||||
end
|
||||
|
||||
-- Initialize auto-loot handler (LOOT_OPENED listener)
|
||||
if addon.Modules.AutoLoot and addon.Modules.AutoLoot.Initialize then
|
||||
addon.Modules.AutoLoot:Initialize()
|
||||
end
|
||||
|
||||
-- Initialize clam opener (registers BAG_UPDATE for auto-open)
|
||||
if addon.Modules.ClamOpener and addon.Modules.ClamOpener.Initialize then
|
||||
addon.Modules.ClamOpener:Initialize()
|
||||
end
|
||||
|
||||
-- Setup slash commands
|
||||
Main:SetupSlashCommands()
|
||||
|
||||
@@ -133,6 +143,10 @@ function Main:SetupSlashCommands()
|
||||
-- Sort bank
|
||||
addon.Modules.SortEngine:SortBank()
|
||||
|
||||
elseif msg == "openclams" or msg == "clams" then
|
||||
-- Open all clams in bags
|
||||
addon.Modules.ClamOpener:Open()
|
||||
|
||||
elseif msg == "debug" then
|
||||
-- Toggle debug
|
||||
addon.DEBUG = not addon.DEBUG
|
||||
@@ -255,6 +269,7 @@ function Main:SetupSlashCommands()
|
||||
addon:Print(L["/guda settings - Open settings"])
|
||||
addon:Print(L["/guda sort - Sort bags"])
|
||||
addon:Print(L["/guda sortbank - Sort bank"])
|
||||
addon:Print(L["/guda openclams - Open all clams in bags"])
|
||||
addon:Print(L["/guda track - Toggle item tracking"])
|
||||
addon:Print(L["/guda debug - Toggle debug mode"])
|
||||
addon:Print(L["/guda debugsort - Toggle sort debug output"])
|
||||
|
||||
Reference in New Issue
Block a user