otclient-redemption/modules/corelib/util.lua
LIBERGOD 8311a011a0
improve: UI, Styles, MiniWindow General, see Roadmap
- Introduced a new miniwindow header image for UI.
- Created a new style file for the miniwindow battle interface, defining layout, buttons, and scrollable content.
- Configured button states for close, minimize, and toggle filter functionalities.
- Established visual elements such as borders, backgrounds, and dimensions for a cohesive design.
- Multi-Instance battle list feature.
- Filters and Orderings for each instance.
- Lock buttons specific to each instance.
- Keep open/re-launch when relog/exit game.
- On close, it destroys the instance, cleaning up its settings.
- A bunch of safety checks and locks to ensure they are working properly, and update all windows accordingly.
2025-08-27 18:42:18 -03:00

468 lines
No EOL
11 KiB
Lua

-- @docfuncs @{
function print(...)
local msg = ''
local args = { ... }
local appendSpace = #args > 1
for i, v in ipairs(args) do
msg = msg .. tostring(v)
if appendSpace and i < #args then
msg = msg .. ' '
end
end
g_logger.log(LogInfo, msg)
end
function pinfo(msg)
g_logger.log(LogInfo, msg)
end
function perror(msg)
g_logger.log(LogError, msg)
end
function pwarning(msg)
g_logger.log(LogWarning, msg)
end
function pdebug(msg)
g_logger.log(LogDebug, msg)
end
function fatal(msg)
g_logger.log(LogFatal, msg)
end
function exit()
g_app.exit()
end
function quit()
g_app.quit()
end
function connect(object, arg1, arg2, arg3)
if not object then
return
end
local signalsAndSlots
local pushFront
if type(arg1) == 'string' then
signalsAndSlots = {
[arg1] = arg2
}
pushFront = arg3
else
signalsAndSlots = arg1
pushFront = arg2
end
for signal, slot in pairs(signalsAndSlots) do
if not object[signal] then
local mt = getmetatable(object)
if mt and type(object) == 'userdata' then
object[signal] = function(...)
return signalcall(mt[signal], ...)
end
end
end
if not object[signal] then
object[signal] = slot
elseif type(object[signal]) == 'function' then
object[signal] = { object[signal] }
end
if type(slot) ~= 'function' then
perror(debug.traceback('unable to connect a non function value'))
end
if type(object[signal]) == 'table' then
if pushFront then
table.insert(object[signal], 1, slot)
else
table.insert(object[signal], #object[signal] + 1, slot)
end
end
end
end
function disconnect(object, arg1, arg2)
local signalsAndSlots
if type(arg1) == 'string' then
if arg2 == nil then
object[arg1] = nil
return
end
signalsAndSlots = {
[arg1] = arg2
}
elseif type(arg1) == 'table' then
signalsAndSlots = arg1
else
perror(debug.traceback('unable to disconnect'))
end
for signal, slot in pairs(signalsAndSlots) do
if not object[signal] then
elseif type(object[signal]) == 'function' then
if object[signal] == slot then
object[signal] = nil
end
elseif type(object[signal]) == 'table' then
for k, func in pairs(object[signal]) do
if func == slot then
table.remove(object[signal], k)
if #object[signal] == 1 then
object[signal] = object[signal][1]
end
break
end
end
end
end
end
function newclass(name)
if not name then
perror(debug.traceback('new class has no name.'))
end
local class = {}
function class.internalCreate()
local instance = {}
for k, v in pairs(class) do
instance[k] = v
end
return instance
end
class.create = class.internalCreate
class.__class = name
class.getClassName = function()
return name
end
return class
end
function extends(base, name)
if not name then
perror(debug.traceback('extended class has no name.'))
end
local derived = {}
function derived.internalCreate()
local instance = base.create()
for k, v in pairs(derived) do
instance[k] = v
end
return instance
end
derived.create = derived.internalCreate
derived.__class = name
derived.getClassName = function()
return name
end
return derived
end
function runinsandbox(func, ...)
if type(func) == 'string' then
func, err = loadfile(resolvepath(func, 2))
if not func then
error(err)
end
end
local env = {}
local oldenv = getfenv(0)
setmetatable(env, {
__index = oldenv
})
setfenv(0, env)
func(...)
setfenv(0, oldenv)
return env
end
function loadasmodule(name, file)
file = file or resolvepath(name, 2)
if package.loaded[name] then
return package.loaded[name]
end
local env = runinsandbox(file)
package.loaded[name] = env
return env
end
local function module_loader(modname)
local module = g_modules.getModule(modname)
if not module then
return '\n\tno module \'' .. modname .. '\''
end
return function()
if not module:load() then
error('unable to load required module ' .. modname)
end
return module:getSandbox()
end
end
table.insert(package.loaders, 1, module_loader)
function import(table)
assert(type(table) == 'table')
local env = getfenv(2)
for k, v in pairs(table) do
env[k] = v
end
end
function export(what, key)
if key ~= nil then
_G[key] = what
else
for k, v in pairs(what) do
_G[k] = v
end
end
end
function unexport(key)
if type(key) == 'table' then
for _k, v in pairs(key) do
_G[v] = nil
end
else
_G[key] = nil
end
end
function getfsrcpath(depth)
depth = depth or 2
local info = debug.getinfo(1 + depth, 'Sn')
local path
if info.short_src then
path = info.short_src:match('(.*)/.*')
end
if not path then
path = '/'
elseif path:sub(0, 1) ~= '/' then
path = '/' .. path
end
return path
end
function resolvepath(filePath, depth)
if not filePath then
return nil
end
depth = depth or 1
if filePath then
if filePath:sub(0, 1) ~= '/' then
local basepath = getfsrcpath(depth + 1)
if basepath:sub(#basepath) ~= '/' then
basepath = basepath .. '/'
end
return basepath .. filePath
else
return filePath
end
else
local basepath = getfsrcpath(depth + 1)
if basepath:sub(#basepath) ~= '/' then
basepath = basepath .. '/'
end
return basepath
end
end
function toboolean(v)
if type(v) == 'string' then
v = v:trim():lower()
if v == '1' or v == 'true' then
return true
end
elseif type(v) == 'number' then
if v == 1 then
return true
end
elseif type(v) == 'boolean' then
return v
end
return false
end
function fromboolean(boolean)
if boolean then
return 'true'
else
return 'false'
end
end
function booleantonumber(boolean)
if boolean then
return 1
else
return 0
end
end
function numbertoboolean(number)
if number ~= 0 then
return true
else
return false
end
end
function protectedcall(func, ...)
local status, ret = pcall(func, ...)
if status then
return ret
end
perror(ret)
return false
end
function signalcall(param, ...)
if type(param) == 'function' then
local status, ret = pcall(param, ...)
if status then
return ret
else
perror(ret)
end
elseif type(param) == 'table' then
for k, v in pairs(param) do
local status, ret = pcall(v, ...)
if status then
if ret then
return true
end
else
perror(ret)
end
end
elseif param ~= nil then
error('attempt to call a non function value')
end
return false
end
function tr(s, ...)
return string.format(s, ...)
end
function getOppositeAnchor(anchor)
if anchor == AnchorLeft then
return AnchorRight
elseif anchor == AnchorRight then
return AnchorLeft
elseif anchor == AnchorTop then
return AnchorBottom
elseif anchor == AnchorBottom then
return AnchorTop
elseif anchor == AnchorVerticalCenter then
return AnchorHorizontalCenter
elseif anchor == AnchorHorizontalCenter then
return AnchorVerticalCenter
end
return anchor
end
function makesingleton(obj)
local singleton = {}
if obj.getClassName then
for key, value in pairs(_G[obj:getClassName()]) do
if type(value) == 'function' then
singleton[key] = function(...)
return value(obj, ...)
end
end
end
end
return singleton
end
-- TFS compliant Lua debugging:
-- https://github.com/otland/forgottenserver/blob/b23850046a2aec05636761c49d296c755865288a/data/lib/debugging/dump.lua
-- recursive dump function
function dumpLevel(input, level)
local indent = ''
for i = 1, level do
indent = indent .. ' '
end
if type(input) == 'table' then
local str = '{ \n'
local lines = {}
for k, v in pairs(input) do
if type(k) ~= 'number' then
k = '"' .. k .. '"'
end
if type(v) == 'string' then
v = '"' .. v .. '"'
end
table.insert(lines, indent .. ' [' .. k .. '] = ' .. dumpLevel(v, level + 1))
end
return str .. table.concat(lines, ',\n') .. '\n' .. indent .. '}'
end
return tostring(input)
end
-- Return a string representation of input for debugging purposes
function dump(input)
return dumpLevel(input, 0)
end
-- Call the dump function and print it to console
function pdump(input)
local dump_str = dump(input)
print(dump_str)
return dump_str
end
-- Call the dump function with a title and print it beautifully to the console
function tdump(title, input)
local title_fill = ''
for i = 1, title:len() do
title_fill = title_fill .. '='
end
local header_str = '\n====' .. title_fill .. '====\n'
header_str = header_str .. '=== ' .. title .. ' ===\n'
header_str = header_str .. '====' .. title_fill .. '====\n'
local dump_str = dump(input)
local footer_str = '\n====' .. title_fill .. '====\n'
print(header_str .. dump_str .. footer_str)
return dump_str
end
-- @}
io.content = function(path)
return g_resources.readFileContents('/' .. path)
end
function pdumpWidgetId(widget, indent)
indent = indent or ""
local children = widget:getChildren()
for i, child in ipairs(children) do
local prefix = (i == #children) and "`-- " or "|-- "
print(indent .. prefix .. child:getId())
local newIndent = (i == #children) and (indent .. " ") or (indent .. "| ")
pdumpWidgetId(child, newIndent)
end
end