136 lines
4.6 KiB
Lua
136 lines
4.6 KiB
Lua
-- Adapted from AoELoot by Sandrea / ChatGPT for ShaguTweaks-ClassicAPI.
|
|
|
|
local T = ShaguTweaks.T
|
|
|
|
local module = ShaguTweaks:register({
|
|
title = T["AoE Loot"],
|
|
description = T["Automatically loots all nearby corpses using ClassicAPI without confirmation prompts. Leaves Master Loot untouched."],
|
|
expansions = { ["vanilla"] = true },
|
|
category = T["Loot"],
|
|
enabled = nil,
|
|
})
|
|
|
|
module.enable = function(self)
|
|
local frame = CreateFrame("Frame", "ShaguTweaksAoELoot")
|
|
local pending = false
|
|
local releaseDeadline = 0
|
|
local containerLootDeadline = 0
|
|
|
|
local function TrackContainerUse(bag, slot)
|
|
if not C_Container or not C_Container.IsContainerItemOpenable then return end
|
|
|
|
local isOpenable, canOpen = C_Container.IsContainerItemOpenable(bag, slot)
|
|
if isOpenable and canOpen then
|
|
-- LOOT_OPENED is also fired by clams, lockboxes and similar bag items.
|
|
-- Mark that session before UseContainerItem runs so it remains under the
|
|
-- normal client handling and the consumed container can leave the bag.
|
|
containerLootDeadline = GetTime() + 3
|
|
end
|
|
end
|
|
|
|
-- Use the ShaguTweaks hook explicitly because its prepend mode records the
|
|
-- bag item before UseContainerItem can synchronously fire LOOT_OPENED.
|
|
ShaguTweaks.hooksecurefunc("UseContainerItem", TrackContainerUse, true)
|
|
|
|
local function IsMasterLootActive()
|
|
if not GetLootMethod then return false end
|
|
return GetLootMethod() == "master"
|
|
end
|
|
|
|
local function CanStartAoELoot()
|
|
if IsMasterLootActive() then return false end
|
|
if not C_Loot or not C_Loot.LootAllCorpses then return false end
|
|
|
|
if C_Loot.IsScanInProgress and C_Loot.IsScanInProgress() then
|
|
return false
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function HasNearbyLootableCorpse()
|
|
-- Older ClassicAPI builds may not expose the query yet. Keep AoE Loot
|
|
-- functional there; the container hook above still protects bag items.
|
|
if not C_Loot or not C_Loot.GetNearbyLootableUnits then return true end
|
|
|
|
local units = C_Loot.GetNearbyLootableUnits()
|
|
return type(units) == "table" and table.getn(units) > 0
|
|
end
|
|
|
|
local function StartAoELoot()
|
|
-- Defer for one frame so the normal loot session can close cleanly.
|
|
frame:SetScript("OnUpdate", nil)
|
|
pending = false
|
|
releaseDeadline = 0
|
|
|
|
if CanStartAoELoot() then
|
|
C_Loot.LootAllCorpses()
|
|
end
|
|
end
|
|
|
|
local function QueueAoELoot()
|
|
releaseDeadline = 0
|
|
frame:SetScript("OnUpdate", StartAoELoot)
|
|
end
|
|
|
|
local function WaitForLootRelease()
|
|
-- Native Auto Loot can finish before this module receives LOOT_OPENED.
|
|
-- In that case LOOT_CLOSED has already passed, so use the empty session
|
|
-- as a fallback and queue the ClassicAPI walk on the following frame.
|
|
if pending and (not GetNumLootItems or GetNumLootItems() == 0) then
|
|
QueueAoELoot()
|
|
elseif pending and GetTime() >= releaseDeadline then
|
|
-- Never leave a per-frame waiter behind if the native loot session
|
|
-- cannot finish, for example because every inventory bag is full.
|
|
pending = false
|
|
releaseDeadline = 0
|
|
frame:SetScript("OnUpdate", nil)
|
|
end
|
|
end
|
|
|
|
frame:RegisterEvent("LOOT_OPENED")
|
|
frame:RegisterEvent("LOOT_CLOSED")
|
|
|
|
frame:SetScript("OnEvent", function()
|
|
if event == "LOOT_OPENED" then
|
|
-- Inventory containers use the same loot events as corpses. Let the
|
|
-- normal client finish them instead of closing their loot session.
|
|
if containerLootDeadline > 0 then
|
|
local isContainerLoot = GetTime() <= containerLootDeadline
|
|
containerLootDeadline = 0
|
|
if isContainerLoot then return end
|
|
end
|
|
|
|
-- The master looter must keep the normal window to inspect and assign loot.
|
|
if IsMasterLootActive() then
|
|
pending = false
|
|
releaseDeadline = 0
|
|
frame:SetScript("OnUpdate", nil)
|
|
return
|
|
end
|
|
|
|
-- Chests, fishing nodes and other non-corpse sources also emit
|
|
-- LOOT_OPENED. AoE Loot only has work to do when ClassicAPI can actually
|
|
-- see at least one nearby lootable unit.
|
|
if not HasNearbyLootableCorpse() then return end
|
|
|
|
if pending or not CanStartAoELoot() then return end
|
|
|
|
pending = true
|
|
releaseDeadline = GetTime() + 3
|
|
frame:SetScript("OnUpdate", WaitForLootRelease)
|
|
|
|
-- Close the normal session so ClassicAPI can take over. The temporary
|
|
-- waiter also handles clients whose native Auto Loot already completed
|
|
-- and fired LOOT_CLOSED before this module received LOOT_OPENED.
|
|
CloseLoot()
|
|
|
|
return
|
|
end
|
|
|
|
if event == "LOOT_CLOSED" and pending then
|
|
QueueAoELoot()
|
|
end
|
|
end)
|
|
end
|