Update
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
function(progress)
|
||||
return aura_env.Display.color.r, aura_env.Display.color.g, aura_env.Display.color.b, 1
|
||||
end
|
||||
function(progress)
|
||||
return aura_env.Display.color.r, aura_env.Display.color.g, aura_env.Display.color.b, 1
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
function()
|
||||
local current, max = aura_env.ShieldManager.GetStats()
|
||||
return current, max, 1
|
||||
function()
|
||||
local current, max = aura_env.ShieldManager.GetStats()
|
||||
return current, max, 1
|
||||
end
|
||||
@@ -1,8 +1,8 @@
|
||||
-- TICKER_500
|
||||
function()
|
||||
for _, shield in pairs(aura_env.Shields) do
|
||||
shield:Update()
|
||||
end
|
||||
aura_env.Display.Update()
|
||||
return true
|
||||
-- TICKER_500
|
||||
function()
|
||||
for _, shield in pairs(aura_env.Shields) do
|
||||
shield:Update()
|
||||
end
|
||||
aura_env.Display.Update()
|
||||
return true
|
||||
end
|
||||
@@ -1,31 +1,31 @@
|
||||
---@param table table
|
||||
---@param depth number?
|
||||
function DumpTable(table, depth)
|
||||
if depth == nil then
|
||||
depth = 0
|
||||
end
|
||||
if (depth > 200) then
|
||||
print("Error: Depth > 200 in dumpTable()")
|
||||
return
|
||||
end
|
||||
for k, v in pairs(table) do
|
||||
if (type(v) == "table") then
|
||||
print(string.rep(" ", depth) .. k .. ":")
|
||||
DumpTable(v, depth + 1)
|
||||
else
|
||||
print(string.rep(" ", depth) .. k .. ": ", v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local colorer = Colorer.new()
|
||||
|
||||
local value = 1.21
|
||||
local rgb = colorer:Interpolate(value)
|
||||
|
||||
for i = 1, 150 do
|
||||
local value = i / 100
|
||||
local rgb = colorer:Interpolate(value)
|
||||
print(value, rgb.r, rgb.g, rgb.b)
|
||||
end
|
||||
---@param table table
|
||||
---@param depth number?
|
||||
function DumpTable(table, depth)
|
||||
if depth == nil then
|
||||
depth = 0
|
||||
end
|
||||
if (depth > 200) then
|
||||
print("Error: Depth > 200 in dumpTable()")
|
||||
return
|
||||
end
|
||||
for k, v in pairs(table) do
|
||||
if (type(v) == "table") then
|
||||
print(string.rep(" ", depth) .. k .. ":")
|
||||
DumpTable(v, depth + 1)
|
||||
else
|
||||
print(string.rep(" ", depth) .. k .. ": ", v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local colorer = Colorer.new()
|
||||
|
||||
local value = 1.21
|
||||
local rgb = colorer:Interpolate(value)
|
||||
|
||||
for i = 1, 150 do
|
||||
local value = i / 100
|
||||
local rgb = colorer:Interpolate(value)
|
||||
print(value, rgb.r, rgb.g, rgb.b)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
function()
|
||||
return aura_env.Display.current, aura_env.Display.max, aura_env.Display.ofHp
|
||||
function()
|
||||
return aura_env.Display.current, aura_env.Display.max, aura_env.Display.ofHp
|
||||
end
|
||||
@@ -1,14 +1,14 @@
|
||||
-- CHAT_MSG_SYSTEM
|
||||
function(e, msg)
|
||||
if msg == nil then return false end
|
||||
if string.find(msg, "You are now Away") then
|
||||
aura_env.KickTime = time() + 30 * 60
|
||||
aura_env.AfkSince = time()
|
||||
return true
|
||||
end
|
||||
if string.find(msg, "You are no longer Away") then
|
||||
aura_env.KickTime = 0
|
||||
aura_env.AfkSince = 0
|
||||
return false
|
||||
end
|
||||
-- CHAT_MSG_SYSTEM
|
||||
function(e, msg)
|
||||
if msg == nil then return false end
|
||||
if string.find(msg, "You are now Away") then
|
||||
aura_env.KickTime = time() + 30 * 60
|
||||
aura_env.AfkSince = time()
|
||||
return true
|
||||
end
|
||||
if string.find(msg, "You are no longer Away") then
|
||||
aura_env.KickTime = 0
|
||||
aura_env.AfkSince = 0
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,3 @@
|
||||
function(e)
|
||||
return math.floor(time() - aura_env.AfkSince), math.floor(aura_env.KickTime - aura_env.AfkSince), 1
|
||||
function(e)
|
||||
return math.floor(time() - aura_env.AfkSince), math.floor(aura_env.KickTime - aura_env.AfkSince), 1
|
||||
end
|
||||
@@ -1,2 +1,2 @@
|
||||
aura_env.KickTime = 0
|
||||
aura_env.KickTime = 0
|
||||
aura_env.AfkSince = 0
|
||||
@@ -1,3 +1,3 @@
|
||||
function(t)
|
||||
return t[2]
|
||||
function(t)
|
||||
return t[2]
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
-- CHAT_MSG_LOOT
|
||||
function(allstates, e, msg)
|
||||
if msg:match("You receive loot") then
|
||||
DrawIcon(msg, allstates)
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- CHAT_MSG_LOOT
|
||||
function(allstates, e, msg)
|
||||
if msg:match("You receive loot") then
|
||||
DrawIcon(msg, allstates)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
local iconDisplayDuration = 3
|
||||
|
||||
---@alias CItemInfo {name: string, icon: number, quantity: number, color: string}
|
||||
|
||||
---@param msg string
|
||||
---@return CItemInfo, string | nil
|
||||
GetItemInfo = function(msg)
|
||||
local itemInfo = {name = "", icon = 134400, quantity = 0, color = ""}
|
||||
local name = msg:match("h%[(.+)%]")
|
||||
if not name then return itemInfo, "No item name found" end
|
||||
local quantity = msg:match("x(%d+)") or 1
|
||||
itemInfo.name = name
|
||||
itemInfo.quantity = quantity
|
||||
|
||||
if WeakAurasSaved.Cyka.ItemCache[name] then
|
||||
itemInfo.icon = WeakAurasSaved.Cyka.ItemCache[name].icon
|
||||
end
|
||||
local color = msg:match("cff(%w%w%w%w%w%w)") or "ffffff"
|
||||
itemInfo.color = color
|
||||
|
||||
return itemInfo, nil
|
||||
end
|
||||
|
||||
---@param msg string
|
||||
---@param allstates allstates
|
||||
DrawIcon = function(msg, allstates)
|
||||
local info, err = GetItemInfo(msg)
|
||||
if err then
|
||||
print(err)
|
||||
return
|
||||
end
|
||||
|
||||
local formattedName = "\124cff" .. info.color .. "[" .. info.name .. "]\124r x" .. info.quantity
|
||||
allstates[#aura_env.allstates + 1] = {
|
||||
show = true,
|
||||
changed = true,
|
||||
index = GetTime(),
|
||||
resort = true,
|
||||
|
||||
icon = info.icon,
|
||||
name = formattedName,
|
||||
|
||||
progressType = "timed",
|
||||
expirationTime = GetTime() + iconDisplayDuration,
|
||||
duration = iconDisplayDuration,
|
||||
autoHide = true,
|
||||
}
|
||||
local iconDisplayDuration = 3
|
||||
|
||||
---@alias CItemInfo {name: string, icon: number, quantity: number, color: string}
|
||||
|
||||
---@param msg string
|
||||
---@return CItemInfo, string | nil
|
||||
GetItemInfo = function(msg)
|
||||
local itemInfo = {name = "", icon = 134400, quantity = 0, color = ""}
|
||||
local name = msg:match("h%[(.+)%]")
|
||||
if not name then return itemInfo, "No item name found" end
|
||||
local quantity = msg:match("x(%d+)") or 1
|
||||
itemInfo.name = name
|
||||
itemInfo.quantity = quantity
|
||||
|
||||
if WeakAurasSaved.Cyka.ItemCache[name] then
|
||||
itemInfo.icon = WeakAurasSaved.Cyka.ItemCache[name].icon
|
||||
end
|
||||
local color = msg:match("cff(%w%w%w%w%w%w)") or "ffffff"
|
||||
itemInfo.color = color
|
||||
|
||||
return itemInfo, nil
|
||||
end
|
||||
|
||||
---@param msg string
|
||||
---@param allstates allstates
|
||||
DrawIcon = function(msg, allstates)
|
||||
local info, err = GetItemInfo(msg)
|
||||
if err then
|
||||
print(err)
|
||||
return
|
||||
end
|
||||
|
||||
local formattedName = "\124cff" .. info.color .. "[" .. info.name .. "]\124r x" .. info.quantity
|
||||
allstates[#aura_env.allstates + 1] = {
|
||||
show = true,
|
||||
changed = true,
|
||||
index = GetTime(),
|
||||
resort = true,
|
||||
|
||||
icon = info.icon,
|
||||
name = formattedName,
|
||||
|
||||
progressType = "timed",
|
||||
expirationTime = GetTime() + iconDisplayDuration,
|
||||
duration = iconDisplayDuration,
|
||||
autoHide = true,
|
||||
}
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
-- LOOT_READY
|
||||
function(e)
|
||||
C_Timer.After(0.1, function()
|
||||
WeakAuras.ScanEvents("DO_AUTOLOOT")
|
||||
end)
|
||||
end
|
||||
-- LOOT_READY
|
||||
function(e)
|
||||
C_Timer.After(0.1, function()
|
||||
WeakAuras.ScanEvents("DO_AUTOLOOT")
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-- DO_AUTOLOOT
|
||||
function(e)
|
||||
local lootInfo = GetLootInfo()
|
||||
aura_env.FilterService.Run(lootInfo)
|
||||
CloseLoot()
|
||||
end
|
||||
-- DO_AUTOLOOT
|
||||
function(e)
|
||||
local lootInfo = GetLootInfo()
|
||||
aura_env.FilterService.Run(lootInfo)
|
||||
CloseLoot()
|
||||
end
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
-- MERCHANT_SHOW
|
||||
function(allstates, e)
|
||||
if (GetMerchantNumItems() > 0) then
|
||||
aura_env.allstates = allstates
|
||||
|
||||
if CanMerchantRepair() == true then RepairAllItems() end
|
||||
|
||||
for container = 0, 4 do
|
||||
for slot = 1, GetContainerNumSlots(container) do
|
||||
aura_env.FilterService.Run(container, slot)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- MERCHANT_SHOW
|
||||
function(allstates, e)
|
||||
if (GetMerchantNumItems() > 0) then
|
||||
aura_env.allstates = allstates
|
||||
|
||||
if CanMerchantRepair() == true then RepairAllItems() end
|
||||
|
||||
for container = 0, 4 do
|
||||
for slot = 1, GetContainerNumSlots(container) do
|
||||
aura_env.FilterService.Run(container, slot)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,264 +1,264 @@
|
||||
local debug = false
|
||||
local iconDisplayDuration = 3
|
||||
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemLink(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local link = select(7, GetContainerItemInfo(container, slot))
|
||||
if link == nil then return "", string.format("GetContainerItemInfo returned nil for link (arg 7)") end
|
||||
return link
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemQuantity(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local quantity = select(2, GetContainerItemInfo(container, slot))
|
||||
if quantity == nil then
|
||||
return "",
|
||||
string.format("GetContainerItemInfo returned nil for quantity (arg 2)")
|
||||
end
|
||||
return quantity
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemName(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local name = select(1, GetItemInfo(getItemLink(container, slot)))
|
||||
if name == nil then return "", string.format("GetItemInfo returned nil for name (arg 1)") end
|
||||
return name
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemType(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local type = select(6, GetItemInfo(getItemLink(container, slot)))
|
||||
if type == nil then return "", string.format("GetItemInfo returned nil for type (arg 6)") end
|
||||
return type
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemSubtype(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local subtype = select(7, GetItemInfo(getItemLink(container, slot)))
|
||||
if subtype == nil then return "", string.format("GetItemInfo returned nil for subtype (arg 7)") end
|
||||
return subtype
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemLevel(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local level = select(4, GetItemInfo(getItemLink(container, slot)))
|
||||
if level == nil then return "", string.format("GetItemInfo returned nil for level (arg 4)") end
|
||||
return level
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemValue(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local value = select(11, GetItemInfo(getItemLink(container, slot)))
|
||||
if value == nil then return "", string.format("GetItemInfo returned nil for value (arg 11)") end
|
||||
return value
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemQuality(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local quality = select(3, GetItemInfo(getItemLink(container, slot)))
|
||||
if quality == nil then return "", string.format("GetItemInfo returned nil for quality (arg 3)") end
|
||||
return quality
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemEquipLocation(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local equipLoc = select(9, GetItemInfo(getItemLink(container, slot)))
|
||||
if equipLoc == nil then return "", string.format("GetItemInfo returned nil for equipLoc (arg 9)") end
|
||||
return equipLoc
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemIcon(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local icon = select(10, GetItemInfo(getItemLink(container, slot)))
|
||||
if icon == nil then return "", string.format("GetItemInfo returned nil for icon (arg 10)") end
|
||||
return icon
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getBindType(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local bindType = select(14, GetItemInfo(getItemLink(container, slot)))
|
||||
if bindType == nil then return "", string.format("GetItemInfo returned nil for bindType (arg 14)") end
|
||||
return bindType
|
||||
end
|
||||
|
||||
---@class Filter
|
||||
---@field requires table<string, fun(container:number, slot: number): string|number|boolean> | nil
|
||||
---@field filter fun(container: number, slot: number, provided: table<string, string|number|boolean>): boolean
|
||||
Filter = {
|
||||
---@param requires table<string, fun(container:number, slot: number): string|number|boolean> | nil
|
||||
---@param filter fun(container: number, slot: number, provided: table<string, string|number|boolean>): boolean
|
||||
---@return Filter
|
||||
new = function(requires, filter)
|
||||
local self = setmetatable({}, {
|
||||
__index = Filter
|
||||
})
|
||||
self.requires = requires
|
||||
self.filter = filter
|
||||
return self
|
||||
end,
|
||||
|
||||
---@param self Filter
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return boolean, string|nil
|
||||
Run = function(self, container, slot)
|
||||
---@type table<string, string|number|boolean>
|
||||
local provided = {}
|
||||
if self.requires then
|
||||
for k, v in pairs(self.requires) do
|
||||
local res, err = v(container, slot)
|
||||
if err then
|
||||
if debug then print(err) end
|
||||
return false, err
|
||||
end
|
||||
provided[k] = res
|
||||
end
|
||||
end
|
||||
local res, err = self.filter(container, slot, provided)
|
||||
if err then
|
||||
if debug then print(err) end
|
||||
end
|
||||
return res, nil
|
||||
end
|
||||
}
|
||||
|
||||
local grayFilter = Filter.new({
|
||||
["quality"] = getItemQuality
|
||||
},
|
||||
function(container, slot, provided)
|
||||
if provided.quality == 0 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
||||
local gearFilter = Filter.new({
|
||||
["ilvl"] = getItemLevel,
|
||||
["bindType"] = getBindType,
|
||||
["type"] = getItemType,
|
||||
["equipLoc"] = getItemEquipLocation
|
||||
},
|
||||
function(container, slot, provided)
|
||||
local ilvlThreshold = 850
|
||||
local sellBoe = false
|
||||
|
||||
if provided.type == "Armor"
|
||||
or provided.type == "Weapon"
|
||||
or provided.equipLoc == "INVTYPE_FINGER"
|
||||
or provided.equipLoc == "INVTYPE_TRINKET"
|
||||
or provided.equipLoc == "INVTYPE_CLOAK"
|
||||
or provided.equipLoc == "INVTYPE_NECK" then
|
||||
if provided.ilvl < ilvlThreshold and (provided.bindType == 1 or sellBoe) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return nil, string|nil
|
||||
local function doSell(container, slot)
|
||||
local itemIcon, err = getItemIcon(container, slot)
|
||||
if err then return nil, string.format("Error getting item icon: %s", err) end
|
||||
local itemName, err = getItemName(container, slot)
|
||||
if err then return nil, string.format("Error getting item name: %s", err) end
|
||||
local itemQuantity, err = getItemQuantity(container, slot)
|
||||
if err then return nil, string.format("Error getting item quantity: %s", err) end
|
||||
local itemQuality, err = getItemQuality(container, slot)
|
||||
if err then return nil, string.format("Error getting item quality: %s", err) end
|
||||
|
||||
local nameWithColor = string.format("%s[%s]\124r", aura_env.qualityColors[itemQuality + 1], itemName)
|
||||
local nameWithQuantity = string.format("%s x%s", nameWithColor, itemQuantity)
|
||||
|
||||
aura_env.allstates[#aura_env.allstates + 1] = {
|
||||
show = true,
|
||||
changed = true,
|
||||
index = GetTime(),
|
||||
resort = true,
|
||||
|
||||
icon = itemIcon,
|
||||
name = nameWithQuantity,
|
||||
amount = itemQuantity,
|
||||
|
||||
progressType = "timed",
|
||||
expirationTime = GetTime() + iconDisplayDuration,
|
||||
duration = iconDisplayDuration,
|
||||
autoHide = true,
|
||||
}
|
||||
|
||||
UseContainerItem(container, slot)
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
---@type table<Filter>
|
||||
local filters = {
|
||||
grayFilter,
|
||||
-- gearFilter
|
||||
}
|
||||
|
||||
---@class FilterService
|
||||
aura_env.FilterService = {
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return nil, nil
|
||||
Run = function(container, slot)
|
||||
local itemLink, err = getItemLink(container, slot)
|
||||
if err then return nil, string.format("Err: slot empty (%d-%d) - %s", container, slot, err) end
|
||||
for k, filter in pairs(filters) do
|
||||
local res, err = filter:Run(container, slot)
|
||||
if err then
|
||||
if debug then print(err) end
|
||||
else
|
||||
if res then
|
||||
doSell(container, slot)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
aura_env.qualityColors = {
|
||||
"\124cff9d9d9d", -- Poor
|
||||
"\124cffffffff", -- Common
|
||||
"\124cff1eff00", -- Uncommon
|
||||
"\124cff0070dd", -- Rare
|
||||
"\124cffa335ee", -- Epic
|
||||
"\124cffff8000", -- Legendary
|
||||
"\124cffe6cc80", -- Artifact
|
||||
"\124cff00ccff", -- Heirloom
|
||||
}
|
||||
local debug = false
|
||||
local iconDisplayDuration = 3
|
||||
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemLink(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local link = select(7, GetContainerItemInfo(container, slot))
|
||||
if link == nil then return "", string.format("GetContainerItemInfo returned nil for link (arg 7)") end
|
||||
return link
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemQuantity(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local quantity = select(2, GetContainerItemInfo(container, slot))
|
||||
if quantity == nil then
|
||||
return "",
|
||||
string.format("GetContainerItemInfo returned nil for quantity (arg 2)")
|
||||
end
|
||||
return quantity
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemName(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local name = select(1, GetItemInfo(getItemLink(container, slot)))
|
||||
if name == nil then return "", string.format("GetItemInfo returned nil for name (arg 1)") end
|
||||
return name
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemType(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local type = select(6, GetItemInfo(getItemLink(container, slot)))
|
||||
if type == nil then return "", string.format("GetItemInfo returned nil for type (arg 6)") end
|
||||
return type
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemSubtype(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local subtype = select(7, GetItemInfo(getItemLink(container, slot)))
|
||||
if subtype == nil then return "", string.format("GetItemInfo returned nil for subtype (arg 7)") end
|
||||
return subtype
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemLevel(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local level = select(4, GetItemInfo(getItemLink(container, slot)))
|
||||
if level == nil then return "", string.format("GetItemInfo returned nil for level (arg 4)") end
|
||||
return level
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemValue(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local value = select(11, GetItemInfo(getItemLink(container, slot)))
|
||||
if value == nil then return "", string.format("GetItemInfo returned nil for value (arg 11)") end
|
||||
return value
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemQuality(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local quality = select(3, GetItemInfo(getItemLink(container, slot)))
|
||||
if quality == nil then return "", string.format("GetItemInfo returned nil for quality (arg 3)") end
|
||||
return quality
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemEquipLocation(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local equipLoc = select(9, GetItemInfo(getItemLink(container, slot)))
|
||||
if equipLoc == nil then return "", string.format("GetItemInfo returned nil for equipLoc (arg 9)") end
|
||||
return equipLoc
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getItemIcon(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local icon = select(10, GetItemInfo(getItemLink(container, slot)))
|
||||
if icon == nil then return "", string.format("GetItemInfo returned nil for icon (arg 10)") end
|
||||
return icon
|
||||
end
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return string, string|nil
|
||||
local function getBindType(container, slot)
|
||||
if container == nil then return "", string.format("Container is nil") end
|
||||
if slot == nil then return "", string.format("Slot is nil") end
|
||||
local bindType = select(14, GetItemInfo(getItemLink(container, slot)))
|
||||
if bindType == nil then return "", string.format("GetItemInfo returned nil for bindType (arg 14)") end
|
||||
return bindType
|
||||
end
|
||||
|
||||
---@class Filter
|
||||
---@field requires table<string, fun(container:number, slot: number): string|number|boolean> | nil
|
||||
---@field filter fun(container: number, slot: number, provided: table<string, string|number|boolean>): boolean
|
||||
Filter = {
|
||||
---@param requires table<string, fun(container:number, slot: number): string|number|boolean> | nil
|
||||
---@param filter fun(container: number, slot: number, provided: table<string, string|number|boolean>): boolean
|
||||
---@return Filter
|
||||
new = function(requires, filter)
|
||||
local self = setmetatable({}, {
|
||||
__index = Filter
|
||||
})
|
||||
self.requires = requires
|
||||
self.filter = filter
|
||||
return self
|
||||
end,
|
||||
|
||||
---@param self Filter
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return boolean, string|nil
|
||||
Run = function(self, container, slot)
|
||||
---@type table<string, string|number|boolean>
|
||||
local provided = {}
|
||||
if self.requires then
|
||||
for k, v in pairs(self.requires) do
|
||||
local res, err = v(container, slot)
|
||||
if err then
|
||||
if debug then print(err) end
|
||||
return false, err
|
||||
end
|
||||
provided[k] = res
|
||||
end
|
||||
end
|
||||
local res, err = self.filter(container, slot, provided)
|
||||
if err then
|
||||
if debug then print(err) end
|
||||
end
|
||||
return res, nil
|
||||
end
|
||||
}
|
||||
|
||||
local grayFilter = Filter.new({
|
||||
["quality"] = getItemQuality
|
||||
},
|
||||
function(container, slot, provided)
|
||||
if provided.quality == 0 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
||||
local gearFilter = Filter.new({
|
||||
["ilvl"] = getItemLevel,
|
||||
["bindType"] = getBindType,
|
||||
["type"] = getItemType,
|
||||
["equipLoc"] = getItemEquipLocation
|
||||
},
|
||||
function(container, slot, provided)
|
||||
local ilvlThreshold = 850
|
||||
local sellBoe = false
|
||||
|
||||
if provided.type == "Armor"
|
||||
or provided.type == "Weapon"
|
||||
or provided.equipLoc == "INVTYPE_FINGER"
|
||||
or provided.equipLoc == "INVTYPE_TRINKET"
|
||||
or provided.equipLoc == "INVTYPE_CLOAK"
|
||||
or provided.equipLoc == "INVTYPE_NECK" then
|
||||
if provided.ilvl < ilvlThreshold and (provided.bindType == 1 or sellBoe) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return nil, string|nil
|
||||
local function doSell(container, slot)
|
||||
local itemIcon, err = getItemIcon(container, slot)
|
||||
if err then return nil, string.format("Error getting item icon: %s", err) end
|
||||
local itemName, err = getItemName(container, slot)
|
||||
if err then return nil, string.format("Error getting item name: %s", err) end
|
||||
local itemQuantity, err = getItemQuantity(container, slot)
|
||||
if err then return nil, string.format("Error getting item quantity: %s", err) end
|
||||
local itemQuality, err = getItemQuality(container, slot)
|
||||
if err then return nil, string.format("Error getting item quality: %s", err) end
|
||||
|
||||
local nameWithColor = string.format("%s[%s]\124r", aura_env.qualityColors[itemQuality + 1], itemName)
|
||||
local nameWithQuantity = string.format("%s x%s", nameWithColor, itemQuantity)
|
||||
|
||||
aura_env.allstates[#aura_env.allstates + 1] = {
|
||||
show = true,
|
||||
changed = true,
|
||||
index = GetTime(),
|
||||
resort = true,
|
||||
|
||||
icon = itemIcon,
|
||||
name = nameWithQuantity,
|
||||
amount = itemQuantity,
|
||||
|
||||
progressType = "timed",
|
||||
expirationTime = GetTime() + iconDisplayDuration,
|
||||
duration = iconDisplayDuration,
|
||||
autoHide = true,
|
||||
}
|
||||
|
||||
UseContainerItem(container, slot)
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
---@type table<Filter>
|
||||
local filters = {
|
||||
grayFilter,
|
||||
-- gearFilter
|
||||
}
|
||||
|
||||
---@class FilterService
|
||||
aura_env.FilterService = {
|
||||
---@param container number
|
||||
---@param slot number
|
||||
---@return nil, nil
|
||||
Run = function(container, slot)
|
||||
local itemLink, err = getItemLink(container, slot)
|
||||
if err then return nil, string.format("Err: slot empty (%d-%d) - %s", container, slot, err) end
|
||||
for k, filter in pairs(filters) do
|
||||
local res, err = filter:Run(container, slot)
|
||||
if err then
|
||||
if debug then print(err) end
|
||||
else
|
||||
if res then
|
||||
doSell(container, slot)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
aura_env.qualityColors = {
|
||||
"\124cff9d9d9d", -- Poor
|
||||
"\124cffffffff", -- Common
|
||||
"\124cff1eff00", -- Uncommon
|
||||
"\124cff0070dd", -- Rare
|
||||
"\124cffa335ee", -- Epic
|
||||
"\124cffff8000", -- Legendary
|
||||
"\124cffe6cc80", -- Artifact
|
||||
"\124cff00ccff", -- Heirloom
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
function()
|
||||
return BattlepassInfo.currentDayOnlineTimeMinutes, 120, 1
|
||||
function()
|
||||
return BattlepassInfo.currentDayOnlineTimeMinutes, 120, 1
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
--- TICKER_10000
|
||||
function()
|
||||
BattlepassInfo()
|
||||
--- TICKER_10000
|
||||
function()
|
||||
BattlepassInfo()
|
||||
end
|
||||
@@ -1,13 +1,13 @@
|
||||
--- COMBAT_LOG_EVENT_UNFILTERED
|
||||
---@param e string
|
||||
---@param ... any
|
||||
function(allstates, e, ...)
|
||||
local spellId, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
local spellname, err = CLEUParser.GetSpellName(...)
|
||||
if err then return end
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
|
||||
aura_env.LogSpell(spellname, spellId, subevent, ...)
|
||||
end
|
||||
--- COMBAT_LOG_EVENT_UNFILTERED
|
||||
---@param e string
|
||||
---@param ... any
|
||||
function(allstates, e, ...)
|
||||
local spellId, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
local spellname, err = CLEUParser.GetSpellName(...)
|
||||
if err then return end
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
|
||||
aura_env.LogSpell(spellname, spellId, subevent, ...)
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,24 +1,24 @@
|
||||
-- UNIT_SPELLCAST_SENT
|
||||
---@param allstates allstates
|
||||
---@param e string
|
||||
---@param source string
|
||||
---@param spellname string
|
||||
---@param _ string
|
||||
---@param target string
|
||||
function(allstates, e, source, spellname, _, target)
|
||||
if source ~= "player" then return end
|
||||
if spellname ~= "Mining" then return end
|
||||
if aura_env.depositIterator[target] == nil then aura_env.depositIterator[target] = 0 end
|
||||
local state = {
|
||||
changed = true,
|
||||
show = true,
|
||||
progressType = "timed",
|
||||
autoHide = true,
|
||||
duration = aura_env.cooldown,
|
||||
expirationTime = GetTime() + aura_env.cooldown,
|
||||
name = string.format("%s #%d", target, aura_env.depositIterator[target]),
|
||||
}
|
||||
allstates[aura_env.depositIterator[target]] = state
|
||||
aura_env.depositIterator[target] = aura_env.depositIterator[target] + 1
|
||||
return true
|
||||
-- UNIT_SPELLCAST_SENT
|
||||
---@param allstates allstates
|
||||
---@param e string
|
||||
---@param source string
|
||||
---@param spellname string
|
||||
---@param _ string
|
||||
---@param target string
|
||||
function(allstates, e, source, spellname, _, target)
|
||||
if source ~= "player" then return end
|
||||
if spellname ~= "Mining" then return end
|
||||
if aura_env.depositIterator[target] == nil then aura_env.depositIterator[target] = 0 end
|
||||
local state = {
|
||||
changed = true,
|
||||
show = true,
|
||||
progressType = "timed",
|
||||
autoHide = true,
|
||||
duration = aura_env.cooldown,
|
||||
expirationTime = GetTime() + aura_env.cooldown,
|
||||
name = string.format("%s #%d", target, aura_env.depositIterator[target]),
|
||||
}
|
||||
allstates[aura_env.depositIterator[target]] = state
|
||||
aura_env.depositIterator[target] = aura_env.depositIterator[target] + 1
|
||||
return true
|
||||
end
|
||||
@@ -1,2 +1,2 @@
|
||||
rt
|
||||
rt
|
||||
!LJrBtnoVX)l05M5UPeMeia39HBMM3aY1qcnY8WD94CIWwXrhosPs2qG26F7DxjlBhiaPx78mtNoWKOSA1Q99xKFdFpFI2NS)EnoAVd8jl8jH4)a0iL8EFs3rxn0NCJufYuTPb3gQKl9jTJ5p(ivfMn1tkJt4aOvJMntZs8pP5No(Jh(PpEqJM738OgnB6pRwtGYp42)WJoQ5rFS5N(y96np44Jpc3VPpHkcMlvxi5IeFsNEd96n2DVDKXsfWsdB4pSU)W9nFEG5ZMiq4a6L0agUfHgKWLcTrUsOQeybS)mUGRNBxdlTqH1jkEuetPTu3RaaUmKDt6SzEpSK5toR3Glo5YbW(MFstv09XBLfh3puBPRo9g2DmrcbofFLpzsNweVjeVwJbILIxkzzm9bG4ebDbl)uMJaxaJgNmVKixOygIqUO3GbiEPIswlXY8bY40fcIv0B4tu0qEQg0c1Rd4Rzd4lGl9gGDJ5rIsTkOxmecuG49nMfLRXaAglPgRpC8jbXuTgppiNbiWfPGT2P6S7(uOA(JSNceaFtURtvtzdJPSHXu2WykRV3HiYubFbnHzU5aASv4IncZWdDof4U2v9cJatc5VLsvSSPNKghNn9Q5CGaeLr0SMWWhaTopaCRtx6okb5w0RrlbpfIqkyOAvmJhzvhrkEO94J7A2b0DIKtaNmqmiJB1T)Le4AKju0RZOrTuo3BhKky37lTrjYG7a)ndYhx)a0sXHiUopCl9CUG1P7Pw(lfHYpn58U9Vk(qnedotbonG)jnHAOKnG5eeQLdjDg3RhGOMfplpoYB0fogQVWWpnSY0v8qWBd1M00eGmJwMu6dqvbdyIieHdoQUrSd5v2hintjOX)Mtq2)iNrd)zLipsieOGaNyJB0mKwAxyKrFdeCbLl2ESlIL3o8ToRfb4qMc1T5IFJ6Bu(d5Amq1JTc(X7wwM7cnVBmpLHOJxZlaC4rxnr4wK9AMe8PIPraRp6sVb9h2duYGSmj4vdxSz(kT2fznPQxkoRYLgY00eubAc0wQKrkMwd5Dc4ANuGmMp5ef)XSP)LuAi68b58DQrRA5BguDjo(ppxHEHuMm)ICgYVTjzuRWFMQtyHSqimbz4QH2y6yqObdpyYsvaKEX3Dz)SPTJPIBZzwp3E9r33zqa51DO6eUiQnvD9L9Rv(RAe8agFdjE)BiOLgVCUno0ePgF6TN3F0Xxr)2prh0vmxeMjxSmnjMJPwkSUvuFF1O(caPtUa5XlxgI2esA(3itGrwbwksAHm1Rw4Y6(BPO7Bxic4IYCuEJf42Q6rVC5m3TwRw20lh2hQ)HhWwjeCpZ(sTA1(tGWtxKnLghB4hD1LvrasNRb5seTguTmvf8cBHfKrUzJ7ozJqHuvrSKYTMLkmTq8(cEAxGt21DV7w5wG1t21rHpK9LSP2)4Zk5YS)XNZMUJvtTdG7CMiBQIbUIW3mr4tovPaypi4TdC1BFqmeyctC3EHSLsnpPp4JtHIuF3YC)iB6NbYj4XokTfhiBA9NCtXsOEmQQa1IbH)E5EU)cMtfrSqZ2jQuqj9mu0ZL3)A77sgHUMg82jHVGfUZgqfJooJhYEnY5ktyWPqUdGoxdL3l2WbyRwYThXJVWs6tzj463)HSP)XTJiwR4NDow7ntQGW43VZ7a39)W7qHj3Xz332y8Hk0)FwUSWd97Vfb(roNG2TYJVnUaVnsG(OrjrDEOOTi7lG3ZB0yCWCwWTfPDwVJAdqOO4gsKuKxBoy8HuEO)XMAtglM)tte9DyUuXdfPATOnqgXdGu2UG(QHXfIY33)h5Ic1qO8ZEUeU6H1GUi(FWMIkAjVV4efwMh72gQYC7dDZjGbs1XdYl0uTK7eSSBc2GHNT67e9Ckeq8vxhtnChaOuIPHkdw2LVtK)ZTOphdE2(r6TAjMD4E6dRDNBrJwvW(BooSwdh8YoPADP3Okxz(yc1RaYw107S(D(Zff1nBUw)H9hoS34jd6DINRXXVvPvsd(yBn8zGKyXYmkbZ265WrivTN7Rp9C3bDPDdmBuBu9FWVM6F5)xO(h3)0Z2c9FoA)AgGM)Aga9VKbyt9J)7OHOX(pZqm0m48gmaDgnCIBSK3Ya4WRQfOAOZlAbUX38WaEvu15Vrq(SV5mLzYOcyYsQLdj4TvC5ywA7DqyMhHOr20lGM)JlaBgXbOHjJ7CgpAogTwpFaG2XqrftvesRUD)38vsw3Gu9rlm0gAkkKb5xh273kFjnxU(2p)j9wFIjZ0NAtP184cecwryBgj1C)pvyDJhNlVgpRn8yDeno0C1P0AfgosOV(kg92wqvh91NZc50RjiE6RBlxbDx1sXeudK9Krrqrq5elDUz9IJqL)Lf78F5RQDLhxeHMpku7YHIEwBJMoS(0HzF5L6YY2Kn0Yx(OXOISZBBbEsrL7bB9vkkyLVQy16V6uoPrLYtFNP7TdYAN69LEZP1NqNUcjexGdnBcsmeVR9bhmp3GuXHw7STzqoB04()1rd9An45V10JsqXHXORkJ(nQI7aMR5(O06)Vc
|
||||
@@ -1,2 +1,2 @@
|
||||
aura_env.cooldown = 205
|
||||
aura_env.depositIterator = {}
|
||||
aura_env.cooldown = 205
|
||||
aura_env.depositIterator = {}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
-- TICKER_500
|
||||
function(e)
|
||||
if aura_env.ActiveSet == nil then
|
||||
aura_env.ActiveSet = aura_env.Cooldowns[UnitClass("player")]
|
||||
if aura_env.ActiveSet == nil then
|
||||
aura_env.ActiveSet = {}
|
||||
end
|
||||
end
|
||||
for _, cooldown in ipairs(aura_env.ActiveSet) do
|
||||
if aura_env.debug then print(string.format("Calling update on %s", cooldown.name)) end
|
||||
cooldown:Update()
|
||||
end
|
||||
-- TICKER_500
|
||||
function(e)
|
||||
if aura_env.ActiveSet == nil then
|
||||
aura_env.ActiveSet = aura_env.Cooldowns[UnitClass("player")]
|
||||
if aura_env.ActiveSet == nil then
|
||||
aura_env.ActiveSet = {}
|
||||
end
|
||||
end
|
||||
for _, cooldown in ipairs(aura_env.ActiveSet) do
|
||||
if aura_env.debug then print(string.format("Calling update on %s", cooldown.name)) end
|
||||
cooldown:Update()
|
||||
end
|
||||
end
|
||||
@@ -1,189 +1,189 @@
|
||||
aura_env.debug = true
|
||||
|
||||
---@class BaseCooldown
|
||||
---@field name string
|
||||
---@field filename string
|
||||
---@field id number
|
||||
---@field isOnCooldown boolean
|
||||
---@field announced boolean
|
||||
---@field remaining number
|
||||
aura_env.BaseCooldown = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return BaseCooldown
|
||||
new = function(id, name, playbackSpeed)
|
||||
local self = setmetatable({}, {
|
||||
__index = aura_env.BaseCooldown
|
||||
})
|
||||
self.id = id
|
||||
self.name = name
|
||||
name = string.gsub(name, " ", "_")
|
||||
name = string.gsub(name, "'", "")
|
||||
name = string.lower(name)
|
||||
self.filename = name
|
||||
if playbackSpeed == nil then
|
||||
self.filename = string.format("1x\\%s", self.filename)
|
||||
else
|
||||
self.filename = string.format("%.1fx\\%s", playbackSpeed, self.filename)
|
||||
end
|
||||
if aura_env.debug then print(string.format("Created BaseCooldown with filename %s", self.filename)) end
|
||||
self.isOnCooldown = false
|
||||
self.announced = false
|
||||
self.remaining = 0
|
||||
return self
|
||||
end,
|
||||
|
||||
---@param self BaseCooldown
|
||||
---@param cooldownFunc fun(spellId: number): number, number
|
||||
Update = function(self, cooldownFunc)
|
||||
local start, duration = cooldownFunc(self.id)
|
||||
local isOnCooldown = start > 0 and duration > 3
|
||||
local remaining = start + duration - GetTime()
|
||||
|
||||
if not isOnCooldown and self.isOnCooldown then
|
||||
self:playComplete()
|
||||
self.announced = false
|
||||
end
|
||||
if not self.announced and isOnCooldown and remaining < 10 then
|
||||
self:playSoon()
|
||||
self.announced = true
|
||||
end
|
||||
|
||||
self.isOnCooldown = isOnCooldown
|
||||
self.remaining = remaining
|
||||
end,
|
||||
|
||||
---@param self BaseCooldown
|
||||
playComplete = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s.ogg", self.filename)
|
||||
if aura_env.debug then
|
||||
print(string.format("%s is off cooldown, playing sound file at %s", self.filename,
|
||||
soundFile))
|
||||
end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end,
|
||||
|
||||
---@param self BaseCooldown
|
||||
playSoon = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s_soon.ogg", self.filename)
|
||||
if aura_env.debug then
|
||||
print(string.format("%s is almost off cooldown, playing sound file at %s", self.filename,
|
||||
soundFile))
|
||||
end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end
|
||||
}
|
||||
|
||||
---@class Spell:BaseCooldown
|
||||
aura_env.Spell = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return Spell
|
||||
new = function(id, name, playbackSpeed)
|
||||
local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
|
||||
---@cast obj Spell
|
||||
setmetatable(obj, {
|
||||
__index = aura_env.Spell
|
||||
})
|
||||
return obj
|
||||
end,
|
||||
Update = function(self)
|
||||
aura_env.BaseCooldown.Update(self, GetSpellCooldown)
|
||||
end,
|
||||
playComplete = aura_env.BaseCooldown.playComplete,
|
||||
playSoon = aura_env.BaseCooldown.playSoon
|
||||
}
|
||||
---@class Item:BaseCooldown
|
||||
aura_env.Item = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return Item
|
||||
new = function(id, name, playbackSpeed)
|
||||
local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
|
||||
---@cast obj Item
|
||||
setmetatable(obj, {
|
||||
__index = aura_env.Item
|
||||
})
|
||||
return obj
|
||||
end,
|
||||
Update = function(self)
|
||||
aura_env.BaseCooldown.Update(self, GetItemCooldown)
|
||||
end,
|
||||
playComplete = aura_env.BaseCooldown.playComplete,
|
||||
playSoon = aura_env.BaseCooldown.playSoon
|
||||
}
|
||||
---@class Buff:BaseCooldown
|
||||
---@field isActive boolean
|
||||
aura_env.Buff = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return Buff
|
||||
new = function(id, name, playbackSpeed)
|
||||
local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
|
||||
---@cast obj Buff
|
||||
setmetatable(obj, {
|
||||
__index = aura_env.Buff
|
||||
})
|
||||
obj.isActive = false
|
||||
return obj
|
||||
end,
|
||||
Update = function(self)
|
||||
local duration, expiry = select(6, UnitBuff("player", self.name))
|
||||
|
||||
local isActive = true
|
||||
if duration == nil or expiry == nil then
|
||||
isActive = false
|
||||
expiry = 0
|
||||
end
|
||||
local remaining = expiry - GetTime()
|
||||
|
||||
if not isActive and self.isActive then
|
||||
self:playComplete() -- More like "PlayExpired"
|
||||
self.announced = false
|
||||
end
|
||||
if not self.announced and isActive and remaining < 5 then
|
||||
self:playSoon() -- More like "PlayExpiring"
|
||||
self.announced = true
|
||||
end
|
||||
|
||||
self.isActive = isActive
|
||||
self.remaining = remaining
|
||||
end,
|
||||
---@param self Buff
|
||||
playComplete = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s_expired.ogg", self.filename)
|
||||
if aura_env.debug then print(string.format("%s expired, playing sound file at %s", self.filename, soundFile)) end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end,
|
||||
---@param self Buff
|
||||
playSoon = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s_expiring.ogg", self.filename)
|
||||
if aura_env.debug then print(string.format("%s expiring, playing sound file at %s", self.filename, soundFile)) end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end
|
||||
}
|
||||
|
||||
---@type table<BaseCooldown>|nil
|
||||
aura_env.ActiveSet = nil
|
||||
|
||||
---@type table<string, table<BaseCooldown>>
|
||||
aura_env.Cooldowns = {
|
||||
["Warrior"] = {
|
||||
aura_env.Spell.new(107574, "Avatar", 1.6),
|
||||
aura_env.Spell.new(1719, "Battle Cry", 1.6),
|
||||
aura_env.Spell.new(205545, "Odyns Fury", 1.6),
|
||||
aura_env.Spell.new(26297, "Berserking", 1.6),
|
||||
aura_env.Spell.new(12292, "Bloodbath", 1.6),
|
||||
},
|
||||
["Warlock"] = {
|
||||
aura_env.Spell.new(108416, "Dark Pact", 1.6),
|
||||
aura_env.Spell.new(104773, "Unending Resolve", 1.6),
|
||||
aura_env.Spell.new(196098, "Soul Harvest", 1.6),
|
||||
aura_env.Item.new(5512, "Healthstone", 1.6),
|
||||
aura_env.Buff.new(0, "Deadwind Harvester", 1.6),
|
||||
}
|
||||
}
|
||||
aura_env.debug = true
|
||||
|
||||
---@class BaseCooldown
|
||||
---@field name string
|
||||
---@field filename string
|
||||
---@field id number
|
||||
---@field isOnCooldown boolean
|
||||
---@field announced boolean
|
||||
---@field remaining number
|
||||
aura_env.BaseCooldown = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return BaseCooldown
|
||||
new = function(id, name, playbackSpeed)
|
||||
local self = setmetatable({}, {
|
||||
__index = aura_env.BaseCooldown
|
||||
})
|
||||
self.id = id
|
||||
self.name = name
|
||||
name = string.gsub(name, " ", "_")
|
||||
name = string.gsub(name, "'", "")
|
||||
name = string.lower(name)
|
||||
self.filename = name
|
||||
if playbackSpeed == nil then
|
||||
self.filename = string.format("1x\\%s", self.filename)
|
||||
else
|
||||
self.filename = string.format("%.1fx\\%s", playbackSpeed, self.filename)
|
||||
end
|
||||
if aura_env.debug then print(string.format("Created BaseCooldown with filename %s", self.filename)) end
|
||||
self.isOnCooldown = false
|
||||
self.announced = false
|
||||
self.remaining = 0
|
||||
return self
|
||||
end,
|
||||
|
||||
---@param self BaseCooldown
|
||||
---@param cooldownFunc fun(spellId: number): number, number
|
||||
Update = function(self, cooldownFunc)
|
||||
local start, duration = cooldownFunc(self.id)
|
||||
local isOnCooldown = start > 0 and duration > 3
|
||||
local remaining = start + duration - GetTime()
|
||||
|
||||
if not isOnCooldown and self.isOnCooldown then
|
||||
self:playComplete()
|
||||
self.announced = false
|
||||
end
|
||||
if not self.announced and isOnCooldown and remaining < 10 then
|
||||
self:playSoon()
|
||||
self.announced = true
|
||||
end
|
||||
|
||||
self.isOnCooldown = isOnCooldown
|
||||
self.remaining = remaining
|
||||
end,
|
||||
|
||||
---@param self BaseCooldown
|
||||
playComplete = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s.ogg", self.filename)
|
||||
if aura_env.debug then
|
||||
print(string.format("%s is off cooldown, playing sound file at %s", self.filename,
|
||||
soundFile))
|
||||
end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end,
|
||||
|
||||
---@param self BaseCooldown
|
||||
playSoon = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s_soon.ogg", self.filename)
|
||||
if aura_env.debug then
|
||||
print(string.format("%s is almost off cooldown, playing sound file at %s", self.filename,
|
||||
soundFile))
|
||||
end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end
|
||||
}
|
||||
|
||||
---@class Spell:BaseCooldown
|
||||
aura_env.Spell = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return Spell
|
||||
new = function(id, name, playbackSpeed)
|
||||
local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
|
||||
---@cast obj Spell
|
||||
setmetatable(obj, {
|
||||
__index = aura_env.Spell
|
||||
})
|
||||
return obj
|
||||
end,
|
||||
Update = function(self)
|
||||
aura_env.BaseCooldown.Update(self, GetSpellCooldown)
|
||||
end,
|
||||
playComplete = aura_env.BaseCooldown.playComplete,
|
||||
playSoon = aura_env.BaseCooldown.playSoon
|
||||
}
|
||||
---@class Item:BaseCooldown
|
||||
aura_env.Item = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return Item
|
||||
new = function(id, name, playbackSpeed)
|
||||
local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
|
||||
---@cast obj Item
|
||||
setmetatable(obj, {
|
||||
__index = aura_env.Item
|
||||
})
|
||||
return obj
|
||||
end,
|
||||
Update = function(self)
|
||||
aura_env.BaseCooldown.Update(self, GetItemCooldown)
|
||||
end,
|
||||
playComplete = aura_env.BaseCooldown.playComplete,
|
||||
playSoon = aura_env.BaseCooldown.playSoon
|
||||
}
|
||||
---@class Buff:BaseCooldown
|
||||
---@field isActive boolean
|
||||
aura_env.Buff = {
|
||||
---@param id number
|
||||
---@param name string
|
||||
---@param playbackSpeed number?
|
||||
---@return Buff
|
||||
new = function(id, name, playbackSpeed)
|
||||
local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
|
||||
---@cast obj Buff
|
||||
setmetatable(obj, {
|
||||
__index = aura_env.Buff
|
||||
})
|
||||
obj.isActive = false
|
||||
return obj
|
||||
end,
|
||||
Update = function(self)
|
||||
local duration, expiry = select(6, UnitBuff("player", self.name))
|
||||
|
||||
local isActive = true
|
||||
if duration == nil or expiry == nil then
|
||||
isActive = false
|
||||
expiry = 0
|
||||
end
|
||||
local remaining = expiry - GetTime()
|
||||
|
||||
if not isActive and self.isActive then
|
||||
self:playComplete() -- More like "PlayExpired"
|
||||
self.announced = false
|
||||
end
|
||||
if not self.announced and isActive and remaining < 5 then
|
||||
self:playSoon() -- More like "PlayExpiring"
|
||||
self.announced = true
|
||||
end
|
||||
|
||||
self.isActive = isActive
|
||||
self.remaining = remaining
|
||||
end,
|
||||
---@param self Buff
|
||||
playComplete = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s_expired.ogg", self.filename)
|
||||
if aura_env.debug then print(string.format("%s expired, playing sound file at %s", self.filename, soundFile)) end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end,
|
||||
---@param self Buff
|
||||
playSoon = function(self)
|
||||
local soundFile = string.format("Interface\\Sounds\\alerts\\%s_expiring.ogg", self.filename)
|
||||
if aura_env.debug then print(string.format("%s expiring, playing sound file at %s", self.filename, soundFile)) end
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
|
||||
end
|
||||
}
|
||||
|
||||
---@type table<BaseCooldown>|nil
|
||||
aura_env.ActiveSet = nil
|
||||
|
||||
---@type table<string, table<BaseCooldown>>
|
||||
aura_env.Cooldowns = {
|
||||
["Warrior"] = {
|
||||
aura_env.Spell.new(107574, "Avatar", 1.6),
|
||||
aura_env.Spell.new(1719, "Battle Cry", 1.6),
|
||||
aura_env.Spell.new(205545, "Odyns Fury", 1.6),
|
||||
aura_env.Spell.new(26297, "Berserking", 1.6),
|
||||
aura_env.Spell.new(12292, "Bloodbath", 1.6),
|
||||
},
|
||||
["Warlock"] = {
|
||||
aura_env.Spell.new(108416, "Dark Pact", 1.6),
|
||||
aura_env.Spell.new(104773, "Unending Resolve", 1.6),
|
||||
aura_env.Spell.new(196098, "Soul Harvest", 1.6),
|
||||
aura_env.Item.new(5512, "Healthstone", 1.6),
|
||||
aura_env.Buff.new(0, "Deadwind Harvester", 1.6),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
-- COMBAT_LOG_EVENT_UNFILTERED
|
||||
-- For some reason the non lua version does not work (with status > event > spell aura applied...)
|
||||
function(e, ...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
if subevent == "SPELL_AURA_APPLIED" then
|
||||
local spellid, err = CLEUParser.GetSpellId(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
local target, err = CLEUParser.GetDestName(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
if spellid == 207472 and UnitName("player") == target then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- COMBAT_LOG_EVENT_UNFILTERED
|
||||
-- For some reason the non lua version does not work (with status > event > spell aura applied...)
|
||||
function(e, ...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
if subevent == "SPELL_AURA_APPLIED" then
|
||||
local spellid, err = CLEUParser.GetSpellId(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
local target, err = CLEUParser.GetDestName(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
if spellid == 207472 and UnitName("player") == target then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
function()
|
||||
local target = aura_env.state.target
|
||||
|
||||
for _, subregion in ipairs(aura_env.region.subRegions) do
|
||||
if aura_env.config.borderColor then
|
||||
if subregion.type == "subborder" then
|
||||
---@type ClassColor
|
||||
local classColorInfo = aura_env.ClassColors[aura_env.state.targetClassId]
|
||||
if classColorInfo then
|
||||
local color = classColorInfo.color.rgbInt
|
||||
subregion:SetBorderColor(color[1] / 255, color[2] / 255, color[3] / 255, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
if target and target == PlayerName then
|
||||
if aura_env.config.glow then
|
||||
if subregion.type == "subglow" then
|
||||
subregion:SetVisible(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target and target == PlayerName then
|
||||
return 1, 0, 0, 1
|
||||
end
|
||||
return 0, 0, 1, 1
|
||||
end
|
||||
function()
|
||||
local target = aura_env.statee.target
|
||||
|
||||
for _, subregion in ipairs(aura_env.region.subRegions) do
|
||||
if aura_env.config.borderColor then
|
||||
if subregion.type == "subborder" then
|
||||
---@type ClassColor
|
||||
local classColorInfo = aura_env.ClassColors[aura_env.statee.targetClassId]
|
||||
if classColorInfo then
|
||||
local color = classColorInfo.color.rgbInt
|
||||
subregion:SetBorderColor(color[1] / 255, color[2] / 255, color[3] / 255, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
if target and target == PlayerName then
|
||||
if aura_env.config.glow then
|
||||
if subregion.type == "subglow" then
|
||||
subregion:SetVisible(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target and target == PlayerName then
|
||||
return 1, 0, 0, 1
|
||||
end
|
||||
return 0, 0, 1, 1
|
||||
end
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
--- COMBAT_LOG_EVENT_UNFILTERED
|
||||
---@param e string
|
||||
---@param ... any
|
||||
function(allstates, e, ...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
local spellId, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
|
||||
local spellname, err = CLEUParser.GetSpellName(...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
aura_env.LogSpell(spellname, spellId, subevent, ...)
|
||||
|
||||
local eventMap = aura_env.EventMap[subevent]
|
||||
if eventMap == nil then return end
|
||||
local alert = eventMap[spellId]
|
||||
if alert == nil then return end
|
||||
|
||||
local err = alert:Trigger(allstates, ...)
|
||||
if err then print(err) end
|
||||
|
||||
-- aura_env.LogSpell = function(spellName, spellId, subevent, ...)
|
||||
-- WeakAurasSaved.Cyka.CLEUExample[#WeakAurasSaved.Cyka.CLEUExample + 1] = varargToString(spellName, spellId, subevent, ...)
|
||||
-- end
|
||||
return true
|
||||
end
|
||||
--- COMBAT_LOG_EVENT_UNFILTERED
|
||||
---@param e string
|
||||
---@param ... any
|
||||
function(allstates, e, ...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
local spellId, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
|
||||
local spellname, err = CLEUParser.GetSpellName(...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
aura_env.LogSpell(spellname, spellId, subevent, ...)
|
||||
|
||||
local eventMap = aura_env.EventMap[subevent]
|
||||
if eventMap == nil then return end
|
||||
local alert = eventMap[spellId]
|
||||
if alert == nil then return end
|
||||
|
||||
local err = alert:Trigger(allstates, ...)
|
||||
if err then print(err) end
|
||||
|
||||
-- aura_env.LogSpell = function(spellName, spellId, subevent, ...)
|
||||
-- WeakAurasSaved.Cyka.CLEUExample[#WeakAurasSaved.Cyka.CLEUExample + 1] = varargToString(spellName, spellId, subevent, ...)
|
||||
-- end
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
-- TICKER_100
|
||||
function()
|
||||
aura_env.GUIDUnitMap = {}
|
||||
for i = 1, 40 do
|
||||
local unit = string.format("nameplate%d", i)
|
||||
local GUID = UnitGUID(unit)
|
||||
if GUID then
|
||||
aura_env.GUIDUnitMap[GUID] = unit
|
||||
end
|
||||
end
|
||||
-- TICKER_100
|
||||
function()
|
||||
aura_env.GUIDUnitMap = {}
|
||||
for i = 1, 40 do
|
||||
local unit = string.format("nameplate%d", i)
|
||||
local GUID = UnitGUID(unit)
|
||||
if GUID then
|
||||
aura_env.GUIDUnitMap[GUID] = unit
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,13 +1,13 @@
|
||||
--- COMBAT_LOG_EVENT_UNFILTERED
|
||||
---@param e string
|
||||
---@param ... any
|
||||
function(e, ...)
|
||||
local spellName, err = CLEUParser.GetSpellName(...)
|
||||
if err then return end
|
||||
local spellId, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
aura_env.LogSpell(spellName, spellId, subevent, ...)
|
||||
end
|
||||
--- COMBAT_LOG_EVENT_UNFILTERED
|
||||
---@param e string
|
||||
---@param ... any
|
||||
function(e, ...)
|
||||
local spellName, err = CLEUParser.GetSpellName(...)
|
||||
if err then return end
|
||||
local spellId, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
aura_env.LogSpell(spellName, spellId, subevent, ...)
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
local function varargToString(...)
|
||||
local output = {}
|
||||
for i = 1, select("#", ...) do
|
||||
table.insert(output, tostring(select(i, ...)))
|
||||
end
|
||||
print(table.concat(output, ", "))
|
||||
end
|
||||
|
||||
test("Hello", "world", "again", 2323, true)
|
||||
local function varargToString(...)
|
||||
local output = {}
|
||||
for i = 1, select("#", ...) do
|
||||
table.insert(output, tostring(select(i, ...)))
|
||||
end
|
||||
print(table.concat(output, ", "))
|
||||
end
|
||||
|
||||
test("Hello", "world", "again", 2323, true)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,27 @@
|
||||
-- COMBAT_LOG_EVENT_UNFILTERED
|
||||
-- For some reason the non lua version does not work (with status > event > spell aura applied...)
|
||||
function(e, ...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
if subevent == "SPELL_AURA_APPLIED" then
|
||||
local spellid, err = CLEUParser.GetSpellId(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
local target, err = CLEUParser.GetDestName(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
-- print(target, spellid)
|
||||
if spellid == 208052 and UnitName("player") == target then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- COMBAT_LOG_EVENT_UNFILTERED
|
||||
-- For some reason the non lua version does not work (with status > event > spell aura applied...)
|
||||
function(e, ...)
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
if subevent == "SPELL_AURA_APPLIED" then
|
||||
local spellid, err = CLEUParser.GetSpellId(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
|
||||
local target, err = CLEUParser.GetDestName(...)
|
||||
if err ~= nil then
|
||||
-- print(err)
|
||||
return
|
||||
end
|
||||
-- print(target, spellid)
|
||||
if spellid == 208052 and UnitName("player") == target then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
function()
|
||||
return aura_env.Display
|
||||
function()
|
||||
return aura_env.Display
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
-- TICKER_500
|
||||
function()
|
||||
aura_env.SoulShardStack:CleanOld()
|
||||
aura_env.AgonyStack:CleanOld()
|
||||
aura_env.UpdateDisplay()
|
||||
aura_env.SoulShards = UnitPower("player", 7)
|
||||
end
|
||||
-- TICKER_500
|
||||
function()
|
||||
aura_env.SoulShardStack:CleanOld()
|
||||
aura_env.AgonyStack:CleanOld()
|
||||
aura_env.UpdateDisplay()
|
||||
aura_env.SoulShards = UnitPower("player", 7)
|
||||
end
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
-- UNIT_POWER
|
||||
function(e, unit, power)
|
||||
if unit ~= "player" then return end
|
||||
if power ~= "SOUL_SHARDS" then return end
|
||||
local soulShards = UnitPower("player", 7)
|
||||
if soulShards == aura_env.SoulShards then return end
|
||||
if soulShards < aura_env.SoulShards then
|
||||
aura_env.SoulShards = soulShards
|
||||
return
|
||||
end
|
||||
aura_env.SoulShards = soulShards
|
||||
aura_env.SoulShardStack:Push(time())
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", "Interface\\Sounds\\quack.ogg")
|
||||
-- UNIT_POWER
|
||||
function(e, unit, power)
|
||||
if unit ~= "player" then return end
|
||||
if power ~= "SOUL_SHARDS" then return end
|
||||
local soulShards = UnitPower("player", 7)
|
||||
if soulShards == aura_env.SoulShards then return end
|
||||
if soulShards < aura_env.SoulShards then
|
||||
aura_env.SoulShards = soulShards
|
||||
return
|
||||
end
|
||||
aura_env.SoulShards = soulShards
|
||||
aura_env.SoulShardStack:Push(time())
|
||||
WeakAuras.ScanEvents("PLAY_SOUND", "Interface\\Sounds\\quack.ogg")
|
||||
end
|
||||
@@ -1,14 +1,14 @@
|
||||
-- COMBAT_LOG_EVENT_UNFILTERED
|
||||
function(e, ...)
|
||||
local caster, err = CLEUParser.GetSourceName(...)
|
||||
if err then return end
|
||||
if caster ~= aura_env.PlayerName then return end
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
if subevent ~= "SPELL_PERIODIC_DAMAGE" then return end
|
||||
local spellID, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
if spellID == 980 then
|
||||
aura_env.AgonyStack:Push(time())
|
||||
end
|
||||
-- COMBAT_LOG_EVENT_UNFILTERED
|
||||
function(e, ...)
|
||||
local caster, err = CLEUParser.GetSourceName(...)
|
||||
if err then return end
|
||||
if caster ~= aura_env.PlayerName then return end
|
||||
local subevent, err = CLEUParser.GetSubevent(...)
|
||||
if err then return end
|
||||
if subevent ~= "SPELL_PERIODIC_DAMAGE" then return end
|
||||
local spellID, err = CLEUParser.GetSpellId(...)
|
||||
if err then return end
|
||||
if spellID == 980 then
|
||||
aura_env.AgonyStack:Push(time())
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
-- PLAYER_REGEN_DISABLED
|
||||
function()
|
||||
if aura_env.config.resetOnCombat then
|
||||
aura_env.AgonyStack:CleanOld(0)
|
||||
aura_env.SoulShardStack:CleanOld(0)
|
||||
end
|
||||
-- PLAYER_REGEN_DISABLED
|
||||
function()
|
||||
if aura_env.config.resetOnCombat then
|
||||
aura_env.AgonyStack:CleanOld(0)
|
||||
aura_env.SoulShardStack:CleanOld(0)
|
||||
end
|
||||
end
|
||||
@@ -1,58 +1,58 @@
|
||||
---@class Stack
|
||||
---@field maxSize number
|
||||
---@field values table<any>
|
||||
aura_env.Stack = {
|
||||
---@param maxSize number
|
||||
---@return Stack
|
||||
new = function(maxSize)
|
||||
local self = setmetatable({}, {
|
||||
__index = aura_env.Stack
|
||||
})
|
||||
self.maxSize = maxSize
|
||||
self.values = {}
|
||||
return self
|
||||
end,
|
||||
---@param self Stack
|
||||
---@param value number
|
||||
Push = function(self, value)
|
||||
table.insert(self.values, 0, value)
|
||||
if #self.values > self.maxSize then
|
||||
table.remove(self.values, #self.values)
|
||||
end
|
||||
end,
|
||||
---@param self Stack
|
||||
---@return number
|
||||
PerSecond = function(self)
|
||||
local first = self.values[#self.values]
|
||||
local last = time()
|
||||
|
||||
if first and last then
|
||||
return #self.values / (math.max(last - first, 1))
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
---@param self Stack
|
||||
---@param threshold number?
|
||||
CleanOld = function(self, threshold)
|
||||
local timeThresh = time() - (threshold or aura_env.config.combatMemoryTime)
|
||||
-- print(string.format("Clean %d %d", #self.values, timeThresh))
|
||||
for i = #self.values, 1, -1 do
|
||||
if self.values[i] < timeThresh then
|
||||
table.remove(self.values, i)
|
||||
end
|
||||
end
|
||||
-- print(string.format("Clean after %d", #self.values))
|
||||
end,
|
||||
}
|
||||
|
||||
aura_env.SoulShardStack = aura_env.Stack.new(aura_env.config.soulShardStackMaxSize)
|
||||
aura_env.AgonyStack = aura_env.Stack.new(aura_env.config.agonyStackMaxSize)
|
||||
aura_env.Display = ""
|
||||
aura_env.SoulShards = 0
|
||||
aura_env.PlayerName = UnitName("player")
|
||||
|
||||
aura_env.UpdateDisplay = function()
|
||||
local shards = string.format("%-8s %-7.2f/m", "Shards: ", aura_env.SoulShardStack:PerSecond() * 60)
|
||||
local agony = string.format("%-8s %-7.2f/m", "Agony: ", aura_env.AgonyStack:PerSecond() * 60)
|
||||
aura_env.Display = string.format("%s\n%s", shards, agony)
|
||||
end
|
||||
---@class Stack
|
||||
---@field maxSize number
|
||||
---@field values table<any>
|
||||
aura_env.Stack = {
|
||||
---@param maxSize number
|
||||
---@return Stack
|
||||
new = function(maxSize)
|
||||
local self = setmetatable({}, {
|
||||
__index = aura_env.Stack
|
||||
})
|
||||
self.maxSize = maxSize
|
||||
self.values = {}
|
||||
return self
|
||||
end,
|
||||
---@param self Stack
|
||||
---@param value number
|
||||
Push = function(self, value)
|
||||
table.insert(self.values, 0, value)
|
||||
if #self.values > self.maxSize then
|
||||
table.remove(self.values, #self.values)
|
||||
end
|
||||
end,
|
||||
---@param self Stack
|
||||
---@return number
|
||||
PerSecond = function(self)
|
||||
local first = self.values[#self.values]
|
||||
local last = time()
|
||||
|
||||
if first and last then
|
||||
return #self.values / (math.max(last - first, 1))
|
||||
end
|
||||
return 0
|
||||
end,
|
||||
---@param self Stack
|
||||
---@param threshold number?
|
||||
CleanOld = function(self, threshold)
|
||||
local timeThresh = time() - (threshold or aura_env.config.combatMemoryTime)
|
||||
-- print(string.format("Clean %d %d", #self.values, timeThresh))
|
||||
for i = #self.values, 1, -1 do
|
||||
if self.values[i] < timeThresh then
|
||||
table.remove(self.values, i)
|
||||
end
|
||||
end
|
||||
-- print(string.format("Clean after %d", #self.values))
|
||||
end,
|
||||
}
|
||||
|
||||
aura_env.SoulShardStack = aura_env.Stack.new(aura_env.config.soulShardStackMaxSize)
|
||||
aura_env.AgonyStack = aura_env.Stack.new(aura_env.config.agonyStackMaxSize)
|
||||
aura_env.Display = ""
|
||||
aura_env.SoulShards = 0
|
||||
aura_env.PlayerName = UnitName("player")
|
||||
|
||||
aura_env.UpdateDisplay = function()
|
||||
local shards = string.format("%-8s %-7.2f/m", "Shards: ", aura_env.SoulShardStack:PerSecond() * 60)
|
||||
local agony = string.format("%-8s %-7.2f/m", "Agony: ", aura_env.AgonyStack:PerSecond() * 60)
|
||||
aura_env.Display = string.format("%s\n%s", shards, agony)
|
||||
end
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
-- TICKER_200
|
||||
function()
|
||||
for _, unit in ipairs(aura_env.Units) do
|
||||
if UnitExists(unit) and not UnitIsDeadOrGhost(unit) and aura_env.HasDispellableDebuff(unit) then
|
||||
local unitName = UnitName(unit)
|
||||
local frame = aura_env.GetFrameFromName(unitName)
|
||||
if frame == nil then
|
||||
print("Frame not found for " .. unitName)
|
||||
-- Unexpected behavior, bail!
|
||||
return
|
||||
end
|
||||
-- print("Dispel " .. unitName)
|
||||
aura_env.MakeFrameGlow(frame)
|
||||
end
|
||||
end
|
||||
-- TICKER_200
|
||||
function()
|
||||
for _, unit in ipairs(aura_env.Units) do
|
||||
if UnitExists(unit) and not UnitIsDeadOrGhost(unit) and aura_env.HasDispellableDebuff(unit) then
|
||||
local unitName = UnitName(unit)
|
||||
local frame = aura_env.GetFrameFromName(unitName)
|
||||
if frame == nil then
|
||||
print("Frame not found for " .. unitName)
|
||||
-- Unexpected behavior, bail!
|
||||
return
|
||||
end
|
||||
-- print("Dispel " .. unitName)
|
||||
aura_env.MakeFrameGlow(frame)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
-- TICKER_1000
|
||||
function()
|
||||
aura_env.SetupPartyCache()
|
||||
-- TICKER_1000
|
||||
function()
|
||||
aura_env.SetupPartyCache()
|
||||
end
|
||||
@@ -1,90 +1,90 @@
|
||||
LCG = LibStub("LibCustomGlow-1.0")
|
||||
---@table <string, boolean>
|
||||
aura_env.BusyFrames = {}
|
||||
|
||||
aura_env.Units = {
|
||||
"player",
|
||||
"party1",
|
||||
"party2",
|
||||
"party3",
|
||||
"party4",
|
||||
"party5",
|
||||
}
|
||||
|
||||
---@param unit string
|
||||
---@return boolean, nil|string
|
||||
aura_env.HasDispellableDebuff = function(unit)
|
||||
for i = 1, 20 do
|
||||
local debuff, _, _, _, type = UnitDebuff(unit, i)
|
||||
if debuff == nil then
|
||||
return false, nil
|
||||
end
|
||||
if type == "Magic" then
|
||||
return true, nil
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
---@param frame Frame
|
||||
---@return string, nil|string
|
||||
aura_env.GetNameFromFrame = function(frame)
|
||||
if frame == nil then
|
||||
return "", "Frame is nil"
|
||||
end
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
if frame.Name == nil then
|
||||
return "", "Frame.Name is nil"
|
||||
end
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
local name = frame.Name:GetText()
|
||||
if name == nil then
|
||||
return "", "Frame.Name.GetText is nil"
|
||||
end
|
||||
return name, nil
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return Frame|nil
|
||||
aura_env.GetFrameFromName = function(name)
|
||||
return aura_env.PartyCache[name]
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return string
|
||||
---@return number
|
||||
aura_env.RemoveNamePadding = function(name)
|
||||
return string.gsub(name, "|c%w%w%w%w%w%w%w%w", "", 1)
|
||||
end
|
||||
|
||||
aura_env.MakeFrameGlow = function(frame)
|
||||
local frameName = frame:GetName()
|
||||
if frame ~= nil and aura_env.BusyFrames[frameName] == nil then
|
||||
---@type Frame
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
local healthBar = frame.Health
|
||||
if healthBar ~= nil then
|
||||
LCG.PixelGlow_Start(healthBar, { 1, 1, 0, 1 }, 16, 0.35, 8, 4)
|
||||
aura_env.BusyFrames[frameName] = true
|
||||
local aura_env = aura_env
|
||||
C_Timer.After(1, function()
|
||||
LCG.PixelGlow_Stop(healthBar)
|
||||
aura_env.BusyFrames[frameName] = nil
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
aura_env.PartyCache = {}
|
||||
aura_env.SetupPartyCache = function()
|
||||
for i = 1, 5 do
|
||||
-- /dump _G[string.format("ElvUF_RaidGroup1UnitButton%d", 1)].Name:GetText()
|
||||
local raidFrame = _G[string.format("ElvUF_RaidGroup1UnitButton%d", i + 1)]
|
||||
local name, err = aura_env.GetNameFromFrame(raidFrame)
|
||||
if err == nil then
|
||||
name = aura_env.RemoveNamePadding(name)
|
||||
aura_env.PartyCache[name] = raidFrame
|
||||
end
|
||||
end
|
||||
end
|
||||
LCG = LibStub("LibCustomGlow-1.0")
|
||||
---@table <string, boolean>
|
||||
aura_env.BusyFrames = {}
|
||||
|
||||
aura_env.Units = {
|
||||
"player",
|
||||
"party1",
|
||||
"party2",
|
||||
"party3",
|
||||
"party4",
|
||||
"party5",
|
||||
}
|
||||
|
||||
---@param unit string
|
||||
---@return boolean, nil|string
|
||||
aura_env.HasDispellableDebuff = function(unit)
|
||||
for i = 1, 20 do
|
||||
local debuff, _, _, _, type = UnitDebuff(unit, i)
|
||||
if debuff == nil then
|
||||
return false, nil
|
||||
end
|
||||
if type == "Magic" then
|
||||
return true, nil
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
---@param frame Frame
|
||||
---@return string, nil|string
|
||||
aura_env.GetNameFromFrame = function(frame)
|
||||
if frame == nil then
|
||||
return "", "Frame is nil"
|
||||
end
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
if frame.Name == nil then
|
||||
return "", "Frame.Name is nil"
|
||||
end
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
local name = frame.Name:GetText()
|
||||
if name == nil then
|
||||
return "", "Frame.Name.GetText is nil"
|
||||
end
|
||||
return name, nil
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return Frame|nil
|
||||
aura_env.GetFrameFromName = function(name)
|
||||
return aura_env.PartyCache[name]
|
||||
end
|
||||
|
||||
---@param name string
|
||||
---@return string
|
||||
---@return number
|
||||
aura_env.RemoveNamePadding = function(name)
|
||||
return string.gsub(name, "|c%w%w%w%w%w%w%w%w", "", 1)
|
||||
end
|
||||
|
||||
aura_env.MakeFrameGlow = function(frame)
|
||||
local frameName = frame:GetName()
|
||||
if frame ~= nil and aura_env.BusyFrames[frameName] == nil then
|
||||
---@type Frame
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
local healthBar = frame.Health
|
||||
if healthBar ~= nil then
|
||||
LCG.PixelGlow_Start(healthBar, { 1, 1, 0, 1 }, 16, 0.35, 8, 4)
|
||||
aura_env.BusyFrames[frameName] = true
|
||||
local aura_env = aura_env
|
||||
C_Timer.After(1, function()
|
||||
LCG.PixelGlow_Stop(healthBar)
|
||||
aura_env.BusyFrames[frameName] = nil
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
aura_env.PartyCache = {}
|
||||
aura_env.SetupPartyCache = function()
|
||||
for i = 1, 5 do
|
||||
-- /dump _G[string.format("ElvUF_RaidGroup1UnitButton%d", 1)].Name:GetText()
|
||||
local raidFrame = _G[string.format("ElvUF_RaidGroup1UnitButton%d", i + 1)]
|
||||
local name, err = aura_env.GetNameFromFrame(raidFrame)
|
||||
if err == nil then
|
||||
name = aura_env.RemoveNamePadding(name)
|
||||
aura_env.PartyCache[name] = raidFrame
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- PLAY_SOUND
|
||||
function(e, file)
|
||||
local success = PlaySoundFile(file, "Master")
|
||||
if not success then
|
||||
if aura_env.debug then print(string.format("Failed to play sound file at %s", file)) end
|
||||
end
|
||||
-- PLAY_SOUND
|
||||
function(e, file)
|
||||
local success = PlaySoundFile(file, "Master")
|
||||
if not success then
|
||||
if aura_env.debug then print(string.format("Failed to play sound file at %s", file)) end
|
||||
end
|
||||
end
|
||||
@@ -1,14 +1,14 @@
|
||||
-- PLAYER_ENTERING_WORLD GUILD_ROSTER_UPDATE
|
||||
function(e)
|
||||
for interval, ticker in pairs(aura_env.Tickers) do
|
||||
if ticker == 0 then
|
||||
if aura_env.Debug then print("Creating ticker") end
|
||||
local tickerEvent = string.format("TICKER_%d", interval)
|
||||
aura_env.Tickers[interval] = C_Timer.NewTicker(interval / 1000, function()
|
||||
WeakAuras.ScanEvents(tickerEvent)
|
||||
end)
|
||||
else
|
||||
if aura_env.Debug then print("Ticker already exists") end
|
||||
end
|
||||
end
|
||||
-- PLAYER_ENTERING_WORLD GUILD_ROSTER_UPDATE
|
||||
function(e)
|
||||
for interval, ticker in pairs(aura_env.Tickers) do
|
||||
if ticker == 0 then
|
||||
if aura_env.Debug then print("Creating ticker") end
|
||||
local tickerEvent = string.format("TICKER_%d", interval)
|
||||
aura_env.Tickers[interval] = C_Timer.NewTicker(interval / 1000, function()
|
||||
WeakAuras.ScanEvents(tickerEvent)
|
||||
end)
|
||||
else
|
||||
if aura_env.Debug then print("Ticker already exists") end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,8 +1,8 @@
|
||||
aura_env.Debug = false
|
||||
aura_env.Tickers = {
|
||||
[100] = 0,
|
||||
[200] = 0,
|
||||
[500] = 0,
|
||||
[1000] = 0,
|
||||
[10000] = 0,
|
||||
}
|
||||
aura_env.Debug = false
|
||||
aura_env.Tickers = {
|
||||
[100] = 0,
|
||||
[200] = 0,
|
||||
[500] = 0,
|
||||
[1000] = 0,
|
||||
[10000] = 0,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,66 +1,66 @@
|
||||
---@alias Color {r: number, g: number, b: number}
|
||||
---@class Colorer
|
||||
---@field colors table<number, Color>
|
||||
---@field breakpoints table<number>
|
||||
Colorer = {
|
||||
--- Make sure colors and breakpoints always have the same number of entries! VERY IMPORTANT!
|
||||
---@type table<number, Color>
|
||||
colors = {
|
||||
{ r = 0.62, g = 0.62, b = 0.62 }, -- Grey
|
||||
{ r = 1, g = 1, b = 1 }, -- White
|
||||
{ r = 0.12, g = 1, b = 0 }, -- Green
|
||||
{ r = 0, g = 0.44, b = 0.87 }, -- Blue
|
||||
{ r = 0.64, g = 0.21, b = 0.93 }, -- Purple
|
||||
{ r = 1, g = 0.5, b = 0 }, -- Orange
|
||||
{ r = 0.9, g = 0.8, b = 0.5 }, -- Light Gold
|
||||
{ r = 0, g = 0.8, b = 1.0 }, -- Blizzard Blue
|
||||
},
|
||||
breakpoints = { -999, -10, -5, -2, 2, 5, 10, 999 },
|
||||
|
||||
---@param value number
|
||||
---@return Color, nil|string
|
||||
Interpolate = function(value)
|
||||
local color = { r = 0, g = 0, b = 0 }
|
||||
|
||||
---@type table<number, table<number, number>>
|
||||
local bracket = { { 0, 0 }, { 1, 1 } }
|
||||
for i = 1, #Colorer.breakpoints do
|
||||
if value < Colorer.breakpoints[i] then
|
||||
bracket[2] = { i, Colorer.breakpoints[i] }
|
||||
break
|
||||
end
|
||||
bracket[1] = { i, Colorer.breakpoints[i] }
|
||||
end
|
||||
|
||||
---@type Color
|
||||
local startColor = Colorer.colors[bracket[1][1]]
|
||||
---@type Color
|
||||
local endColor = Colorer.colors[bracket[2][1]]
|
||||
|
||||
local fraction = (value - bracket[1][2]) / (bracket[2][2] - bracket[1][2])
|
||||
|
||||
for k, v in pairs(startColor) do
|
||||
color[k] = Colorer.lerp(v, endColor[k], fraction)
|
||||
end
|
||||
|
||||
return color, nil
|
||||
end,
|
||||
|
||||
---@param color Color
|
||||
---@return string
|
||||
RGBToHex = function(color)
|
||||
local r = math.floor(color.r * 255)
|
||||
local g = math.floor(color.g * 255)
|
||||
local b = math.floor(color.b * 255)
|
||||
return string.format("%02x%02x%02x", r, g, b)
|
||||
end,
|
||||
|
||||
---@param a number
|
||||
---@param b number
|
||||
---@param t number
|
||||
---@return number
|
||||
lerp = function(a, b, t)
|
||||
return a * (1 - t) + b * t
|
||||
end
|
||||
}
|
||||
---@alias Color {r: number, g: number, b: number}
|
||||
---@class Colorer
|
||||
---@field colors table<number, Color>
|
||||
---@field breakpoints table<number>
|
||||
Colorer = {
|
||||
--- Make sure colors and breakpoints always have the same number of entries! VERY IMPORTANT!
|
||||
---@type table<number, Color>
|
||||
colors = {
|
||||
{ r = 0.62, g = 0.62, b = 0.62 }, -- Grey
|
||||
{ r = 1, g = 1, b = 1 }, -- White
|
||||
{ r = 0.12, g = 1, b = 0 }, -- Green
|
||||
{ r = 0, g = 0.44, b = 0.87 }, -- Blue
|
||||
{ r = 0.64, g = 0.21, b = 0.93 }, -- Purple
|
||||
{ r = 1, g = 0.5, b = 0 }, -- Orange
|
||||
{ r = 0.9, g = 0.8, b = 0.5 }, -- Light Gold
|
||||
{ r = 0, g = 0.8, b = 1.0 }, -- Blizzard Blue
|
||||
},
|
||||
breakpoints = { -999, -10, -5, -2, 2, 5, 10, 999 },
|
||||
|
||||
---@param value number
|
||||
---@return Color, nil|string
|
||||
Interpolate = function(value)
|
||||
local color = { r = 0, g = 0, b = 0 }
|
||||
|
||||
---@type table<number, table<number, number>>
|
||||
local bracket = { { 0, 0 }, { 1, 1 } }
|
||||
for i = 1, #Colorer.breakpoints do
|
||||
if value < Colorer.breakpoints[i] then
|
||||
bracket[2] = { i, Colorer.breakpoints[i] }
|
||||
break
|
||||
end
|
||||
bracket[1] = { i, Colorer.breakpoints[i] }
|
||||
end
|
||||
|
||||
---@type Color
|
||||
local startColor = Colorer.colors[bracket[1][1]]
|
||||
---@type Color
|
||||
local endColor = Colorer.colors[bracket[2][1]]
|
||||
|
||||
local fraction = (value - bracket[1][2]) / (bracket[2][2] - bracket[1][2])
|
||||
|
||||
for k, v in pairs(startColor) do
|
||||
color[k] = Colorer.lerp(v, endColor[k], fraction)
|
||||
end
|
||||
|
||||
return color, nil
|
||||
end,
|
||||
|
||||
---@param color Color
|
||||
---@return string
|
||||
RGBToHex = function(color)
|
||||
local r = math.floor(color.r * 255)
|
||||
local g = math.floor(color.g * 255)
|
||||
local b = math.floor(color.b * 255)
|
||||
return string.format("%02x%02x%02x", r, g, b)
|
||||
end,
|
||||
|
||||
---@param a number
|
||||
---@param b number
|
||||
---@param t number
|
||||
---@return number
|
||||
lerp = function(a, b, t)
|
||||
return a * (1 - t) + b * t
|
||||
end
|
||||
}
|
||||
setmetatable(Colorer, { __index = Colorer })
|
||||
Reference in New Issue
Block a user