init
This commit is contained in:
337
Bags/Offline.lua
Normal file
337
Bags/Offline.lua
Normal file
@@ -0,0 +1,337 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- S-Frames: Offline DB for Bag Module (Bags/Offline.lua)
|
||||
-- Tracks inventory across characters and realms
|
||||
-- NOTE: This file defines SFrames.Bags first - must be loaded before other Bag files
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
SFrames.Bags = SFrames.Bags or {}
|
||||
SFrames.Bags.Offline = {}
|
||||
|
||||
local offlineFrame = CreateFrame("Frame")
|
||||
offlineFrame:RegisterEvent("PLAYER_LOGIN")
|
||||
offlineFrame:RegisterEvent("BAG_UPDATE")
|
||||
offlineFrame:RegisterEvent("BANKFRAME_OPENED")
|
||||
offlineFrame:RegisterEvent("BANKFRAME_CLOSED")
|
||||
offlineFrame:RegisterEvent("PLAYERBANKSLOTS_CHANGED")
|
||||
offlineFrame:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED")
|
||||
|
||||
local realmName = ""
|
||||
local playerName = ""
|
||||
local isBankOpen = false
|
||||
|
||||
local function QueueBankRefreshScans()
|
||||
local elapsed = 0
|
||||
local index = 1
|
||||
local points = { 0.08, 0.25, 0.55, 0.95 }
|
||||
local t = CreateFrame("Frame")
|
||||
t:SetScript("OnUpdate", function()
|
||||
elapsed = elapsed + (arg1 or 0)
|
||||
if index <= table.getn(points) and elapsed >= points[index] then
|
||||
if isBankOpen then
|
||||
SFrames.Bags.Offline:ScanBags()
|
||||
end
|
||||
index = index + 1
|
||||
end
|
||||
if index > table.getn(points) then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function InitDB()
|
||||
if not SFramesGlobalDB then SFramesGlobalDB = {} end
|
||||
if not SFramesGlobalDB[realmName] then SFramesGlobalDB[realmName] = {} end
|
||||
if not SFramesGlobalDB[realmName][playerName] then
|
||||
SFramesGlobalDB[realmName][playerName] = {
|
||||
bags = {},
|
||||
bank = {},
|
||||
bankSlots = 0,
|
||||
bankBags = {},
|
||||
equippedBags = {},
|
||||
money = 0
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function IsBankBagInvSlot(slotID)
|
||||
return type(slotID) == "number" and slotID > 23
|
||||
end
|
||||
|
||||
local function IsBagItemLink(link)
|
||||
if type(link) ~= "string" or link == "" then
|
||||
return false
|
||||
end
|
||||
local _, _, _, _, _, _, _, equipLoc = GetItemInfo(link)
|
||||
return equipLoc == "INVTYPE_BAG"
|
||||
end
|
||||
|
||||
local function GetBagLinkState(link)
|
||||
if type(link) ~= "string" or link == "" then
|
||||
return "invalid"
|
||||
end
|
||||
local _, _, _, _, _, _, _, equipLoc = GetItemInfo(link)
|
||||
if equipLoc == "INVTYPE_BAG" then
|
||||
return "bag"
|
||||
end
|
||||
if equipLoc == nil or equipLoc == "" then
|
||||
return "unknown"
|
||||
end
|
||||
return "invalid"
|
||||
end
|
||||
|
||||
local function SafeGetInventorySlotByName(slotName)
|
||||
if not GetInventorySlotInfo then return nil end
|
||||
local ok, slotID = pcall(function() return GetInventorySlotInfo(slotName) end)
|
||||
if ok and IsBankBagInvSlot(slotID) then
|
||||
return slotID
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function ResolvePlayerBagInvSlot(index)
|
||||
if type(index) ~= "number" or index < 1 or index > 4 then
|
||||
return nil
|
||||
end
|
||||
|
||||
local liveBtn = _G["CharacterBag" .. (index - 1) .. "Slot"]
|
||||
if liveBtn and liveBtn.GetID then
|
||||
local id = liveBtn:GetID()
|
||||
if type(id) == "number" and id > 0 then
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
if ContainerIDToInventoryID then
|
||||
local ok, slotID = pcall(function() return ContainerIDToInventoryID(index) end)
|
||||
if ok and type(slotID) == "number" and slotID > 0 then
|
||||
return slotID
|
||||
end
|
||||
end
|
||||
|
||||
local slotNames = {
|
||||
"Bag" .. (index - 1) .. "Slot",
|
||||
"Bag" .. index .. "Slot",
|
||||
"CharacterBag" .. (index - 1) .. "Slot",
|
||||
}
|
||||
for _, slotName in ipairs(slotNames) do
|
||||
local ok, slotID = pcall(function() return GetInventorySlotInfo(slotName) end)
|
||||
if ok and type(slotID) == "number" and slotID > 0 then
|
||||
return slotID
|
||||
end
|
||||
end
|
||||
|
||||
-- Vanilla fallback.
|
||||
local fallback = { [1] = 20, [2] = 21, [3] = 22, [4] = 23 }
|
||||
return fallback[index]
|
||||
end
|
||||
|
||||
local function CaptureEquippedBagMeta(db)
|
||||
if not db then return end
|
||||
|
||||
db.equippedBags = {}
|
||||
for index = 1, 4 do
|
||||
local invSlot = ResolvePlayerBagInvSlot(index)
|
||||
local link = nil
|
||||
local texture = nil
|
||||
if invSlot then
|
||||
link = GetInventoryItemLink("player", invSlot)
|
||||
texture = GetInventoryItemTexture("player", invSlot)
|
||||
end
|
||||
|
||||
local size = GetContainerNumSlots(index) or 0
|
||||
db.equippedBags[index] = {
|
||||
link = link,
|
||||
texture = texture,
|
||||
size = tonumber(size) or 0,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function ResolveBankBagInvSlot(index)
|
||||
if type(index) ~= "number" or index <= 0 then return nil end
|
||||
local bagID = index + 4
|
||||
local function TryBankButtonID(buttonID, isBank)
|
||||
if not BankButtonIDToInvSlotID then return nil end
|
||||
local ok, slotID = pcall(function() return BankButtonIDToInvSlotID(buttonID, isBank) end)
|
||||
if ok and IsBankBagInvSlot(slotID) then
|
||||
return slotID
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function AcceptIfBag(slotID)
|
||||
if not IsBankBagInvSlot(slotID) then return nil end
|
||||
local link = GetInventoryItemLink("player", slotID)
|
||||
if link and IsBagItemLink(link) then
|
||||
return slotID
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Guda-style primary mapping
|
||||
local slot = TryBankButtonID(bagID, 1)
|
||||
if slot then return slot end
|
||||
slot = TryBankButtonID(index, 1)
|
||||
if slot then return slot end
|
||||
|
||||
-- Fallbacks: only when they are confirmed bag items
|
||||
slot = TryBankButtonID(bagID, nil)
|
||||
slot = AcceptIfBag(slot)
|
||||
if slot then return slot end
|
||||
|
||||
slot = TryBankButtonID(index, nil)
|
||||
slot = AcceptIfBag(slot)
|
||||
if slot then return slot end
|
||||
|
||||
if ContainerIDToInventoryID then
|
||||
local ok, s = pcall(function() return ContainerIDToInventoryID(bagID) end)
|
||||
if ok then
|
||||
slot = AcceptIfBag(s)
|
||||
if slot then return slot end
|
||||
end
|
||||
end
|
||||
|
||||
slot = AcceptIfBag(SafeGetInventorySlotByName("BankBagSlot" .. index))
|
||||
if slot then return slot end
|
||||
slot = AcceptIfBag(SafeGetInventorySlotByName("BankSlot" .. index))
|
||||
if slot then return slot end
|
||||
|
||||
local liveBtn = _G["BankFrameBag" .. index] or _G["BankFrameBag" .. index .. "Slot"]
|
||||
if liveBtn and liveBtn.GetID then
|
||||
slot = AcceptIfBag(liveBtn:GetID())
|
||||
if slot then return slot end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function CaptureBankBagMeta(db)
|
||||
if not db then return end
|
||||
|
||||
local purchased = (GetNumBankSlots and GetNumBankSlots()) or 0
|
||||
db.bankSlots = tonumber(purchased) or 0
|
||||
db.bankBags = {}
|
||||
|
||||
for index = 1, 7 do
|
||||
local bagID = index + 4
|
||||
local size = GetContainerNumSlots(bagID) or 0
|
||||
local info = {
|
||||
unlocked = (index <= db.bankSlots),
|
||||
size = tonumber(size) or 0,
|
||||
link = nil,
|
||||
texture = nil,
|
||||
}
|
||||
|
||||
if info.unlocked then
|
||||
local invSlot = ResolveBankBagInvSlot(index)
|
||||
if invSlot then
|
||||
local link = GetInventoryItemLink("player", invSlot)
|
||||
local state = GetBagLinkState(link)
|
||||
if (state == "bag") or (state == "unknown" and info.size > 0) then
|
||||
info.link = link
|
||||
info.texture = GetInventoryItemTexture("player", invSlot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
db.bankBags[index] = info
|
||||
end
|
||||
end
|
||||
|
||||
local function ScanContainer(containerID, isBank)
|
||||
if not SFramesGlobalDB or realmName == "" or playerName == "" then return end
|
||||
local db = SFramesGlobalDB[realmName][playerName]
|
||||
local targetDB = isBank and db.bank or db.bags
|
||||
local size = GetContainerNumSlots(containerID)
|
||||
targetDB[containerID] = { size = size, items = {} }
|
||||
for slot = 1, size do
|
||||
local link = GetContainerItemLink(containerID, slot)
|
||||
if link then
|
||||
local texture, itemCount, locked, quality = GetContainerItemInfo(containerID, slot)
|
||||
local _, _, idStr = string.find(link, "item:(%d+):")
|
||||
local itemID = idStr and tonumber(idStr) or nil
|
||||
targetDB[containerID].items[slot] = { id = itemID,
|
||||
link = link,
|
||||
count = itemCount,
|
||||
texture = texture,
|
||||
quality = quality
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SFrames.Bags.Offline:ScanBags()
|
||||
if realmName == "" or playerName == "" then return end
|
||||
InitDB()
|
||||
local db = SFramesGlobalDB[realmName] and SFramesGlobalDB[realmName][playerName]
|
||||
if not db then return end
|
||||
|
||||
for bag = 0, 4 do
|
||||
ScanContainer(bag, false)
|
||||
end
|
||||
CaptureEquippedBagMeta(db)
|
||||
if isBankOpen then
|
||||
ScanContainer(-1, true)
|
||||
for bag = 5, 11 do
|
||||
ScanContainer(bag, true)
|
||||
end
|
||||
CaptureBankBagMeta(db)
|
||||
end
|
||||
db.money = GetMoney()
|
||||
end
|
||||
|
||||
local scanPending = false
|
||||
local scanTimer = CreateFrame("Frame")
|
||||
scanTimer:Hide()
|
||||
local function RequestBagScan()
|
||||
if scanPending then return end
|
||||
scanPending = true
|
||||
scanTimer.elapsed = 0
|
||||
scanTimer:Show()
|
||||
scanTimer:SetScript("OnUpdate", function()
|
||||
this.elapsed = this.elapsed + arg1
|
||||
if this.elapsed > 0.5 then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this:Hide()
|
||||
scanPending = false
|
||||
SFrames.Bags.Offline:ScanBags()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
offlineFrame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_LOGIN" then
|
||||
realmName = GetRealmName() or ""
|
||||
playerName = UnitName("player") or ""
|
||||
InitDB()
|
||||
SFrames.Bags.Offline:ScanBags()
|
||||
elseif event == "BAG_UPDATE" or event == "PLAYERBANKSLOTS_CHANGED" or event == "PLAYERBANKBAGSLOTS_CHANGED" then
|
||||
RequestBagScan()
|
||||
elseif event == "BANKFRAME_OPENED" then
|
||||
isBankOpen = true
|
||||
RequestBagScan()
|
||||
QueueBankRefreshScans()
|
||||
elseif event == "BANKFRAME_CLOSED" then
|
||||
isBankOpen = false
|
||||
end
|
||||
end)
|
||||
|
||||
function SFrames.Bags.Offline:GetCharacterList()
|
||||
if not SFramesGlobalDB or realmName == "" then return {} end
|
||||
local chars = {}
|
||||
if SFramesGlobalDB[realmName] then
|
||||
for name in pairs(SFramesGlobalDB[realmName]) do
|
||||
table.insert(chars, name)
|
||||
end
|
||||
end
|
||||
return chars
|
||||
end
|
||||
|
||||
function SFrames.Bags.Offline:GetCharacterData(name)
|
||||
if not SFramesGlobalDB or realmName == "" then return nil end
|
||||
return SFramesGlobalDB[realmName] and SFramesGlobalDB[realmName][name]
|
||||
end
|
||||
|
||||
function SFrames.Bags.Offline:GetCurrentPlayerName()
|
||||
return playerName
|
||||
end
|
||||
Reference in New Issue
Block a user