mirror of
https://github.com/mehah/otclient
synced 2026-08-15 16:29:06 -04:00
- 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.
468 lines
No EOL
11 KiB
Lua
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 |