|
|
|
local MAJOR, MINOR = "DiesalTools-2.0", 1
|
|
|
|
---@alias Diesal.Tools DiesalTools-2.0
|
|
|
|
|
|
|
|
---@class DiesalTools-2.0
|
|
|
|
local DiesalTools = LibStub:NewLibrary(MAJOR, MINOR)
|
|
|
|
|
|
|
|
if not DiesalTools then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local type, select, pairs, tonumber, tostring = type, select, pairs, tonumber, tostring
|
|
|
|
local table_concat = table.concat
|
|
|
|
local setmetatable, getmetatable, next = setmetatable, getmetatable, next
|
|
|
|
local sub, format, lower, upper, gsub = string.sub, string.format, string.lower, string.upper, string.gsub
|
|
|
|
local floor, ceil, abs, modf = math.floor, math.ceil, math.abs, math.modf
|
|
|
|
|
|
|
|
local CreateFrame, UIParent, GetCursorPosition = CreateFrame, UIParent, GetCursorPosition
|
|
|
|
local GetScreenWidth, GetScreenHeight = GetScreenWidth, GetScreenHeight
|
|
|
|
|
|
|
|
local escapeSequences = {
|
|
|
|
["\a"] = "\\a", -- Bell
|
|
|
|
["\b"] = "\\b", -- Backspace
|
|
|
|
["\t"] = "\\t", -- Horizontal tab
|
|
|
|
["\n"] = "\\n", -- Newline
|
|
|
|
["\v"] = "\\v", -- Vertical tab
|
|
|
|
["\f"] = "\\f", -- Form feed
|
|
|
|
["\r"] = "\\r", -- Carriage return
|
|
|
|
["\\"] = "\\\\", -- Backslash
|
|
|
|
['"'] = '\\"', -- Quotation mark
|
|
|
|
["|"] = "||",
|
|
|
|
}
|
|
|
|
|
|
|
|
local lua_keywords = {
|
|
|
|
["and"] = true,
|
|
|
|
["break"] = true,
|
|
|
|
["do"] = true,
|
|
|
|
["else"] = true,
|
|
|
|
["elseif"] = true,
|
|
|
|
["end"] = true,
|
|
|
|
["false"] = true,
|
|
|
|
["for"] = true,
|
|
|
|
["function"] = true,
|
|
|
|
["if"] = true,
|
|
|
|
["in"] = true,
|
|
|
|
["local"] = true,
|
|
|
|
["nil"] = true,
|
|
|
|
["not"] = true,
|
|
|
|
["or"] = true,
|
|
|
|
["repeat"] = true,
|
|
|
|
["return"] = true,
|
|
|
|
["then"] = true,
|
|
|
|
["true"] = true,
|
|
|
|
["until"] = true,
|
|
|
|
["while"] = true,
|
|
|
|
}
|
|
|
|
local sub_table = {}
|
|
|
|
local colors = {
|
|
|
|
blue = "|cff" .. "00aaff",
|
|
|
|
darkblue = "|cff" .. "004466",
|
|
|
|
orange = "|cff" .. "ffaa00",
|
|
|
|
darkorange = "|cff" .. "4c3300",
|
|
|
|
grey = "|cff" .. "7f7f7f",
|
|
|
|
darkgrey = "|cff" .. "414141",
|
|
|
|
white = "|cff" .. "ffffff",
|
|
|
|
red = "|cff" .. "ff0000",
|
|
|
|
green = "|cff" .. "00ff2b",
|
|
|
|
yellow = "|cff" .. "ffff00",
|
|
|
|
lightyellow = "|cff" .. "ffea7f",
|
|
|
|
}
|
|
|
|
|
|
|
|
local formattedArgs = {}
|
|
|
|
|
|
|
|
---@param level number
|
|
|
|
local function GetCaller(level)
|
|
|
|
for trace in debugstack(level, 2, 0):gmatch("(.-)\n") do
|
|
|
|
-- Blizzard Sandbox
|
|
|
|
local match, _, file, line = trace:find("^.*\\(.-):(%d+)")
|
|
|
|
if match then
|
|
|
|
return format("%s[%s%s: %s%s%s]|r", colors.orange, colors.yellow, file, colors.lightyellow, line, colors.orange)
|
|
|
|
end
|
|
|
|
-- PQI DataFile
|
|
|
|
local match, _, file, line = trace:find('^%[string "[%s%-]*(.-%.lua).-"%]:(%d+)')
|
|
|
|
if match then
|
|
|
|
return format("%s[%s%s: %s%s%s]|r", colors.orange, colors.yellow, file, colors.lightyellow, line, colors.orange)
|
|
|
|
end
|
|
|
|
-- PQR Ability code
|
|
|
|
local match, _, file, line = trace:find('^%[string "(.-)"%]:(%d+)')
|
|
|
|
if match then
|
|
|
|
return format("%s[%s%s: %s%s%s]|r", colors.orange, colors.yellow, file, colors.lightyellow, line, colors.orange)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return format("%s[%sUnknown Caller%s]|r", colors.orange, colors.red, colors.orange)
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param number number
|
|
|
|
---@param base? number
|
|
|
|
local function round(number, base)
|
|
|
|
base = base or 1
|
|
|
|
return floor((number + base / 2) / base) * base
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param color string | number | table
|
|
|
|
---@param g? number
|
|
|
|
---@param b? number
|
|
|
|
local function getRGBColorValues(color, g, b)
|
|
|
|
if type(color) == "number" and type(g) == "number" and type(b) == "number" then
|
|
|
|
if color <= 1 and g <= 1 and b <= 1 then
|
|
|
|
return round(color * 255), round(g * 255), round(b * 255)
|
|
|
|
end
|
|
|
|
return color[1], color[2], color[3]
|
|
|
|
elseif type(color) == "table" and type(color[1]) == "number" and type(color[2]) == "number" and type(color[3]) == "number" then
|
|
|
|
if color[1] <= 1 and color[2] <= 1 and color[3] <= 1 then
|
|
|
|
return round(color[1] * 255), round(color[2] * 255), round(color[3] * 255)
|
|
|
|
end
|
|
|
|
return color[1], color[2], color[3]
|
|
|
|
elseif type(color) == "string" then
|
|
|
|
return tonumber(sub(color, 1, 2), 16), tonumber(sub(color, 3, 4), 16), tonumber(sub(color, 5, 6), 16)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param value string | number[]
|
|
|
|
function DiesalTools.GetColor(value)
|
|
|
|
if not value then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
if type(value) == "table" and #value >= 3 then
|
|
|
|
return value[1] / 255, value[2] / 255, value[3] / 255
|
|
|
|
elseif type(value) == "string" then
|
|
|
|
return tonumber(sub(value, 1, 2), 16) / 255, tonumber(sub(value, 3, 4), 16) / 255, tonumber(sub(value, 5, 6), 16) / 255
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- ~~| API |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
function DiesalTools.Stack()
|
|
|
|
local stack = debugstack(1, 12, 0)
|
|
|
|
if not stack then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
print("|r--------------------------------| Stack Trace |---------------------------------")
|
|
|
|
for trace in stack:gmatch("(.-)\n") do
|
|
|
|
local match, _, file, line, func = trace:find("^.*\\(.-):(%d+).-`(.*)'$")
|
|
|
|
if match then
|
|
|
|
print(format("%s[%s%s: %s%s%s] %sfunction|r %s|r", colors.orange, colors.yellow, file, colors.lightyellow, line, colors.orange, colors.blue, func))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
print("|r--------------------------------------------------------------------------------")
|
|
|
|
end
|
|
|
|
|
|
|
|
---@generic S
|
|
|
|
---@param src S
|
|
|
|
---@param dest? table
|
|
|
|
---@param metatable? boolean
|
|
|
|
---@return S
|
|
|
|
function DiesalTools.TableCopy(src, dest, metatable)
|
|
|
|
if type(src) == "table" then
|
|
|
|
if not dest then
|
|
|
|
dest = {}
|
|
|
|
end
|
|
|
|
for sk, sv in next, src, nil do
|
|
|
|
dest[DiesalTools.TableCopy(sk)] = DiesalTools.TableCopy(sv)
|
|
|
|
end
|
|
|
|
if metatable then
|
|
|
|
setmetatable(dest, DiesalTools.TableCopy(getmetatable(src)))
|
|
|
|
end
|
|
|
|
else -- number, string, boolean, etc
|
|
|
|
dest = src
|
|
|
|
end
|
|
|
|
return dest
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ red, blue, green = GetColor(value)
|
|
|
|
@Arguments:
|
|
|
|
value Hex color code or a table contating R,G,B color values
|
|
|
|
@Returns:
|
|
|
|
red Red component of color (0-1) (number)
|
|
|
|
green Green component of color(0-1) (number)
|
|
|
|
blue Blue component of color (0-1) (number)
|
|
|
|
-- ]]
|
|
|
|
|
|
|
|
-- | Color Tools |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
---@alias ColorType "rgb255" | "rgb1" | "hex" | "hsl"
|
|
|
|
-- converts a color from RGB[0-1], RGB[0-255], HEX or HSL to RGB[0-1], RGB[0-255] or HEX
|
|
|
|
---@param from ColorType
|
|
|
|
---@param to ColorType
|
|
|
|
---@param v1 string | number | table
|
|
|
|
---@param v2? string | number
|
|
|
|
---@param v3? string | number
|
|
|
|
---@overload fun(from: "rgb1" | "hsl" | "rbg255", to: "rgb1" | "hsl" | "rbg255", v1: number, v2: number, v3: number): number, number, number
|
|
|
|
---@overload fun(from: "hex", to: "rgb255" | "rgb1" | "hsl", v1: string): number, number, number
|
|
|
|
---@overload fun(from: "rgb255" | "rgb1" | "hsl", to: "hex", v1: number, v2: number, v3: number): string, nil, nil
|
|
|
|
function DiesalTools.ConvertColor(from, to, v1, v2, v3)
|
|
|
|
if not from or not to or not v1 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if type(v1) == "table" then
|
|
|
|
v1, v2, v3 = v1[1], v1[2], v1[3]
|
|
|
|
end
|
|
|
|
local r, g, b
|
|
|
|
---@type ColorType, ColorType
|
|
|
|
from, to = from:lower(), to:lower()
|
|
|
|
|
|
|
|
if from == "rgb255" and type(v1) == "number" and type(v2) == "number" and type(v3) == "number" then
|
|
|
|
r, g, b = v1, v2, v3
|
|
|
|
elseif from == "rgb1" and type(v1) == "number" and type(v2) == "number" and type(v3) == "number" then
|
|
|
|
r, g, b = round(v1 * 255), round(v2 * 255), round(v3 * 255)
|
|
|
|
elseif from == "hex" and type(v1) == "string" and #v1 > 5 then
|
|
|
|
r, g, b = tonumber(sub(v1, 1, 2), 16), tonumber(sub(v1, 3, 4), 16), tonumber(sub(v1, 5, 6), 16)
|
|
|
|
elseif from == "hsl" and type(v1) == "number" and type(v2) == "number" and type(v3) == "number" then
|
|
|
|
if v2 == 0 then
|
|
|
|
v3 = round(v3 * 255)
|
|
|
|
r, g, b = v3, v3, v3
|
|
|
|
else
|
|
|
|
v1, v2, v3 = v1 / 360 * 6, min(max(0, v2), 1), min(max(0, v3), 1)
|
|
|
|
local c = (1 - abs(2 * v3 - 1)) * v2
|
|
|
|
local x = (1 - abs(v1 % 2 - 1)) * c
|
|
|
|
local m = (v3 - 0.5 * c)
|
|
|
|
r, g, b = 0, 0, 0
|
|
|
|
if v1 < 1 then
|
|
|
|
r, g, b = c, x, 0
|
|
|
|
elseif v1 < 2 then
|
|
|
|
r, g, b = x, c, 0
|
|
|
|
elseif v1 < 3 then
|
|
|
|
r, g, b = 0, c, x
|
|
|
|
elseif v1 < 4 then
|
|
|
|
r, g, b = 0, x, c
|
|
|
|
elseif v1 < 5 then
|
|
|
|
r, g, b = x, 0, c
|
|
|
|
else
|
|
|
|
r, g, b = c, 0, x
|
|
|
|
end
|
|
|
|
|
|
|
|
r, g, b = round((r + m) * 255), round((g + m) * 255), round((b + m) * 255)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
return
|
|
|
|
end
|
|
|
|
r, g, b = min(255, max(0, r)), min(255, max(0, g)), min(255, max(0, b))
|
|
|
|
|
|
|
|
if to == "rgb255" then
|
|
|
|
return r, g, b
|
|
|
|
elseif to == "rgb1" then
|
|
|
|
return r / 255, g / 255, b / 255
|
|
|
|
elseif to == "hex" then
|
|
|
|
return format("%02x%02x%02x", r, g, b)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Mixes a color with pure white to produce a lighter color
|
|
|
|
---@param color string | table
|
|
|
|
---@param percent number
|
|
|
|
---@param to? ColorType
|
|
|
|
---@param from? ColorType
|
|
|
|
function DiesalTools.TintColor(color, percent, to, from)
|
|
|
|
percent = min(1, max(0, percent))
|
|
|
|
from, to = from or "hex", to and to:lower() or "hex"
|
|
|
|
local r, g, b = DiesalTools.ConvertColor(from, "rgb255", color)
|
|
|
|
|
|
|
|
if to == "rgb255" then
|
|
|
|
return round((255 - r) * percent + r), round((255 - g) * percent + g), round((255 - b) * percent + b)
|
|
|
|
elseif to == "rgb1" then
|
|
|
|
return round(((255 - r) * percent + r) / 255), round(((255 - g) * percent + g) / 255), round(((255 - b) * percent + b) / 255)
|
|
|
|
elseif to == "hex" then
|
|
|
|
return format("%02x%02x%02x", round((255 - r) * percent + r), round((255 - g) * percent + g), round((255 - b) * percent + b))
|
|
|
|
end
|
|
|
|
-- return format("%02x%02x%02x", round((255-r)*percent+r), round((255-g)*percent+g), round((255-b)*percent+b) )
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Mixes a color with pure black to produce a darker color
|
|
|
|
---@overload fun(color: string, percent: number, to: "hex", from: "hex"): string, nil, nil
|
|
|
|
---@overload fun(color: string, percent: number, to: "rgb255" | "rgb1", from: ColorType): number, number, number
|
|
|
|
---@overload fun(color: table, percent: number, to: "hex", from: ColorType): string
|
|
|
|
function DiesalTools.ShadeColor(color, percent, to, from)
|
|
|
|
percent = min(1, max(0, percent))
|
|
|
|
from, to = from or "hex", to and to:lower() or "hex"
|
|
|
|
local r, g, b = DiesalTools.ConvertColor(from, "rgb255", color)
|
|
|
|
|
|
|
|
if to == "rgb255" then
|
|
|
|
return round(-r * percent + r), round(-g * percent + g), round(-b * percent + b)
|
|
|
|
elseif to == "rgb1" then
|
|
|
|
return round((-r * percent + r) / 255), round((-g * percent + g) / 255), round((-b * percent + b) / 255)
|
|
|
|
elseif to == "hex" then
|
|
|
|
return format("%02x%02x%02x", round(-r * percent + r), round(-g * percent + g), round(-b * percent + b))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Mixes a color with the another color to produce an intermediate color.
|
|
|
|
function DiesalTools.MixColors(color1, color2, percent, to, from)
|
|
|
|
percent = min(1, max(0, percent))
|
|
|
|
from, to = from or "hex", to and to:lower() or "hex"
|
|
|
|
-- to = to and to:lower() or 'hex'
|
|
|
|
|
|
|
|
local r1, g1, b1 = DiesalTools.ConvertColor(from, "rgb255", color1)
|
|
|
|
local r2, g2, b2 = DiesalTools.ConvertColor(from, "rgb255", color2)
|
|
|
|
|
|
|
|
if to == "rgb255" then
|
|
|
|
return round((r2 - r1) * percent) + r1, round((g2 - g1) * percent) + g1, round((b2 - b1) * percent) + b1
|
|
|
|
elseif to == "rgb1" then
|
|
|
|
return round(((r2 - r1) * percent + r1) / 255), round(((g2 - g1) * percent + g1) / 255), round(((b2 - b1) * percent + b1) / 255)
|
|
|
|
elseif to == "hex" then
|
|
|
|
return format("%02x%02x%02x", round((r2 - r1) * percent) + r1, round((g2 - g1) * percent) + g1, round((b2 - b1) * percent) + b1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- converts color HSL to HEX
|
|
|
|
function DiesalTools.HSL(v1, v2, v3)
|
|
|
|
if not v1 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if type(v1) == "table" then
|
|
|
|
v1, v2, v3 = v1[1], v1[2], v1[3]
|
|
|
|
end
|
|
|
|
local r, g, b
|
|
|
|
|
|
|
|
if v2 == 0 then
|
|
|
|
v3 = round(v3 * 255)
|
|
|
|
r, g, b = v3, v3, v3
|
|
|
|
else
|
|
|
|
v1, v2, v3 = v1 / 360 * 6, min(max(0, v2), 1), min(max(0, v3), 1)
|
|
|
|
local c = (1 - abs(2 * v3 - 1)) * v2
|
|
|
|
local x = (1 - abs(v1 % 2 - 1)) * c
|
|
|
|
local m = (v3 - 0.5 * c)
|
|
|
|
r, g, b = 0, 0, 0
|
|
|
|
if v1 < 1 then
|
|
|
|
r, g, b = c, x, 0
|
|
|
|
elseif v1 < 2 then
|
|
|
|
r, g, b = x, c, 0
|
|
|
|
elseif v1 < 3 then
|
|
|
|
r, g, b = 0, c, x
|
|
|
|
elseif v1 < 4 then
|
|
|
|
r, g, b = 0, x, c
|
|
|
|
elseif v1 < 5 then
|
|
|
|
r, g, b = x, 0, c
|
|
|
|
else
|
|
|
|
r, g, b = c, 0, x
|
|
|
|
end
|
|
|
|
|
|
|
|
r, g, b = round((r + m) * 255), round((g + m) * 255), round((b + m) * 255)
|
|
|
|
end
|
|
|
|
|
|
|
|
r, g, b = min(255, max(0, r)), min(255, max(0, g)), min(255, max(0, b))
|
|
|
|
|
|
|
|
return format("%02x%02x%02x", r, g, b)
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ GetTxtColor(value)
|
|
|
|
@Arguments:
|
|
|
|
value Hex color code or a table contating R,G,B color values
|
|
|
|
@Returns:
|
|
|
|
text coloring escape sequence (|cFFFFFFFF)
|
|
|
|
-- ]]
|
|
|
|
function DiesalTools.GetTxtColor(value)
|
|
|
|
if not value then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if type(value) == "table" then
|
|
|
|
value = string.format("%02x%02x%02x", value[1], value[2], value[3])
|
|
|
|
end
|
|
|
|
return format("|cff%s", value)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- | Texture Tools |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
--[[ Get a piece of a texture as a cooridnate refernce
|
|
|
|
-- @Param column the column in the texture
|
|
|
|
-- @Param row the row in the texture
|
|
|
|
-- @Param size the size in the texture
|
|
|
|
-- @Param textureWidth total texture width
|
|
|
|
-- @Param textureHeight total texture height
|
|
|
|
-- @Return edge coordinates for image cropping, plugs directly into Texture:SetTexCoord(left, right, top, bottom) ]]
|
|
|
|
function DiesalTools.GetIconCoords(column, row, size, textureWidth, textureHeight)
|
|
|
|
size = size or 16
|
|
|
|
textureWidth = textureWidth or 128
|
|
|
|
textureHeight = textureHeight or 16
|
|
|
|
return (column * size - size) / textureWidth, (column * size) / textureWidth, (row * size - size) / textureHeight, (row * size) / textureHeight
|
|
|
|
end
|
|
|
|
|
|
|
|
-- | String Tools |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
--[[ Capitalize a string
|
|
|
|
-- @Param str the string to capatilize
|
|
|
|
-- @Return the capitilized string ]]
|
|
|
|
function DiesalTools.Capitalize(str)
|
|
|
|
return (str:gsub("^%l", upper))
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ Escape all formatting in a WoW-lua string
|
|
|
|
-- @Param str the string to escape
|
|
|
|
-- @Return the escaped string ]]
|
|
|
|
function DiesalTools.EscapeString(string)
|
|
|
|
return string:gsub('[%z\1-\31"\\|\127-\255]', escapeSequences)
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ ID = CreateID(s)
|
|
|
|
-- @Param s string to parse
|
|
|
|
-- @Return ID string stripped of all non letter characters and color codes.]]
|
|
|
|
function DiesalTools.CreateID(s)
|
|
|
|
return gsub(s:gsub("c%x%x%x%x%x%x%x%x", ""), "[^%a%d]", "")
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ str = TrimString(s)
|
|
|
|
-- @Param s string to parse
|
|
|
|
-- @Return str string stripped of leading and trailing spaces.]]
|
|
|
|
function DiesalTools.TrimString(s)
|
|
|
|
return s:gsub("^%s*(.-)%s*$", "%1")
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ string = SpliceString(string, start, End, txt)
|
|
|
|
@Arguments:
|
|
|
|
string string to splice
|
|
|
|
start starting index of splice
|
|
|
|
End ending index of splice
|
|
|
|
txt new text to splice in (string)
|
|
|
|
@Returns:
|
|
|
|
string resulting string of the splice
|
|
|
|
|
|
|
|
@example:
|
|
|
|
DiesalTools.SpliceString("123456789",2,4,'NEW') -- returns: "1NEW56789"
|
|
|
|
--]]
|
|
|
|
function DiesalTools.SpliceString(string, start, End, txt)
|
|
|
|
return string:sub(1, start - 1) .. txt .. string:sub(End + 1, -1)
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ string = Serialize(table)
|
|
|
|
@Param table - table to to serialize
|
|
|
|
@Return string - serialized table (string)
|
|
|
|
|
|
|
|
@deserialization:
|
|
|
|
local func,err = loadstring('serialized table')
|
|
|
|
if err then error(err) end
|
|
|
|
return func()
|
|
|
|
]]
|
|
|
|
local function serialize_number(number)
|
|
|
|
-- no argument checking - called very often
|
|
|
|
local text = ("%.17g"):format(number)
|
|
|
|
-- on the same platform tostring() and string.format()
|
|
|
|
-- return the same results for 1/0, -1/0, 0/0
|
|
|
|
-- so we don't need separate substitution table
|
|
|
|
return sub_table[text] or text
|
|
|
|
end
|
|
|
|
local function impl(t, cat, visited)
|
|
|
|
local t_type = type(t)
|
|
|
|
if t_type == "table" then
|
|
|
|
if not visited[t] then
|
|
|
|
visited[t] = true
|
|
|
|
|
|
|
|
cat("{")
|
|
|
|
-- Serialize numeric indices
|
|
|
|
local next_i = 0
|
|
|
|
for i, v in ipairs(t) do
|
|
|
|
if i > 1 then -- TODO: Move condition out of the loop
|
|
|
|
cat(",")
|
|
|
|
end
|
|
|
|
impl(v, cat, visited)
|
|
|
|
next_i = i
|
|
|
|
end
|
|
|
|
next_i = next_i + 1
|
|
|
|
-- Serialize hash part
|
|
|
|
-- Skipping comma only at first element iff there is no numeric part.
|
|
|
|
local need_comma = (next_i > 1)
|
|
|
|
for k, v in pairs(t) do
|
|
|
|
local k_type = type(k)
|
|
|
|
if k_type == "string" then
|
|
|
|
if need_comma then
|
|
|
|
cat(",")
|
|
|
|
end
|
|
|
|
need_comma = true
|
|
|
|
-- TODO: Need "%q" analogue, which would put quotes
|
|
|
|
-- only if string does not match regexp below
|
|
|
|
if not lua_keywords[k] and ("^[%a_][%a%d_]*$"):match(k) then
|
|
|
|
cat(k)
|
|
|
|
cat("=")
|
|
|
|
else
|
|
|
|
cat(format("[%q]=", k))
|
|
|
|
end
|
|
|
|
impl(v, cat, visited)
|
|
|
|
else
|
|
|
|
if
|
|
|
|
k_type ~= "number" -- non-string non-number
|
|
|
|
or k >= next_i
|
|
|
|
or k < 1 -- integer key in hash part of the table
|
|
|
|
or k % 1 ~= 0 -- non-integer key
|
|
|
|
then
|
|
|
|
if need_comma then
|
|
|
|
cat(",")
|
|
|
|
end
|
|
|
|
need_comma = true
|
|
|
|
|
|
|
|
cat("[")
|
|
|
|
impl(k, cat, visited)
|
|
|
|
cat("]=")
|
|
|
|
impl(v, cat, visited)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
cat("}")
|
|
|
|
visited[t] = nil
|
|
|
|
else
|
|
|
|
-- this loses information on recursive tables
|
|
|
|
cat('"table (recursive)"')
|
|
|
|
end
|
|
|
|
elseif t_type == "number" then
|
|
|
|
cat(serialize_number(t))
|
|
|
|
elseif t_type == "boolean" then
|
|
|
|
cat(tostring(t))
|
|
|
|
elseif t == nil then
|
|
|
|
cat("nil")
|
|
|
|
else
|
|
|
|
-- this converts non-serializable (functions) types to strings
|
|
|
|
cat(format("%q", tostring(t)))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local function tstr_cat(cat, t)
|
|
|
|
impl(t, cat, {})
|
|
|
|
end
|
|
|
|
function DiesalTools.Serialize(t)
|
|
|
|
local buf = {}
|
|
|
|
local cat = function(v)
|
|
|
|
buf[#buf + 1] = v
|
|
|
|
end
|
|
|
|
impl(t, cat, {})
|
|
|
|
return format("return %s", table_concat(buf))
|
|
|
|
end
|
|
|
|
|
|
|
|
-- | Math Tools |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
--[[ Round a number
|
|
|
|
-- @Param number the number to round
|
|
|
|
-- @Param base the number to round to (can be a decimal) [Default:1]
|
|
|
|
-- @Return the rounded number to base ]]
|
|
|
|
---@param number number
|
|
|
|
---@param base? number
|
|
|
|
function DiesalTools.Round(number, base)
|
|
|
|
base = base or 1
|
|
|
|
return floor((number + base / 2) / base) * base
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ Round a number for printing
|
|
|
|
-- @Param number the number to round
|
|
|
|
-- @Param idp number of decimal points to round to [Default:1]
|
|
|
|
-- @Return the rounded number ]]
|
|
|
|
function DiesalTools.RoundPrint(num, idp)
|
|
|
|
return string.format("%." .. (idp or 0) .. "f", num)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- | Frame Tools |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
--[[ GetFrameQuadrant(frame)
|
|
|
|
@Arguments:
|
|
|
|
frame frame?!!
|
|
|
|
@Returns:
|
|
|
|
quadrant quadrant of the screen frames center is in
|
|
|
|
-- ]]
|
|
|
|
function DiesalTools.GetFrameQuadrant(frame)
|
|
|
|
if not frame:GetCenter() then
|
|
|
|
return "UNKNOWN", frame:GetName()
|
|
|
|
end
|
|
|
|
|
|
|
|
local x, y = frame:GetCenter()
|
|
|
|
local screenWidth = GetScreenWidth()
|
|
|
|
local screenHeight = GetScreenHeight()
|
|
|
|
local quadrant
|
|
|
|
|
|
|
|
if (x > (screenWidth / 4) and x < (screenWidth / 4) * 3) and y > (screenHeight / 4) * 3 then
|
|
|
|
quadrant = "TOP"
|
|
|
|
elseif x < (screenWidth / 4) and y > (screenHeight / 4) * 3 then
|
|
|
|
quadrant = "TOPLEFT"
|
|
|
|
elseif x > (screenWidth / 4) * 3 and y > (screenHeight / 4) * 3 then
|
|
|
|
quadrant = "TOPRIGHT"
|
|
|
|
elseif (x > (screenWidth / 4) and x < (screenWidth / 4) * 3) and y < (screenHeight / 4) then
|
|
|
|
quadrant = "BOTTOM"
|
|
|
|
elseif x < (screenWidth / 4) and y < (screenHeight / 4) then
|
|
|
|
quadrant = "BOTTOMLEFT"
|
|
|
|
elseif x > (screenWidth / 4) * 3 and y < (screenHeight / 4) then
|
|
|
|
quadrant = "BOTTOMRIGHT"
|
|
|
|
elseif x < (screenWidth / 4) and (y > (screenHeight / 4) and y < (screenHeight / 4) * 3) then
|
|
|
|
quadrant = "LEFT"
|
|
|
|
elseif x > (screenWidth / 4) * 3 and y < (screenHeight / 4) * 3 and y > (screenHeight / 4) then
|
|
|
|
quadrant = "RIGHT"
|
|
|
|
else
|
|
|
|
quadrant = "CENTER"
|
|
|
|
end
|
|
|
|
|
|
|
|
return quadrant
|
|
|
|
end
|
|
|
|
|
|
|
|
-- | Misc Tools |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
--[[ Pack(...) blizzard dosnt use 5.2 yet so will have to create this one
|
|
|
|
@Arguments:
|
|
|
|
... arguments to pack into a table
|
|
|
|
@Returns:
|
|
|
|
a new table with all parameters stored into keys 1, 2, etc. and with a field "n" with the total number of parameters
|
|
|
|
]]
|
|
|
|
function DiesalTools.Pack(...)
|
|
|
|
return { n = select("#", ...), ... }
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[ Unpack(...)
|
|
|
|
@Arguments:
|
|
|
|
t table to unpack
|
|
|
|
@Returns:
|
|
|
|
... list of arguments
|
|
|
|
]]
|
|
|
|
function DiesalTools.Unpack(t)
|
|
|
|
if t.n then
|
|
|
|
return unpack(t, 1, t.n)
|
|
|
|
end
|
|
|
|
return unpack(table)
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param zoomPercent number
|
|
|
|
---@param width number
|
|
|
|
---@param height number
|
|
|
|
---@param xOffset? number
|
|
|
|
---@param yOffset? number
|
|
|
|
---@return number, number, number, number, number, number, number, number
|
|
|
|
function DiesalTools.GetTexCoords(zoomPercent, width, height, xOffset, yOffset)
|
|
|
|
xOffset = xOffset or 0
|
|
|
|
yOffset = yOffset or 0
|
|
|
|
local textureWidth = 1 - 0.5 * zoomPercent
|
|
|
|
local aspectRatio = (width == 0 or height == 0) and 1 or width / height
|
|
|
|
---@type table<number, number>
|
|
|
|
local currentCoord = {}
|
|
|
|
currentCoord[1], currentCoord[2], currentCoord[3], currentCoord[4], currentCoord[5], currentCoord[6], currentCoord[7], currentCoord[8] = 0, 0, 0, 1, 1, 0, 1, 1
|
|
|
|
|
|
|
|
local xRatio = aspectRatio < 1 and aspectRatio or 1
|
|
|
|
local yRatio = aspectRatio > 1 and 1 / aspectRatio or 1
|
|
|
|
for i, coord in ipairs(currentCoord) do
|
|
|
|
if i % 2 == 1 then
|
|
|
|
currentCoord[i] = (coord - 0.5) * textureWidth * xRatio + 0.5 - xOffset
|
|
|
|
else
|
|
|
|
currentCoord[i] = (coord - 0.5) * textureWidth * yRatio + 0.5 - yOffset
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return unpack(currentCoord)
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param texture string|integer
|
|
|
|
function DiesalTools.CreateTextureString(texture)
|
|
|
|
if type(texture) ~= "string" then
|
|
|
|
texture = tostring(texture)
|
|
|
|
end
|
|
|
|
|
|
|
|
return string.format("|T%s:0::0:0:64:64:4:60:4:60|t", texture)
|
|
|
|
end
|
|
|
|
|
|
|
|
---@param spell number | Bastion.Spell
|
|
|
|
---@param text? string
|
|
|
|
---@param position? "start" | "end"
|
|
|
|
function DiesalTools.AttachSpellIcon(spell, text, position)
|
|
|
|
position = position or "start"
|
|
|
|
local icon
|
|
|
|
if type(spell) == "table" then
|
|
|
|
text = text or spell:GetName()
|
|
|
|
icon = spell:GetIcon()
|
|
|
|
elseif type(spell) == "number" then
|
|
|
|
local spellInfo = C_Spell.GetSpellInfo(spell)
|
|
|
|
if spellInfo then
|
|
|
|
text = text or spellInfo.name
|
|
|
|
icon = spellInfo.iconID
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return string.format("%s %s", position == "start" and DiesalTools.CreateTextureString(icon) or text, position == "end" and DiesalTools.CreateTextureString(icon) or text)
|
|
|
|
end
|