You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1556 lines
54 KiB

local _, S = ...
local pairs, ipairs, string, type, time = pairs, ipairs, string, type, time
local GetContainerItemLink = GetContainerItemLink
if C_Container then
GetContainerItemLink = C_Container.GetContainerItemLink
end
local GetItemInfoInstant, GetItemInfo, GetItemClassInfo, GetItemSubClassInfo, GetDetailedItemLevelInfo, IsEquippableItem = GetItemInfoInstant, GetItemInfo, GetItemClassInfo, GetItemSubClassInfo, GetDetailedItemLevelInfo, IsEquippableItem
if C_Item then
GetItemInfoInstant, GetItemInfo, GetItemClassInfo, GetItemSubClassInfo, GetDetailedItemLevelInfo, IsEquippableItem = C_Item.GetItemInfoInstant, C_Item.GetItemInfo, C_Item.GetItemClassInfo, C_Item.GetItemSubClassInfo, C_Item.GetDetailedItemLevelInfo, C_Item.IsEquippableItem
end
S.CONTAINER_TYPES = {}
S.CONTAINER_TYPES.ALL = "ALL"
S.CONTAINER_TYPES.BAGS = "BAGS"
S.CONTAINER_TYPES.REAGENT_BAGS = "REAGENT_BAGS"
S.CONTAINER_TYPES.BANK = "BANK"
S.CONTAINER_TYPES.KEYRING = "KEYRING"
S.CONTAINER_TYPES.REAGENT = "REAGENT"
S.CONTAINER_TYPES.ACCOUNT = "ACCOUNT"
S.Utils = {}
-- Use new APIs
local ContainerIDToInventoryID, GetContainerItemInfo, PickupContainerItem, GetContainerNumSlots, GetContainerNumFreeSlots = ContainerIDToInventoryID, GetContainerItemInfo, PickupContainerItem, GetContainerNumSlots, GetContainerNumFreeSlots
local UseContainerItem = UseContainerItem
local useNewContainerInfo = false
if C_Container then
if C_Container.ContainerIDToInventoryID then ContainerIDToInventoryID = C_Container.ContainerIDToInventoryID end
if C_Container.GetContainerItemInfo then
GetContainerItemInfo = C_Container.GetContainerItemInfo
useNewContainerInfo = true
end
if C_Container.PickupContainerItem then PickupContainerItem = C_Container.PickupContainerItem end
if C_Container.GetContainerNumSlots then GetContainerNumSlots = C_Container.GetContainerNumSlots end
if C_Container.GetContainerNumFreeSlots then GetContainerNumFreeSlots = C_Container.GetContainerNumFreeSlots end
if C_Container.UseContainerItem then UseContainerItem = C_Container.UseContainerItem end
end
function S.Utils.GetContainerNumSlots(containerID)
-- Fix bug in SoD where it's returning 24 for some reason
if containerID == BANK_CONTAINER and GetContainerNumSlots(containerID) < 28 then
return 28
end
return GetContainerNumSlots(containerID)
end
S.Utils.GetContainerNumFreeSlots = GetContainerNumFreeSlots
5 months ago
function S.Utils.FetchNextPurchasableBankTabData(bankType)
if C_Bank.FetchNextPurchasableBankTabData then
return C_Bank.FetchNextPurchasableBankTabData(bankType)
else
return {["tabCost"] = C_Bank.FetchNextPurchasableBankTabCost(bankType)}
end
end
-- S.WoWVersion() returns the major WoW version number, Classic: 1, TBC: 2, WotlK: 3, etc...
3 years ago
local WoWMajorVersionNumber = tonumber(string.sub(GetBuildInfo(), 1, 2))
function S.WoWVersion()
3 years ago
return WoWMajorVersionNumber
end
5 months ago
local useNewBank = true
if NUM_BANKBAGSLOTS then useNewBank = false end
function S.UseNewBank()
return useNewBank
end
-- Returns the number of bags that can be equipped
function S.Utils.GetMaxNumBags()
return NUM_BAG_SLOTS
--[[if S.WoWVersion() < 10 then
return 4
else
return 5
end]]
end
-- Constant. Maximum number of categories the player can create
local NUM_MAX_CATEGORIES = 36
function S.Utils.GetMaxNumCategories()
return NUM_MAX_CATEGORIES
end
3 years ago
-- Constant. Size of the largest bag in WoW
local MAX_BAG_SLOTS = 36
function S.Utils.MaxBagSlots()
return MAX_BAG_SLOTS
end
-- Dealing with other bag frames
--[[local killableFramesParent = CreateFrame("FRAME")
killableFramesParent:Hide()
local function ClearPoints(frame)
local children = { frame:GetChildren() }
for k,v in pairs(children) do
ClearPoints(v)
end
frame:SetParent(killableFramesParent)
frame:ClearAllPoints()
end
local function KillFrame(frame)
frame:SetScript("OnShow", nil)
frame:SetScript("OnHide", nil)
frame:SetScript("OnEnter", nil)
frame:SetScript("OnLeave", nil)
--frame:UnregisterAllEvents()
ClearPoints(frame)
end
local framesToKill = {}
for i = 1, NUM_CONTAINER_FRAMES do
framesToKill[#framesToKill + 1] = _G["ContainerFrame"..i]
end
framesToKill[#framesToKill + 1] = _G["BankFrame"]
if ContainerFrameCombinedBags then
framesToKill[#framesToKill + 1] = ContainerFrameCombinedBags
end
function S.Utils.KillBlizzBags()
for _, frame in pairs(framesToKill) do
KillFrame(frame)
end
end
-- Add bag frames from other addons and kill them all after PLAYER_LOGIN
local eventHandlerFrame2 = CreateFrame("FRAME")
eventHandlerFrame2:RegisterEvent("PLAYER_LOGIN")
eventHandlerFrame2:SetScript("OnEvent", function(self)
if _G["ElvUI_ContainerFrame"] then
framesToKill[#framesToKill + 1] = _G["ElvUI_ContainerFrame"]
framesToKill[#framesToKill + 1] = _G["ElvUI_BankContainerFrame"]
end
if _G["GwBagFrame"] then
framesToKill[#framesToKill + 1] = _G["GwBagFrame"]
framesToKill[#framesToKill + 1] = _G["GwBankFrame"]
end
S.Utils.KillBlizzBags()
end)]]
-- Basic event handling for custom events
--[[ Events
--------------
EnteredWorld: fired after PLAYER_ENTERING_WORLD
Resizing: fired every time the window changes size
Resized: fired when window is finished resizing
Moved: fired when window is finished moving
SettingChanged-<setting>: Parameters: (Value)
FontChanged: fired after any changes are applied to the font object
CategorySelected: fired when the selected category changes
SortingChanged: fired when the sorting method changes, like when a column heading is clicked
BagsUpdated: fired when the contents of a bag has changed
CurrenciesUpdated: fired when the quantity of a currency changes
PrimaryFrameOpened
PrimaryFrameClosed
BankOpened: fired after the bank is opened and the data of its bag contents have been updated
BankClosed: fired after the bank is closed
SearchChanged
CharacterSelected
ColumnsChanged: fired when a column is enabled or disabled
FavoriteChanged: fired when an item or currency has its marker icon changed
ReagentsPurchased
LayoutChanged: fired when a list changes to grid view, or vice versa
EquipmentSetsUpdated
GuildBankUpdated: fired each time a guild bank tab is updated
GuildBankUpdatedFull: fired after all 8 tabs have been updated
ProfileChanged: fired when the character switches settings profile
]]
local registeredEvents = {}
function S.Utils.RunOnEvent(self, event, func)
if not registeredEvents[event] then
registeredEvents[event] = {}
end
table.insert(registeredEvents[event], {
["func"] = func,
["self"] = self
})
end
-- Run all functions registered with an event. Extra parameters a,b,c can be passed to the function
function S.Utils.TriggerEvent(event, a, b, c)
if registeredEvents[event] then
for k,v in pairs(registeredEvents[event]) do
v.func(v.self, event, a, b, c)
end
end
end
-- Dealing with other bag frames
5 months ago
local function NoneFunc() end
function S.Utils.KillFrame(f)
if f.ClearAllPoints then
f:ClearAllPoints()
f:SetPoint("TOPLEFT", -10000, -10000)
f:SetPoint("BOTTOMRIGHT", -10000, -10000)
end
if f.UnregisterAllEvents then
f:UnregisterAllEvents()
f.OnShow = NoneFunc
f:SetScript("OnShow", NoneFunc)
f.OnHide = NoneFunc
f:SetScript("OnHide", NoneFunc)
f.OnEnter = NoneFunc
f:SetScript("OnEnter", NoneFunc)
f.OnLeave = NoneFunc
f:SetScript("OnLeave", NoneFunc)
f.OnEvent = NoneFunc
f:SetScript("OnEvent", NoneFunc)
end
if f.GetNumRegions then
for _, c in pairs(f) do
if c and type(c) == "table" then
S.Utils.KillFrame(c)
end
end
end
end
-- Make killable frames children of 'killableFramesParent', which can be hidden or shown to enable or disable the killable frames
local killableFramesParent = CreateFrame("FRAME", nil, UIParent)
killableFramesParent:SetAllPoints()
killableFramesParent:SetFrameStrata("HIGH")
killableFramesParent:Hide()
local function MakeFrameKillable(frame)
frame:SetParent(killableFramesParent)
end
local killedFramesParent = CreateFrame("FRAME", nil, UIParent)
killedFramesParent:SetAllPoints()
killedFramesParent:Hide()
local function KillFramePermanently(frame)
frame:SetParent(killedFramesParent)
end
local function MakeBlizzBagsKillable()
if _G["ElvUI_ContainerFrame"] then
MakeFrameKillable(_G["ElvUI_ContainerFrame"])
MakeFrameKillable(_G["ElvUI_BankContainerFrame"])
-- Get rid of blizz bags permanently, since they are replaced by ElvUI
if _G["ContainerFrameCombinedBags"] then
KillFramePermanently(_G["ContainerFrameCombinedBags"])
end
for i = 1, NUM_CONTAINER_FRAMES do
KillFramePermanently(_G["ContainerFrame"..i])
end
5 months ago
if not S.UseNewBank() then
KillFramePermanently(_G["BankFrame"])
end
else
if _G["ContainerFrameCombinedBags"] then
MakeFrameKillable(_G["ContainerFrameCombinedBags"])
end
for i = 1, NUM_CONTAINER_FRAMES do
MakeFrameKillable(_G["ContainerFrame"..i])
end
5 months ago
if not S.UseNewBank() then
MakeFrameKillable(_G["BankFrame"])
end
end
if _G["GwBagFrame"] then
MakeFrameKillable(_G["GwBagFrame"])
MakeFrameKillable(_G["GwBankFrame"])
end
end
local function MakeBlizzVoidStorageKillable()
MakeFrameKillable(_G["VoidStorageFrame"])
end
function S.Utils.KillBlizzBags()
S.Enable()
killableFramesParent:Hide()
end
function S.Utils.ResurrectBlizzBags()
5 months ago
--[[S.Disable()
killableFramesParent:Show()]]
end
function S.Utils.ToggleBlizzBags()
5 months ago
--[[if killableFramesParent:IsShown() then
S.Utils.KillBlizzBags()
else
S.Utils.ResurrectBlizzBags()
5 months ago
end]]
end
local function CreateToggleButton(parent)
if parent.ClickableTitleFrame then
parent.ClickableTitleFrame:Hide()
end
if parent.TitleContainer then
parent.TitleContainer:Hide()
end
local button = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
button:SetFrameLevel(parent:GetFrameLevel() + 520)
button.text = button:CreateTexture()
button.text:SetPoint("CENTER", 4, -1)
button.text:SetSize(60, 20)
button.text:SetTexCoord(0, 0.75, 0, 1)
button.text:SetTexture("Interface\\Addons\\Sorted\\Textures\\title-no-bg")
button:SetSize(64, 24)
button:HookScript("OnMouseDown", function(self)
self.text:SetPoint("CENTER", 3, -2)
self.text:SetAlpha(0.75)
end)
button:HookScript("OnMouseUp", function(self)
self.text:SetPoint("CENTER", 4, -1)
self.text:SetAlpha(1)
end)
button:HookScript("OnEnter", function(self)
S.Tooltip.CreateLocalized(self, "ANCHOR_RIGHT", "TOOLTIP_TOGGLE_SORTED")
end)
button:HookScript("OnLeave", function(self)
S.Tooltip.Cancel()
end)
button:HookScript("OnClick", function(self)
S.Utils.ToggleBlizzBags()
CloseAllBags()
OpenAllBags()
end)
return button
end
local function CreateBlizzToggleButtons()
local f = _G["ContainerFrame1"]
f.SortedButton = CreateToggleButton(f)
if S.WoWVersion() <= 3 then
_G["ContainerFrame1Name"]:Hide()
f.SortedButton:SetPoint("TOPRIGHT", -26, -6)
else
f.SortedButton:SetPoint("TOPRIGHT", -22, -1)
end
f.SortedButton:SetHeight(20)
if _G["ContainerFrameCombinedBags"] then
f = _G["ContainerFrameCombinedBags"]
f.SortedButton = CreateToggleButton(f)
f.SortedButton:SetPoint("TOPRIGHT", -22, -1)
f.SortedButton:SetHeight(20)
end
if _G["ElvUI_ContainerFrame"] then
f = _G["ElvUI_ContainerFrame"]
f.SortedButton = CreateToggleButton(f)
f.SortedButton:SetPoint("TOPLEFT", 2, -2)
f.SortedButton:SetSize(64, 22)
if S.Skinning.GetSkin() == S.Skinning.ADDONSKINS then
S.AS:SkinButton(f.SortedButton)
f.SortedButton.text:SetTexture("Interface\\Addons\\Sorted\\Textures\\Title-Flat")
end
end
end
S.Utils.RunOnEvent(nil, "EnteredWorld", function()
5 months ago
--CreateBlizzToggleButtons() - Disabled since 11.2, due to changes with the bank frame
MakeBlizzBagsKillable()
S.Utils.KillBlizzBags()
end)
local eventHandlerFrame_MakeAddonKillable = CreateFrame("FRAME")
eventHandlerFrame_MakeAddonKillable:RegisterEvent("ADDON_LOADED")
eventHandlerFrame_MakeAddonKillable:SetScript("OnEvent", function(self, event, addon)
if addon == "Blizzard_VoidStorageUI" then
MakeBlizzVoidStorageKillable()
end
end)
-- Shared media
local LSM = LibStub("LibSharedMedia-3.0")
LSM:Register(LSM.MediaType.BACKGROUND, "Sorted Original", "Interface\\Addons\\Sorted\\Textures\\UI-Background")
LSM:Register(LSM.MediaType.BACKGROUND, "Sorted Abstract", "Interface\\Addons\\Sorted\\Textures\\Abstract")
LSM:Register(LSM.MediaType.BACKGROUND, "Sorted Abstract Light", "Interface\\Addons\\Sorted\\Textures\\Abstract-Light")
LSM:Register(LSM.MediaType.BACKGROUND, "Solid", "Interface\\Addons\\Sorted\\Textures\\Solid")
LSM:Register(LSM.MediaType.BACKGROUND, "Necrolord", "Interface\\FrameGeneral\\UIFrameNecrolordBackground")
LSM:Register(LSM.MediaType.BACKGROUND, "Night Fae", "Interface\\FrameGeneral\\UIFrameNightFaeBackground")
LSM:Register(LSM.MediaType.BACKGROUND, "Oribos", "Interface\\FrameGeneral\\UIFrameOribosBackground")
LSM:Register(LSM.MediaType.BACKGROUND, "Venthyr", "Interface\\FrameGeneral\\UIFrameVenthyrBackground")
LSM:Register(LSM.MediaType.BACKGROUND, "Kyrian", "Interface\\FrameGeneral\\UIFrameKyrianBackground")
LSM:Register(LSM.MediaType.BACKGROUND, "Dragonflight", "Interface\\FrameGeneral\\UIFrameDragonflightBackground")
LSM:Register(LSM.MediaType.BACKGROUND, "Rock", "Interface\\FrameGeneral\\UI-Background-Rock")
LSM:Register(LSM.MediaType.BACKGROUND, "Bank", "Interface\\BankFrame\\Bank-Background")
LSM:Register(LSM.MediaType.STATUSBAR, "Sorted", "Interface\\Addons\\Sorted\\Textures\\UI-Highlight")
LSM:Register(LSM.MediaType.FONT, "Metamorphous", "Interface\\Addons\\Sorted\\Textures\\Metamorphous-Regular.ttf")
LSM:Register(LSM.MediaType.FONT, "Open Sans", "Interface\\Addons\\Sorted\\Textures\\OpenSans-Regular.ttf")
LSM:Register(LSM.MediaType.FONT, "Open Sans Condensed", "Interface\\Addons\\Sorted\\Textures\\OpenSans-CondBold.ttf")
LSM:Register(LSM.MediaType.FONT, "Open Sans Light", "Interface\\Addons\\Sorted\\Textures\\OpenSans-Light.ttf")
LSM:Register(LSM.MediaType.FONT, "Open Sans Semibold", "Interface\\Addons\\Sorted\\Textures\\OpenSans-Semibold.ttf")
LSM:Register(LSM.MediaType.FONT, "Open Sans Bold", "Interface\\Addons\\Sorted\\Textures\\OpenSans-Bold.ttf")
LSM:Register(LSM.MediaType.FONT, "Semplicita", "Interface\\Addons\\Sorted\\Textures\\SemplicitaPro-Medium.ttf")
LSM:Register(LSM.MediaType.FONT, "Semplicita Light", "Interface\\Addons\\Sorted\\Textures\\SemplicitaPro-Regular.ttf")
LSM:Register(LSM.MediaType.FONT, "Semplicita Semibold", "Interface\\Addons\\Sorted\\Textures\\SemplicitaPro-Semibold.ttf")
LSM:Register(LSM.MediaType.FONT, "Semplicita Bold", "Interface\\Addons\\Sorted\\Textures\\SemplicitaPro-Bold.ttf")
LSM:Register(LSM.MediaType.FONT, "Bliz Quadrata", "Interface\\Addons\\Sorted\\Textures\\Bliz-Quadrata.ttf")
LSM:Register(LSM.MediaType.FONT, "Segoe UI", "Interface\\Addons\\Sorted\\Textures\\segoeui.ttf")
LSM:Register(LSM.MediaType.FONT, "Segoe UI Light", "Interface\\Addons\\Sorted\\Textures\\segoeuisl.ttf")
LSM:Register(LSM.MediaType.FONT, "Segoe UI Semibold", "Interface\\Addons\\Sorted\\Textures\\seguisb.ttf")
LSM:Register(LSM.MediaType.FONT, "Segoe UI Bold", "Interface\\Addons\\Sorted\\Textures\\segoeuib.ttf")
function S.Utils.GetFonts()
return LSM:List("font")
end
function S.Utils.GetFontPath(font)
return LSM:Fetch("font", font)
end
function S.Utils.GetBackgrounds()
return LSM:List(LSM.MediaType.BACKGROUND)
end
function S.Utils.GetBackgroundPath(bg)
return LSM:Fetch(LSM.MediaType.BACKGROUND, bg)
end
function S.Utils.GetItemQualityName(quality)
return _G["ITEM_QUALITY"..quality.."_DESC"]
end
-- COLORS AND TEXT FORMATTING
local SORTED_ITEM_QUALITY_COLORS = {
[0] = CreateColor(0.6, 0.6, 0.6), --Poor
[1] = CreateColor(0.95, 0.95, 0.95), --Common
[2] = CreateColor(0.3, 1, 0.1), --Uncommon
[3] = CreateColor(0, 0.7, 1), --Rare
[4] = CreateColor(0.9, 0.3, 1), --Epic
[5] = CreateColor(1, 0.5, 0.1), --Legendary
[6] = CreateColor(1, 0.85, 0.55), --Artifact
[7] = CreateColor(0.15, 1, 1), --Heirloom
[8] = CreateColor(1, 0.8, 0)--WoWToken
}
function S.Utils.GetItemQualityColor(quality)
if not SORTED_ITEM_QUALITY_COLORS[quality] then
return SORTED_ITEM_QUALITY_COLORS[1]
end
return SORTED_ITEM_QUALITY_COLORS[quality]
--return GetItemQualityColor(quality)
end
local SORTED_ITEM_QUALITY_HIGHLIGHT_COLORS = {
[0] = CreateColor(0.8,0.8,0.8), -- Poor
[1] = CreateColor(1,1,1), -- Common
[2] = CreateColor(0.8,1,0.5), -- Uncommon
[3] = CreateColor(0.4,0.9,1), -- Rare
[4] = CreateColor(1,0.6,1), -- Epic
[5] = CreateColor(1,0.8,0.4), -- Legendary
[6] = CreateColor(1,0.97,0.8), -- Artifact
[7] = CreateColor(0.6,1,1), -- Heirloom
[8] = CreateColor(1,0.8,0) -- WoW Token
}
function S.Utils.GetItemQualityHighlightColor(quality)
if not SORTED_ITEM_QUALITY_HIGHLIGHT_COLORS[quality] then
return SORTED_ITEM_QUALITY_HIGHLIGHT_COLORS[1]
end
return SORTED_ITEM_QUALITY_HIGHLIGHT_COLORS[quality]
end
local SORTED_ITEM_QUALITY_GREYED_COLORS = {}
for k,v in pairs(SORTED_ITEM_QUALITY_COLORS) do
SORTED_ITEM_QUALITY_GREYED_COLORS[k] = CreateColor(
v.r * 0.07 + 0.16,
v.g * 0.07 + 0.16,
v.b * 0.07 + 0.16
)
end
function S.Utils.GetItemQualityGreyedColor(quality)
if not SORTED_ITEM_QUALITY_GREYED_COLORS[quality] then
return SORTED_ITEM_QUALITY_GREYED_COLORS[1]
end
return SORTED_ITEM_QUALITY_GREYED_COLORS[quality]
end
local buttonTextColor = CreateColor(1, 0.8, 0)
function S.Utils.GetButtonTextColor()
return buttonTextColor
end
-- SEARCHING (used to filter the Name attribute in custom categories)
-- Returns true if 'text' contains any of the words in 'search'
function S.Utils.BasicTextSearchAnyWord(search, text)
for s in search:lower():gmatch("([^%s]+)") do
if string.find(text:lower():gsub("%s", ""), s) then
return true
end
end
return false
end
-- Makes lowercase, no punctuation, no whitespace
local function StripString(s)
return s:lower():gsub("[%s%p]", "")
end
-- Returns true if 'text' contains the entire string 'search' (after stripping)
function S.Utils.BasicTextSearchFullString(search, text)
search = StripString(search)
text = StripString(text)
if text == search or text:find(search) then
return true
end
return false
end
-- Returns true if 'text' contains ALL of the words in 'search'
function S.Utils.BasicTextSearch(search, text)
for s in search:lower():gmatch("([^%s%p]+)") do
if not string.find(StripString(text), s) then
return false
end
end
return true
end
5 months ago
function S.Utils.FormatValueString(value, extraPrecision)
if extraPrecision then
if value < 10000 then
return GetMoneyString(value)
elseif value < 10000000 then
return GetMoneyString(math.floor(value / 100) * 100)
elseif value < 1000000000 then
return GetMoneyString(math.floor(value / 10000) * 10000)
else
local n = math.floor(value / 10000000) * 10000
local s = ""..n
n = GetMoneyString(n)
return n:sub(1, #s-4).."K"..n:sub(#s-3)
end
end
if value < 100 then
return GetMoneyString(value)
elseif value < 10000 then
return GetMoneyString(math.floor(value / 100) * 100)
elseif value < 10000000 then
return GetMoneyString(math.floor(value / 10000) * 10000)
else
local n = math.floor(value / 10000000) * 10000
local s = ""..n
n = GetMoneyString(n)
return n:sub(1, #s-4).."K"..n:sub(#s-3)
end
end
function S.Utils.FormatValueStringNoIcon(value)
if value < 100 then
return value
elseif value < 10000 then
return math.floor(value / 100)
else
return S.Utils.FormatBigNumber(math.floor(value / 10000))
end
end
function S.Utils.GetValueIcon(value)
if value < 100 then
return "Interface\\MONEYFRAME\\UI-CopperIcon"
elseif value < 10000 then
return "Interface\\MONEYFRAME\\UI-SilverIcon"
else
return "Interface\\MONEYFRAME\\UI-GoldIcon"
end
end
function S.Utils.FormatBigNumber(n)
if n < 1000 then
return n
elseif n < 10000 then
local s = tostring(n)
return s:sub(1,1).."."..s:sub(2,2).."K"
elseif n < 1000000 then
local s = tostring(floor(n / 1000))
return s.."K"
elseif n < 10000000 then
local s = tostring(floor(n / 1000))
return s:sub(1,1).."."..s:sub(2,2).."M"
elseif n < 1000000000 then
local s = tostring(floor(n / 1000000))
return s.."M"
-- In case some crazy person gets a billion of some currency
elseif n < 10000000000 then
local s = tostring(floor(n / 1000000))
return s:sub(1,1).."."..s:sub(2,2).."B"
else
local s = tostring(floor(n / 1000000000))
return s.."B"
end
end
-- Has slightly more precision
function S.Utils.FormatNumber(n)
if n < 1000 then
return n
elseif n < 10000 then
local s = tostring(n)
return s:sub(1,1)..","..s:sub(2)
elseif n < 100000 then
local s = tostring(n)
return s:sub(1,2).."."..s:sub(3,3).."K"
elseif n < 1000000 then
local s = tostring(n)
return s:sub(1,3).."K"
elseif n < 10000000 then
local s = tostring(n)
return s:sub(1,1).."."..s:sub(2,3).."M"
elseif n < 100000000 then
local s = tostring(n)
return s:sub(1,2).."."..s:sub(3,3).."M"
elseif n < 1000000000 then
local s = tostring(n)
return s:sub(1,3).."M"
elseif n < 10000000000 then
local s = tostring(n)
return s:sub(1,1).."."..s:sub(2,3).."B"
else
local s = tostring(floor(n / 1000000000))
return s.."B"
end
end
f = S.Utils.FormatNumber
local valueColors = {
[1] = CreateColor(0.8, 0.45, 0.2),
[2] = CreateColor(0.7, 0.7, 0.7),
[3] = CreateColor(0.9, 0.75, 0.2),
[4] = CreateColor(1, 0.7, 0.2),
[5] = CreateColor(1, 0.6, 0.1)
}
function S.Utils.GetValueColor(value)
if value < 100 then return valueColors[1]
elseif value < 10000 then return valueColors[2]
elseif value < 10000000 then return valueColors[3]
elseif value < 10000000000 then return valueColors[4]
else return valueColors[5]
end
end
function S.Utils.GetClassColor(class)
return (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[class]
end
function S.Utils.GetClassHexColor(class)
return (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[class]:GenerateHexColorMarkup()
end
function S.Utils.GetRaceName(raceID)
if type(raceID) == "number" then
return C_CreatureInfo.GetRaceInfo(raceID).raceName
else
return raceID
end
end
function S.Utils.FormatFaction(faction, size)
if not size then
size = 20
end
if faction == "Alliance" then
return "|TInterface\\FriendsFrame\\PlusManz-Alliance:"..size..":"..size.."|t"
elseif faction == "Horde" then
return "|TInterface\\FriendsFrame\\PlusManz-Horde:"..size..":"..size.."|t"
else
return "|TInterface\\Addons\\Sorted\\Textures\\Pandaren-Logo:"..size..":"..size.."|t"
end
end
local expansionColors = {
[0] = "|cffFFBB3D", -- wow
[1] = "|cff95C96A", -- tbc
[2] = "|cff45D8FF", -- wrath
[3] = "|cffFF7727", -- cata
[4] = "|cff35FFA3", -- mop
[5] = "|cffE24E21", -- wod
[6] = "|cffB4FF55", -- legion
[7] = "|cff9EBBEE", -- bfa
[8] = "|cffFDFFFB", -- sl
[9] = "|cffFFCC88", -- df
[10] = "|cfff59939", -- tww
[11] = "|cff5b63c7", -- mn
[12] = "|cfff4ca5e", -- titan
}
function S.Utils.ExpacColor(expacID)
return expansionColors[expacID]
end
function S.Utils.FormatExpac(expacID, width, height)
if not expacID then
expacID = 0
end
local x = (expacID % 4) * 64
local y = math.floor(expacID / 4) * 32
if width and height then
return string.format("|TInterface\\Addons\\Sorted\\Textures\\Expac-Icons:%d:%d:0:0:256:256:%d:%d:%d:%d|t", height, width, x, x+64, y, y+32)
else
return string.format("|TInterface\\Addons\\Sorted\\Textures\\Expac-Icons:%d:%d:0:0:256:256:%d:%d:%d:%d|t", 32, 64, x, x+64, y, y+32)
end
end
local expansionStrings = {
[0] = "SUBFILTER_EXPANSION_WOW",
[1] = "SUBFILTER_EXPANSION_TBC",
[2] = "SUBFILTER_EXPANSION_WOTLK",
[3] = "SUBFILTER_EXPANSION_CATA",
[4] = "SUBFILTER_EXPANSION_MOP",
[5] = "SUBFILTER_EXPANSION_WOD",
[6] = "SUBFILTER_EXPANSION_LEGION",
[7] = "SUBFILTER_EXPANSION_BFA",
[8] = "SUBFILTER_EXPANSION_SHADOW",
[9] = "SUBFILTER_EXPANSION_DRAGONFLIGHT",
[10] = "SUBFILTER_EXPANSION_WAR_WITHIN",
[11] = "SUBFILTER_EXPANSION_MIDNIGHT",
[12] = "SUBFILTER_EXPANSION_LAST_TITAN",
}
function S.Utils.FormatExpacLong(expacID)
if not expansionStrings[expacID] then
return S.Localize(expansionStrings[0])
end
return --[[expansionColors[expacID]..]]S.Localize(expansionStrings[expacID])
end
function S.Utils.FormatTime(time)
if not time then return "" end
if time > 31536000 then
if time > 63072000 then
return math.floor(time / 31536000).." "..S.Localize("SUBFILTER_TIME_YEARS_SHORT")
else
return "1 "..S.Localize("SUBFILTER_TIME_YEAR_SHORT")
end
elseif time >= 2419200 then
if time >= 4838400 then
return math.floor(time / 2419200).." "..S.Localize("SUBFILTER_TIME_MONTHS_SHORT")
else
return "1 "..S.Localize("SUBFILTER_TIME_MONTH_SHORT")
end
elseif time >= 604800 then
if time >= 1209600 then
return math.floor(time / 604800).." "..S.Localize("SUBFILTER_TIME_WEEKS_SHORT")
else
return "1 "..S.Localize("SUBFILTER_TIME_WEEK_SHORT")
end
elseif time >= 86400 then
if time >= 172800 then
return math.floor(time / 86400).." "..S.Localize("SUBFILTER_TIME_DAYS_SHORT")
else
return "1 "..S.Localize("SUBFILTER_TIME_DAY_SHORT")
end
elseif time >= 3600 then
if time >= 7200 then
return math.floor(time / 3600).." "..S.Localize("SUBFILTER_TIME_HOURS_SHORT")
else
return "1 "..S.Localize("SUBFILTER_TIME_HOUR_SHORT")
end
elseif time >= 60 then
if time >= 120 then
return math.floor(time / 60).." "..S.Localize("SUBFILTER_TIME_MINUTES_SHORT")
else
return "1 "..S.Localize("SUBFILTER_TIME_MINUTE_SHORT")
end
elseif time >= 0 then
if time == 1 then
return "1 "..S.Localize("SUBFILTER_TIME_SECOND_SHORT")
else
return time.." "..S.Localize("SUBFILTER_TIME_SECONDS_SHORT")
end
else
return ""
end
end
local MARKER_ICON_COLORS = {
[0] = "|cFFFFFFFF",
[1] = "|cFFFFD827",
[2] = "|cFFFF8C0A",
[3] = "|cFFEC40FF",
[4] = "|cFF26FF22",
[5] = "|cFFADD3EB",
[6] = "|cFF13C7FF",
[7] = "|cFFFA543F",
[8] = "|cFFF3EEE8"
}
local FORMATTED_MARKER_ICONS = {
[0] = NONE,
[1] = RAID_TARGET_1,
[2] = RAID_TARGET_2,
[3] = RAID_TARGET_3,
[4] = RAID_TARGET_4,
[5] = RAID_TARGET_5,
[6] = RAID_TARGET_6,
[7] = RAID_TARGET_7,
[8] = RAID_TARGET_8
}
local MARKER_ICONS_TEXTURE = "|TInterface\\Addons\\Sorted\\Textures\\Favorite-Icons:0:0:0:0:128:128:%d:%d:%d:%d|t"
function S.Utils.FormatMarkerIcon(id, width, height)
--[[if not id or not FORMATTED_MARKER_ICONS[id] then
return FORMATTED_MARKER_ICONS[0]
end
return FORMATTED_MARKER_ICONS[id]]
-- Get the image of the icon
if not FORMATTED_MARKER_ICONS[id] then id = 0 end
local x,y = ((id - 1) % 4) * 28, floor((id - 1) / 4) * 28
return string.format(MARKER_ICONS_TEXTURE, x, x+28, y, y+28).." "..FORMATTED_MARKER_ICONS[id]
end
-- TOOLTIPS
local function GetTooltipText(tooltip)
local i = 1
local text = {{}, {}}
local lineCount = 0
while true do
local foundLine = false
local left, right = _G["SortedDataTooltipTextLeft"..i], _G["SortedDataTooltipTextRight"..i]
if left then
local s = left:GetText()
if s and #s > 1 then
text[1][i] = s
lineCount = i
end
foundLine = true
end
if right then
local s = right:GetText()
if s and #s > 1 then
local r,g,b = right:GetTextColor()
if not text[i] then
text[i] = {}
end
text[2][i] = s
lineCount = i
end
foundLine = true
end
if not foundLine then
break
end
i = i + 1
end
return text
end
function S.Utils.GetVoidItemTooltip(tab, slot)
S.DataTooltip:ClearLines()
S.DataTooltip:SetVoidItem(tab, slot)
return GetTooltipText(S.DataTooltip)
end
function S.Utils.GetBagItemTooltip(bag, slot)
S.DataTooltip:ClearLines()
if bag == BANK_CONTAINER then
S.DataTooltip:SetInventoryItem("player", BankButtonIDToInvSlotID(slot, false))
else
S.DataTooltip:SetBagItem(bag, slot)
end
return GetTooltipText(S.DataTooltip)
end
function S.Utils.GetInventoryItemTooltip(inventoryID)
S.DataTooltip:ClearLines()
S.DataTooltip:SetInventoryItem("player", inventoryID)
return GetTooltipText(S.DataTooltip)
end
function S.Utils.GetCurrencyTooltipByID(curID)
S.DataTooltip:ClearLines()
if S.DataTooltip.SetCurrencyByID then
S.DataTooltip:SetCurrencyByID(curID)
-- Classic Honor and Arena Points tooltips are handled differently
elseif ( curID == Constants.CurrencyConsts.CLASSIC_HONOR_CURRENCY_ID ) then
S.DataTooltip:SetText(HONOR_POINTS, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
S.DataTooltip:AddLine(TOOLTIP_HONOR_POINTS, nil, nil, nil, 1);
elseif ( curID == Constants.CurrencyConsts.CLASSIC_ARENA_POINTS_CURRENCY_ID ) then
S.DataTooltip:SetText(ARENA_POINTS, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
S.DataTooltip:AddLine(TOOLTIP_ARENA_POINTS, nil, nil, nil, 1);
elseif S.DataTooltip.SetCurrencyToken then
S.DataTooltip:SetCurrencyToken(curID)
end
return GetTooltipText(S.DataTooltip)
end
function S.Utils.GetItemTooltipString(bag, slot, omitFirstLine)
S.DataTooltip:ClearLines()
local link = GetContainerItemLink(bag, slot)
if link then
S.DataTooltip:SetHyperlink(link)
local tooltip = ""
local line = 1
if omitFirstLine then
line = 2
end
while true do
local foundLine = false
local left, right = _G["SortedDataTooltipTextLeft"..line], _G["SortedDataTooltipTextRight"..line]
if left then
local s = left:GetText()
if s and #s > 1 then
if line > 1 then
tooltip = tooltip.."\n"
end
tooltip = tooltip..s.." "
foundLine = true
end
end
if right then
local s = right:GetText()
if s and #s > 1 then
if not foundLine and line > 1 then
tooltip = tooltip.."\n"
end
tooltip = tooltip..s
foundLine = true
end
end
if not foundLine then
break
end
line = line + 1
end
return tooltip
end
end
function S.Utils.GetItemTooltipLine(bag, slot, line)
S.DataTooltip:ClearLines()
local link = GetContainerItemLink(bag, slot)
if link then
S.DataTooltip:SetHyperlink(link)
local tooltip = ""
local left, right = _G["SortedDataTooltipTextLeft"..line], _G["SortedDataTooltipTextRight"..line]
if left then
left = left:GetText()
end
if right then
right = right:GetText()
end
return left, right
end
end
-- CONTAINERS UTILITIES
local containersOfType = {
[S.CONTAINER_TYPES.BANK] = { BANK_CONTAINER },
[S.CONTAINER_TYPES.BAGS] = { BACKPACK_CONTAINER },
[S.CONTAINER_TYPES.ALL] = { BACKPACK_CONTAINER, BANK_CONTAINER }
}
for i = 1, NUM_BAG_SLOTS do
table.insert(containersOfType[S.CONTAINER_TYPES.BAGS], i)
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], i)
containersOfType["CONTAINER"..i] = { i }
end
-- Include reagent bags post-Dragonflight
if S.WoWVersion() >= 10 then
containersOfType[S.CONTAINER_TYPES.REAGENT_BAGS] = {}
for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_REAGENTBAG_SLOTS do
table.insert(containersOfType[S.CONTAINER_TYPES.--[[REAGENT_]]BAGS], i)
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], i)
containersOfType["CONTAINER"..i] = { i }
end
5 months ago
if S.UseNewBank() then
for i = Enum.BagIndex.CharacterBankTab_1, Enum.BagIndex.CharacterBankTab_6 do
table.insert(containersOfType[S.CONTAINER_TYPES.BANK], i)
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], i)
containersOfType["CONTAINER"..i] = { i }
end
else
for i = NUM_BAG_SLOTS + NUM_REAGENTBAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_REAGENTBAG_SLOTS + NUM_BANKBAGSLOTS do
table.insert(containersOfType[S.CONTAINER_TYPES.BANK], i)
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], i)
containersOfType["CONTAINER"..i] = { i }
end
end
else
for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
table.insert(containersOfType[S.CONTAINER_TYPES.BANK], i)
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], i)
containersOfType["CONTAINER"..i] = { i }
end
end
-- Include account bank post-TWW
if S.WoWVersion() >= 11 then
containersOfType[S.CONTAINER_TYPES.ACCOUNT] = {}
5 months ago
for i = Enum.BagIndex.AccountBankTab_1, Enum.BagIndex.AccountBankTab_5 do
table.insert(containersOfType[S.CONTAINER_TYPES.ACCOUNT], i)
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], i)
end
end
if S.WoWVersion() <= 3 then
containersOfType[S.CONTAINER_TYPES.KEYRING] = { KEYRING_CONTAINER }
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], KEYRING_CONTAINER)
elseif S.WoWVersion() >= 6 then
containersOfType[S.CONTAINER_TYPES.REAGENT] = { REAGENTBANK_CONTAINER }
table.insert(containersOfType[S.CONTAINER_TYPES.ALL], REAGENTBANK_CONTAINER)
end
function S.Utils.ContainersOfType(containerType)
return containersOfType[containerType]
end
local containerTypes = {
[BACKPACK_CONTAINER] = "BAGS"
}
5 months ago
if not S.UseNewBank() then
containerTypes[BANK_CONTAINER] = "BANK"
end
if S.WoWVersion() >= 10 then
for i = 1, NUM_BAG_SLOTS do
containerTypes[i] = "BAGS"
end
for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_REAGENTBAG_SLOTS do
containerTypes[i] = "BAGS"
--containerTypes[i] = "REAGENT_BAGS"
end
5 months ago
if S.UseNewBank() then
for i = Enum.BagIndex.CharacterBankTab_1, Enum.BagIndex.CharacterBankTab_6 do
containerTypes[i] = "BANK"
end
else
for i = NUM_BAG_SLOTS + NUM_REAGENTBAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_REAGENTBAG_SLOTS + NUM_BANKBAGSLOTS do
containerTypes[i] = "BANK"
end
end
else
for i = 1, NUM_BAG_SLOTS do
containerTypes[i] = "BAGS"
end
5 months ago
if not S.UseNewBank() then
for i = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
containerTypes[i] = "BANK"
end
end
end
if S.WoWVersion() >= 11 then
5 months ago
for i = Enum.BagIndex.AccountBankTab_1, Enum.BagIndex.AccountBankTab_5 do
containerTypes[i] = "ACCOUNT"
end
end
if S.WoWVersion() < 4 then
containerTypes[KEYRING_CONTAINER] = "KEYRING"
5 months ago
elseif S.WoWVersion() >= 6 and not S.UseNewBank() then
containerTypes[REAGENTBANK_CONTAINER] = "REAGENT"
end
function S.Utils.GetContainerType(container)
return containerTypes[container]
end
function S.Utils.ContainerIsType(container, type)
return containerTypes[container] == type
end
function S.Utils.GetContainerMaxSlots(container)
if container == REAGENTBANK_CONTAINER then
return 98
elseif container == BANK_CONTAINER then
return 28
-- Account bank
elseif container >= 13 and container <= 17 then
return 98
else
return S.Utils.MaxBagSlots()
end
end
local containerInventoryIDs = {}
for _, containerID in pairs(S.Utils.ContainersOfType("ALL")) do
if containerID ~= BACKPACK_CONTAINER and containerID ~= BANK_CONTAINER and containerID ~= REAGENTBANK_CONTAINER and containerID ~= KEYRING_CONTAINER then
containerInventoryIDs[ContainerIDToInventoryID(containerID)] = containerID
end
end
function S.Utils.InventoryIDToContainerID(inventoryID)
return containerInventoryIDs[inventoryID]
end
-- Sell trash. Every second, checks if there's still trash to be sold and tries again, until all trash is sold.
-- Regardless how many times the button is pressed, it still only checks once per second
local doSellTrash = false
local lastSellTrashTime = GetTime()
function S.Utils.VendorTrash()
doSellTrash = true
end
local function IsThereStillTrash()
for k,bagID in pairs(S.Utils.ContainersOfType("BAGS")) do
for slotID = 1,36 do
local itemData = S.Data.GetItem(bagID, slotID)
if (S.Data.GetTrash(itemData) <= 1) and (not itemData.noValue) then
return true
end
end
end
return false
end
local function SellTrash()
for k,bagID in pairs(S.Utils.ContainersOfType("BAGS")) do
for slotID = 1,36 do
local itemData = S.Data.GetItem(bagID, slotID)
if (S.Data.GetTrash(itemData) <= 1) and (not itemData.noValue) then
UseContainerItem(bagID, slotID)
end
end
end
end
local SellTrashFrame = CreateFrame("FRAME")
SellTrashFrame:SetScript("OnUpdate", function(self)
if doSellTrash and lastSellTrashTime < GetTime() - 1 then
if IsThereStillTrash() and MerchantFrame:IsVisible() then
SellTrash()
lastSellTrashTime = GetTime()
else
doSellTrash = false
end
end
end)
-- Methods to attempt to empty the contents of one bag into the other bags
-- Usage: S.Utils.EmptyBag(bagID)
local lockedSlots
local function DropItemInFirstAvailableBag(containers, excludedBag)
local _, cursorItemID, cursorItemLink = GetCursorInfo()
local itemFamily = GetItemFamily(cursorItemLink)
for _, bag in pairs(containers) do
if bag ~= excludedBag then
if not lockedSlots[bag] then lockedSlots[bag] = {} end
if GetContainerNumFreeSlots(bag) > 0 then
local bagFamily
if bag == BACKPACK_CONTAINER then
bagFamily = 0
else
bagFamily = GetItemFamily(GetInventoryItemLink("player", ContainerIDToInventoryID(bag)))
end
if bagFamily == 0 or bagFamily == itemFamily then
for slot = 1, GetContainerNumSlots(bag) do
if not lockedSlots[bag][slot] then
local itemInfo = GetContainerItemInfo(bag, slot)
if not itemInfo or itemInfo.itemID == cursorItemID then
PickupContainerItem(bag, slot)
lockedSlots[bag][slot] = true
if not CursorHasItem() then
return true
end
end
end
end
end
end
end
end
return false
end
function S.Utils.EmptyBag(bag)
lockedSlots = {}
local containers = S.Utils.ContainersOfType(S.Utils.GetContainerType(bag))
for slot = 1, GetContainerNumSlots(bag) do
PickupContainerItem(bag, slot)
if CursorHasItem() then
if not DropItemInFirstAvailableBag(containers, bag) then
UIErrorsFrame:AddExternalErrorMessage(S.Localize("WARNING_BAG_COULDNT_EMPTY"))
ClearCursor()
return
end
end
end
ClearCursor()
end
function EmptyBag(bag)
S.Utils.EmptyBag(bag)
end
local cursorHasSplitStack = false
local pickupLocationBag, pickupLocationSlot = nil, nil
local pickupLocationInventoryID = nil
local pickupLocationGuildTab, pickupLocationGuildSlot = nil, nil
-- Check cursor has an item first with: infoType, ... = GetCursorInfo()
function S.Utils.PlaceCursorItemInContainerType(containerType, guildTab)
if S.cursorIsHoldingVoidStorageItem then
ClickVoidTransferWithdrawalSlot(9)
return
end
local _, cursorItemID, cursorItemLink = GetCursorInfo()
-- Guild bank function
if containerType == "GUILD_BANK" then
local tabs = {}
if guildTab then
tabs[1] = guildTab
else
for i = 1, GetNumGuildBankTabs() do
tabs[i] = i
end
end
-- First combine stacks if item isn't from a split stack
if not cursorHasSplitStack then
for _, tab in pairs(tabs) do
for slot = 1, 98 do
if not (tab == pickupLocationGuildTab and slot == pickupLocationGuildSlot) then
local itemLink = GetGuildBankItemLink(tab, slot)
if itemLink == cursorItemLink then
PickupGuildBankItem(tab, slot)
if not GetCursorInfo() then
return
end
end
end
end
end
end
-- Then put item in an empty slot
for _, tab in pairs(tabs) do
for slot = 1, 98 do
local itemLink = GetGuildBankItemLink(tab, slot)
if not itemLink then
PickupGuildBankItem(tab, slot)
if not GetCursorInfo() then
cursorHasSplitStack = false
return
end
end
end
end
-- Container function
else
-- First combine stacks if item isn't from a split stack
if not cursorHasSplitStack then
for _, bag in pairs(S.Utils.ContainersOfType(containerType)) do
-- Prevent putting a bag into itself
if not pickupLocationInventoryID or S.Utils.InventoryIDToContainerID(pickupLocationInventoryID) ~= bag then
for slot = 1, GetContainerNumSlots(bag) do
if not (bag == pickupLocationBag and slot == pickupLocationSlot) then
-- Get the item in the slot
local count, itemID
if useNewContainerInfo then
local info = GetContainerItemInfo(bag, slot)
if info then
count = info.stackCount
itemID = info.itemID
end
else
_, count, _, _, _, _, _, _, _, itemID = GetContainerItemInfo(bag, slot)
end
if itemID then
-- Stack the items if they have the same ID and the stack isn't at the max stack count
local _, _, _, _, _, _, _, stackCount = GetItemInfo(itemID)
if itemID == cursorItemID and count < stackCount then
PickupContainerItem(bag, slot)
if not GetCursorInfo() then
return
end
end
end
end
end
end
end
end
-- Then put item in an empty slot
for _, bag in pairs(S.Utils.ContainersOfType(containerType)) do
-- Prevent putting a bag into itself
if not pickupLocationInventoryID or S.Utils.InventoryIDToContainerID(pickupLocationInventoryID) ~= bag then
for slot = 1, GetContainerNumSlots(bag) do
local itemID
if useNewContainerInfo then
itemID = GetContainerItemInfo(bag, slot)
else
_, _, _, _, _, _, _, _, _, itemID = GetContainerItemInfo(bag, slot)
end
if not itemID then
-- Found an empty slot
PickupContainerItem(bag, slot)
if not GetCursorInfo() then
cursorHasSplitStack = false
return
end
end
end
end
end
end
end
-- Record location items are picked up from
local function OnContainerItemPickup(bag, slot)
if GetCursorInfo() then
pickupLocationBag = bag
pickupLocationSlot = slot
pickupLocationInventoryID = nil
pickupLocationGuildTab = nil
pickupLocationGuildSlot = nil
end
end
if C_Container and C_Container.PickupContainerItem then
hooksecurefunc(C_Container, "PickupContainerItem", OnContainerItemPickup)
else
hooksecurefunc("PickupContainerItem", OnContainerItemPickup)
end
local function OnInventoryItemPickup(inventoryID)
if GetCursorInfo() then
pickupLocationBag = nil
pickupLocationSlot = nil
pickupLocationInventoryID = inventoryID
pickupLocationGuildTab = nil
pickupLocationGuildSlot = nil
end
end
hooksecurefunc("PickupInventoryItem", OnInventoryItemPickup)
local function OnGuildItemPickup(tab, slot)
if GetCursorInfo() then
pickupLocationBag = nil
pickupLocationSlot = nil
pickupLocationInventoryID = nil
pickupLocationGuildTab = tab
pickupLocationGuildSlot = slot
end
end
hooksecurefunc("PickupGuildBankItem", OnGuildItemPickup)
-- Record when an item is from a split stack
if C_Container and C_Container.SplitContainerItem then
hooksecurefunc(C_Container, "SplitContainerItem", function()
cursorHasSplitStack = true
end)
else
hooksecurefunc("SplitContainerItem", function()
cursorHasSplitStack = true
end)
end
hooksecurefunc("SplitGuildBankItem", function()
cursorHasSplitStack = true
end)
-- Reset on CURSOR_CHANGED
if S.WoWVersion() > 1 then
local eventHandlerFrame = CreateFrame("FRAME")
eventHandlerFrame:RegisterEvent("CURSOR_CHANGED")
eventHandlerFrame:SetScript("OnEvent", function(self)
if not CursorHasItem() then
pickupLocationBag = nil
pickupLocationSlot = nil
cursorHasSplitStack = false
end
end)
end
-- TABLE UTILITIES
-- Prints a table in a neat format
function S.Utils.PrintTable(t,indent, key)
local s = ""
if indent then
for i = 1,indent do s=s.." " end
else
indent = 0
end
if type(t) == "table" then
if key then
if type(key) == "string" then
print(s..'["'..key..'"] = {')
else
print(s.."["..key.."] = {")
end
else
print(s.."{")
end
for k,v in pairs(t) do
S.Utils.PrintTable(v,indent+1,k)
end
print(s.."},")
else
if key then
if type(key) == "string" then
s = s..'["'..key..'"] = '
else
s = s.."["..key.."] = "
end
end
if type(t) == "string" then
print(s..'"'..t..'"'.. " |cff777777 ("..type(t)..")")
else
print(s..tostring(t).. " |cff777777 ("..type(t)..")")
end
end
end
function Sorted_PrintTable(t,indent, key)
S.Utils.PrintTable(t,indent, key)
end
function S.Utils.TableToString(t)
local s = "{"
local addComma = false
for k, v in pairs(t) do
if addComma then
s = s..","
end
if type(k) == "string" then
s = s..'["'..k..'"]='
else
s = s..'['..k..']='
end
if type(v) == "table" then
s = s..S.Utils.TableToString(v)
elseif type(v) == "string" then
s = s..'"'..v:gsub('"', "")..'"'
else
s = s..tostring(v)
end
addComma = true
end
return s.."}"
end
local keyPattern = '%[(.-)%]'
local stringPattern = '%"(.-)%"'
local function StoT(s)
local t = {}
while #s > 0 do
-- Get the next key
local keyStart, keyEnd = s:find(keyPattern)
if not keyStart then return end
local key = s:sub(keyStart + 1, keyEnd - 1)
if key:find(stringPattern) then
key = key:match(stringPattern)
elseif tonumber(key) then
key = tonumber(key)
end
s = s:sub(keyEnd + 1)
while s:sub(1,1) == " " do s = s:sub(2) end -- Remove whitespace
if s:sub(1,1) ~= "=" then return end -- Find equals sign
s = s:sub(2)
while s:sub(1,1) == " " do s = s:sub(2) end -- Remove whitespace
-- Get the next value
if s:sub(1,1) == "{" then
-- Table value
local i, depth = 1, 1
while depth > 0 and i < #s do
i = i + 1
if s:sub(i,i) == "{" then
depth = depth + 1
elseif s:sub(i,i) == "}" then
depth = depth - 1
end
end
if depth > 0 then return end
t[key] = StoT(s:sub(2, i - 1))
s = s:sub(i + 1)
elseif s:sub(1,1) == '"' then
-- String value
local stringStart, stringEnd = s:find(stringPattern)
t[key] = s:sub(stringStart + 1, stringEnd - 1)
s = s:sub(stringEnd + 1)
else
local i = s:find("[,}]")
local value = nil
if not i then
value = s:sub(1)
s = ""
else
value = s:sub(1, i - 1)
s = s:sub(i + 1)
end
if tonumber(value) then
value = tonumber(value)
elseif value == "true" then
value = true
elseif value == "false" then
value = false
end
t[key] = value
end
end
return t
end
function S.Utils.StringToTable(s)
s = s:match("^%s*(.-)%s*$")
if s:sub(1,1) == "{" and s:sub(-1) == "}" then
local t = StoT(s:sub(2, -2))
return t
end
end
function S.Utils.GetNumKeysInTable(t)
local i = 0
for _, _ in pairs(t) do
i = i + 1
end
return i
end
-- Copies contents of t1 into t2
function S.Utils.CopyTable(t1, t2)
for key, value in pairs(t1) do
if type(value) == "table" then
t2[key] = {}
S.Utils.CopyTable(value, t2[key])
else
t2[key] = value
end
end
end
-- Deletes empty inner tables
function S.Utils.CleanTable(t)
if type(t) == "table" then
local empty = true
for k, v in pairs(t) do
if S.Utils.CleanTable(v) then
t[k] = nil
else
empty = false
end
end
if empty then
return true
end
end
return false
end
-- TEST FOR LAG SPIKES
-- Displays a number in the middle of the screen which shows the longest recent frametime, in milliseconds
function Sorted_TestForLagSpikes()
local fpsLastTime = GetTime()
local fps = CreateFrame("FRAME", nil, UIParent)
fps:SetAllPoints()
local fpsText = fps:CreateFontString(nil, "OVERLAY", "SortedFont")
local fpsHistory = {}
fpsText:SetPoint("CENTER")
fps:SetScript("OnUpdate", function(self)
local time = debugprofilestop()
table.insert(fpsHistory, time - fpsLastTime)
if #fpsHistory > 20 then
table.remove(fpsHistory, 1)
end
local longestTime = 0
for i, duration in pairs(fpsHistory) do
if duration > longestTime then
longestTime = duration
end
end
fpsText:SetText(string.format("%.2d", longestTime))
fpsLastTime = time
end)
5 years ago
end