Version 2.4 - http://otclient.net/showthread.php?tid=160
22
README.md
@ -1,11 +1,22 @@
|
||||
# OTClientV8
|
||||
|
||||
Tibia client designed for versions 7.40 - 11.00.
|
||||
It's based on https://github.com/edubart/otclient and it's not backward compatible.
|
||||
OTClientV8 is highly optimized tile based 2d game engine built with c++, lua, physfs, OpenGL ES 2.0 and OpenAL.
|
||||
It works well even on 12 years old computers. In 2020 it has been used by more than 90k unique players.
|
||||
Supported platforms:
|
||||
- Windows (min. Windows 7)
|
||||
- Linux
|
||||
- Android (min. 5.0)
|
||||
Planned support:
|
||||
- Mac OS
|
||||
- iOS
|
||||
- WebAssembly
|
||||
|
||||
On this GitHub you can find free version of OTClientV8. It comes without c++ sources, there are prebuilt executables instead.
|
||||
In many cases, you won't need access to sources, you can add a lot of custom features in lua.
|
||||
If you're interested in buying access to sources, contact otclient@otclient.ovh or kondrah#7945 on discord.
|
||||
|
||||
## DISCORD: https://discord.gg/feySup6
|
||||
## Forum: https://otland.net/forums/otclient.494/
|
||||
## Open Tibia Login Server: https://github.com/OTCv8/OpenTibiaLoginServer
|
||||
## Forum: http://otclient.net
|
||||
|
||||
# FEATURES
|
||||
- Rewritten and optimized rendering (60 fps on 11 years old computer)
|
||||
@ -43,9 +54,6 @@ It's based on https://github.com/edubart/otclient and it's not backward compatib
|
||||
|
||||
### There's github repo of tfs 1.3 with otclientv8 features: https://github.com/OTCv8/otclientv8-tfs
|
||||
|
||||
# Paid version
|
||||
The difference between paid version and this one is that the 1st one comes with c++ sources and has better support. You may need c++ source if you want to add some more advanced modifications, better encryption, bot protection or some other things. The free version doesn't offer technical support, you need to follow tutorials and in case of any bug or problem you should submit an issue on github. Visit http://otclient.ovh if you want to know more about paid version and other extra services.
|
||||
|
||||
# Quick Start for players
|
||||
|
||||
Download whole repository and run one of binary file.
|
||||
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
$data = file_get_contents("php://input", false, stream_context_get_default(), 0, $_SERVER["CONTENT_LENGTH"]);
|
||||
if($_REQUEST['txt'] == 1) {
|
||||
file_put_contents("crashes/".time()."_".$_SERVER['REMOTE_ADDR'].".txt", $data);
|
||||
} else if($_REQUEST['txt'] == 2) {
|
||||
file_put_contents("crashes/".time()."_".$_SERVER['REMOTE_ADDR'].".log", $data);
|
||||
} else {
|
||||
file_put_contents("crashes/".time()."_".$_SERVER['REMOTE_ADDR'].".dmp", $data);
|
||||
}
|
||||
echo "OK";
|
||||
?>
|
@ -1,14 +0,0 @@
|
||||
<?php
|
||||
$data = file_get_contents("php://input");
|
||||
if(empty($data)) {
|
||||
return http_response_code(400);
|
||||
}
|
||||
$json = json_decode($data);
|
||||
if(!$json) {
|
||||
return http_response_code(400);
|
||||
}
|
||||
|
||||
file_put_contents("feedback.txt", ($json->player->name) .": ". ($json->text) ."\n".$data."\n\n\n", FILE_APPEND);
|
||||
|
||||
echo "OK";
|
||||
?>
|
134
api/updater.php
@ -1,92 +1,68 @@
|
||||
<?php
|
||||
// set write permission or chmod 777 to dir with this file to let it create checksum files
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
$platform = "";
|
||||
$version = 0;
|
||||
if(!empty($data)) {
|
||||
$platform = $data->platform;
|
||||
$version = $data->version; // currently not used
|
||||
}
|
||||
|
||||
if($platform == "WIN32-WGL") { // opengl
|
||||
$binary_path = "/otclient_gl.exe";
|
||||
$checksums_file = "checksums_gl.txt";
|
||||
} else if($platform == "WIN32-EGL") { // dx
|
||||
$binary_path = "/otclient_dx.exe";
|
||||
$checksums_file = "checksums_dx.txt";
|
||||
} else {
|
||||
$binary_path = "";
|
||||
$checksums_file = "checksums.txt";
|
||||
}
|
||||
|
||||
$data_dir = "/var/www/otclient/files";
|
||||
// CONFIG
|
||||
$files_dir = "/var/www/otclient/files";
|
||||
$files_url = "http://otclient.ovh/files";
|
||||
$update_checksum_interval = 60; // caling updater 100x/s would lag disc, we need to cache it
|
||||
$main_files_and_dirs = array("data", "modules", "layouts", "init.lua"); // used to ignore other files/dirs in data_dir
|
||||
|
||||
$files_and_dirs = array("init.lua", "data", "modules", "layouts");
|
||||
$checksum_file = "checksums.txt";
|
||||
$checksum_update_interval = 60; // seconds
|
||||
$binaries = array(
|
||||
"WIN32-WGL" => "otclient_dx.exe",
|
||||
"WIN32-EGL" => "otclient_gl.exe",
|
||||
"WIN32-WGL-GCC" => "otclient_gcc_dx.exe",
|
||||
"WIN32-EGL-GCC" => "otclient_gcc_gl.exe",
|
||||
"X11-GLX" => "otclient_linux",
|
||||
"X11-EGL" => "otclient_linux",
|
||||
"ANDROID-EGL" => "" // we can't update android binary
|
||||
);
|
||||
// CONFIG END
|
||||
|
||||
$data = array("url" => $files_url, "files" => array(), "things" => array(), "binary" => $binary_path);
|
||||
function sendError($error) {
|
||||
echo(json_encode(array("error" => $error)));
|
||||
die();
|
||||
}
|
||||
|
||||
function getDirFiles($dir, &$results = array()){
|
||||
$files = scandir($dir);
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
//if(!$data) {
|
||||
// sendError("Invalid input data");
|
||||
//}
|
||||
|
||||
foreach($files as $key => $value){
|
||||
$path = realpath($dir.DIRECTORY_SEPARATOR.$value);
|
||||
if(!is_dir($path)) {
|
||||
$results[] = $path;
|
||||
} else if($value != "." && $value != "..") {
|
||||
getDirFiles($path, $results);
|
||||
}
|
||||
$version = $data->version ?: 0; // APP_VERSION from init.lua
|
||||
$build = $data->build ?: ""; // 2.4, 2.4.1, 2.5, etc
|
||||
$os = $data->os ?: "unknown"; // android, windows, mac, linux, unknown
|
||||
$platform = $data->platform ?: ""; // WIN32-WGL, X11-GLX, ANDROID-EGL, etc
|
||||
$args = $data->args; // custom args when calling Updater.check()
|
||||
$binary = $binaries[$platform] ?: "";
|
||||
|
||||
$cache = null;
|
||||
$cache_file = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $checksum_file;
|
||||
if (file_exists($cache_file) && (filemtime($cache_file) + $checksum_update_interval > time())) {
|
||||
$cache = json_decode(file_get_contents($cache_file), true);
|
||||
}
|
||||
if(!$cache) { // update cache
|
||||
$dir = realpath($files_dir);
|
||||
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
|
||||
$cache = array();
|
||||
foreach ($rii as $file) {
|
||||
if (!$file->isFile())
|
||||
continue;
|
||||
$path = str_replace($dir, '', $file->getPathname());
|
||||
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
|
||||
$cache[$path] = hash_file("crc32b", $file->getPathname());
|
||||
}
|
||||
return $results;
|
||||
file_put_contents($cache_file . ".tmp", json_encode($cache));
|
||||
rename($cache_file . ".tmp", $cache_file);
|
||||
}
|
||||
|
||||
function updateChecksums() {
|
||||
global $data_dir;
|
||||
global $main_files_and_dirs;
|
||||
global $binary_path;
|
||||
global $checksums_file;
|
||||
global $data;
|
||||
|
||||
$ret = array();
|
||||
$data_dir_realpath = realpath($data_dir);
|
||||
$files = getDirFiles($data_dir);
|
||||
foreach($files as $file) {
|
||||
$relative_path = str_replace($data_dir_realpath, "", $file);
|
||||
$ps = explode(DIRECTORY_SEPARATOR, $relative_path);
|
||||
if($relative_path == $binary_path || (count($ps) >= 2 && in_array($ps[1], $main_files_and_dirs)))
|
||||
$ret[$relative_path] = md5_file($file);
|
||||
}
|
||||
foreach($ret as $file => $checksum) {
|
||||
$data["files"][$file] = $checksum;
|
||||
}
|
||||
$ret = json_encode($data);
|
||||
if(file_put_contents($checksums_file, $ret) === FALSE) {
|
||||
echo "Can't create checksum file (try to set correct chmod) ". - $checksums_file;
|
||||
exit();
|
||||
$ret = array("url" => $files_url, "files" => array(), "keepFiles" => false);
|
||||
foreach($cache as $file => $checksum) {
|
||||
$base = trim(explode("/", ltrim($file, "/"))[0]);
|
||||
if(in_array($base, $files_and_dirs)) {
|
||||
$ret["files"][$file] = $checksum;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if (function_exists('sem_get')) {
|
||||
$semaphore = sem_get(18237192837, 1, 0666, 1);
|
||||
if(!$semaphore)
|
||||
{
|
||||
echo "Failed to get semaphore - sem_get().\n";
|
||||
exit();
|
||||
if($base == $binary && !empty($binary)) {
|
||||
$ret["binary"] = array("file" => $file, "checksum" => $checksum);
|
||||
}
|
||||
|
||||
sem_acquire($semaphore);
|
||||
}
|
||||
|
||||
$ft = file_exists($checksums_file) ? filemtime($checksums_file) : false;
|
||||
if($ft === false || $ft + $update_checksum_interval < time()) {
|
||||
echo updateChecksums();
|
||||
} else {
|
||||
echo file_get_contents($checksums_file);
|
||||
}
|
||||
if (function_exists('sem_get')) {
|
||||
sem_release($semaphore);
|
||||
}
|
||||
echo(json_encode($ret, JSON_PRETTY_PRINT));
|
||||
|
||||
?>
|
76
api/updater_advanced.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
// CONFIG
|
||||
$files_dir = "/var/www/otclient/files";
|
||||
$files_url = "http://otclient.ovh/files";
|
||||
$files_and_dirs = array("data", "modules", "layouts", "init.lua");
|
||||
$checksum_file = "checksums.txt";
|
||||
$checksum_update_interval = 5; // seconds
|
||||
$binaries = array(
|
||||
"WIN32-WGL" => "otclient_dx.exe",
|
||||
"WIN32-EGL" => "otclient_gl.exe",
|
||||
"WIN32-WGL-GCC" => "otclient_gcc_dx.exe",
|
||||
"WIN32-EGL-GCC" => "otclient_gcc_gl.exe",
|
||||
"X11-GLX" => "otclient_linux",
|
||||
"X11-EGL" => "otclient_linux",
|
||||
"ANDROID-EGL" => "" // we can't update android binary
|
||||
);
|
||||
// CONFIG END
|
||||
|
||||
function sendError($error) {
|
||||
echo(json_encode(array("error" => $error)));
|
||||
die();
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents("php://input"));
|
||||
//if(!$data) {
|
||||
// sendError("Invalid input data");
|
||||
//}
|
||||
|
||||
$version = $data->version ?: 0; // APP_VERSION from init.lua
|
||||
$build = $data->build ?: ""; // 2.4, 2.4.1, 2.5, etc
|
||||
$os = $data->os ?: "unknown"; // android, windows, mac, linux, unknown
|
||||
$platform = $data->platform ?: ""; // WIN32-WGL, X11-GLX, ANDROID-EGL, etc
|
||||
$args = $data->args; // custom args when calling Updater.check()
|
||||
$binary = $binaries[$platform] ?: "";
|
||||
|
||||
$forVersion = "";
|
||||
if($args && $args->version) {
|
||||
$forVersion = strval($args->version);
|
||||
}
|
||||
|
||||
$cache = null;
|
||||
$cache_file = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $checksum_file;
|
||||
if (file_exists($cache_file) && (filemtime($cache_file) + $checksum_update_interval > time())) {
|
||||
$cache = json_decode(file_get_contents($cache_file), true);
|
||||
}
|
||||
if(!$cache) { // update cache
|
||||
$dir = realpath($files_dir);
|
||||
$rii = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS));
|
||||
$cache = array();
|
||||
foreach ($rii as $file) {
|
||||
if (!$file->isFile())
|
||||
continue;
|
||||
$path = str_replace($dir, '', $file->getPathname());
|
||||
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
|
||||
$cache[$path] = hash_file("crc32b", $file->getPathname());
|
||||
}
|
||||
file_put_contents($cache_file . ".tmp", json_encode($cache));
|
||||
rename($cache_file . ".tmp", $cache_file);
|
||||
}
|
||||
$ret = array("url" => $files_url, "files" => array(), "keepFiles" => empty($forVersion) ? false : true);
|
||||
foreach($cache as $file => $checksum) {
|
||||
$base = trim(explode("/", ltrim($file, "/"))[0]);
|
||||
if(strpos($file, "data/things") !== false && (empty($forVersion) || strpos($file, $forVersion) === false)) {
|
||||
continue;
|
||||
}
|
||||
if(in_array($base, $files_and_dirs)) {
|
||||
$ret["files"][$file] = $checksum;
|
||||
}
|
||||
if($base == $binary && !empty($binary)) {
|
||||
$ret["binary"] = array("file" => $file, "checksum" => $checksum);
|
||||
}
|
||||
}
|
||||
|
||||
echo(json_encode($ret, JSON_PRETTY_PRINT));
|
||||
|
||||
?>
|
BIN
d3dcompiler_47.dll
Normal file
@ -4,4 +4,3 @@ Font
|
||||
height: 8
|
||||
glyph-size: 8 8
|
||||
space-width: 2
|
||||
default: true
|
||||
|
BIN
data/images/game/mobile/attack.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
data/images/game/mobile/chat.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
data/images/game/mobile/follow.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
data/images/game/mobile/look.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
data/images/game/mobile/use.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
data/images/topbuttons/zoomin.png
Normal file
After Width: | Height: | Size: 162 B |
BIN
data/images/topbuttons/zoomout.png
Normal file
After Width: | Height: | Size: 143 B |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 18 KiB |
1
data/images/ui/otcicon.rc
Normal file
@ -0,0 +1 @@
|
||||
IDI_ICON1 ICON DISCARDABLE "otcicon.ico"
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 720 B |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 1.5 KiB |
@ -1,36 +0,0 @@
|
||||
Particle
|
||||
name: groupcooldown_particle
|
||||
|
||||
duration: 0.4
|
||||
min-position-radius: 0
|
||||
max-position-radius: 32
|
||||
min-position-angle: 0
|
||||
max-position-angle: 360
|
||||
velocity: 10
|
||||
min-velocity-angle: 0
|
||||
max-velocity-angle: 360
|
||||
colors: #ffffff00 #ffffffff #fff13000
|
||||
colors-stops: 0 0.1 1
|
||||
size: 1 1
|
||||
texture: /particles/particle
|
||||
composition-mode: normal
|
||||
|
||||
Effect
|
||||
name: groupcooldown-effect
|
||||
description: Effect for group cooldowns in the cooldown module
|
||||
|
||||
System
|
||||
position: 0 0
|
||||
|
||||
Emitter
|
||||
position: 0 0
|
||||
delay: 0.06
|
||||
duration: 0.2
|
||||
burst-rate: 350
|
||||
burst-count: 50
|
||||
particle-type: groupcooldown_particle
|
||||
|
||||
AttractionAffector
|
||||
position: 0 0
|
||||
acceleration: 1000
|
||||
|
@ -98,6 +98,7 @@ TopMenu < TopMenuPanel
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font: verdana-11px-antialised
|
||||
text-align: center
|
||||
text-auto-resize: true
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
Background < Panel
|
||||
image-source: /images/background
|
||||
image-smooth: true
|
||||
image-fixed-ratio: true
|
||||
margin-top: 1
|
@ -183,3 +183,4 @@ ConsolePanel < Panel
|
||||
margin-bottom: 6
|
||||
shift-navigation: true
|
||||
max-length: 255
|
||||
text-auto-submit: true
|
||||
|
@ -90,6 +90,7 @@ HealthOverlay < UIWidget
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
image-source: /images/game/circle/right_empty
|
||||
image-auto-resize: true
|
||||
margin-left: 130
|
||||
margin-bottom: 16
|
||||
opacity: 0.5
|
||||
|
2
data/things/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
84
init.lua
@ -1,27 +1,27 @@
|
||||
-- CONFIG
|
||||
APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata
|
||||
APP_VERSION = 1337 -- client version for updater and login to identify outdated client
|
||||
DEFAULT_LAYOUT = "retro"
|
||||
APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata
|
||||
APP_VERSION = 1337 -- client version for updater and login to identify outdated client
|
||||
DEFAULT_LAYOUT = "retro" -- on android it's forced to "mobile", check code bellow
|
||||
|
||||
-- If you don't use updater or other service, set it to updater = ""
|
||||
Services = {
|
||||
website = "http://otclient.ovh", -- currently not used
|
||||
updater = "",
|
||||
updater = "http://otclient.ovh/api/updater.php",
|
||||
stats = "",
|
||||
crash = "http://otclient.ovh/api/crash.php",
|
||||
feedback = "",
|
||||
feedback = "http://otclient.ovh/api/feedback.php",
|
||||
status = "http://otclient.ovh/api/status.php"
|
||||
}
|
||||
|
||||
-- Servers accept http login url, websocket login url or ip:port:version
|
||||
Servers = {
|
||||
-- OTClientV8c = "otclient.ovh:7171:1099:25:30:80:90",
|
||||
-- OTClientV8 = "http://otclient.ovh/api/login.php",
|
||||
-- OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1",
|
||||
-- OTClientV8Test = "http://otclient.ovh/api/login2.php",
|
||||
--[[ OTClientV8 = "http://otclient.ovh/api/login.php",
|
||||
OTClientV8c = "otclient.ovh:7171",
|
||||
OTClientV8Test = "http://otclient.ovh/api/login2.php",
|
||||
LocalTestServ = "127.0.0.1:7171:1098:110:30:93" ]]
|
||||
}
|
||||
|
||||
USE_NEW_ENERGAME = false -- not done yet
|
||||
--USE_NEW_ENERGAME = true -- uses entergamev2 based on websockets instead of entergame
|
||||
ALLOW_CUSTOM_SERVERS = true -- if true it shows option ANOTHER on server list
|
||||
|
||||
g_app.setName("OTCv8")
|
||||
@ -39,52 +39,50 @@ if not g_resources.directoryExists("/modules") then
|
||||
g_logger.fatal("Modules dir doesn't exist.")
|
||||
end
|
||||
|
||||
-- send and delete crash report if exist
|
||||
if Services.crash ~= nil and Services.crash:len() > 4 then
|
||||
local crashLog = g_resources.readCrashLog(false)
|
||||
local crashLogTxt = g_resources.readCrashLog(true)
|
||||
local normalLog = g_logger.getLastLog()
|
||||
local crashed = false
|
||||
if crashLog:len() > 0 then
|
||||
g_http.post(Services.crash .. "?txt=0&version=" .. g_app.getVersion(), crashLog)
|
||||
crashed = true
|
||||
end
|
||||
if crashLogTxt:len() > 0 then
|
||||
g_http.post(Services.crash .. "?txt=1&version=" .. g_app.getVersion(), crashLogTxt)
|
||||
crashed = true
|
||||
end
|
||||
if crashed and normalLog:len() > 0 then
|
||||
g_http.post(Services.crash .. "?txt=2&version=" .. g_app.getVersion(), normalLog)
|
||||
end
|
||||
g_resources.deleteCrashLog()
|
||||
end
|
||||
|
||||
-- settings
|
||||
g_configs.loadSettings("/config.otml")
|
||||
|
||||
-- set layout
|
||||
local settings = g_configs.getSettings()
|
||||
local layout = DEFAULT_LAYOUT
|
||||
if settings:exists('layout') then
|
||||
if g_app.isMobile() then
|
||||
layout = "mobile"
|
||||
elseif settings:exists('layout') then
|
||||
layout = settings:getValue('layout')
|
||||
end
|
||||
g_resources.setLayout(layout)
|
||||
|
||||
-- load mods
|
||||
g_modules.discoverModules()
|
||||
|
||||
-- libraries modules 0-99
|
||||
g_modules.autoLoadModules(99)
|
||||
g_modules.ensureModuleLoaded("corelib")
|
||||
g_modules.ensureModuleLoaded("gamelib")
|
||||
|
||||
local function loadModules()
|
||||
-- libraries modules 0-99
|
||||
g_modules.autoLoadModules(99)
|
||||
g_modules.ensureModuleLoaded("gamelib")
|
||||
|
||||
-- client modules 100-499
|
||||
g_modules.autoLoadModules(499)
|
||||
g_modules.ensureModuleLoaded("client")
|
||||
-- client modules 100-499
|
||||
g_modules.autoLoadModules(499)
|
||||
g_modules.ensureModuleLoaded("client")
|
||||
|
||||
-- game modules 500-999
|
||||
g_modules.autoLoadModules(999)
|
||||
g_modules.ensureModuleLoaded("game_interface")
|
||||
-- game modules 500-999
|
||||
g_modules.autoLoadModules(999)
|
||||
g_modules.ensureModuleLoaded("game_interface")
|
||||
|
||||
-- mods 1000-9999
|
||||
g_modules.autoLoadModules(9999)
|
||||
-- mods 1000-9999
|
||||
g_modules.autoLoadModules(9999)
|
||||
end
|
||||
|
||||
-- report crash
|
||||
if type(Services.crash) == 'string' and Services.crash:len() > 4 and g_modules.getModule("crash_reporter") then
|
||||
g_modules.ensureModuleLoaded("crash_reporter")
|
||||
end
|
||||
|
||||
-- run updater, must use data.zip
|
||||
if type(Services.updater) == 'string' and Services.updater:len() > 4
|
||||
and g_resources.isLoadedFromArchive() and g_modules.getModule("updater") then
|
||||
g_modules.ensureModuleLoaded("updater")
|
||||
return Updater.init(loadModules)
|
||||
end
|
||||
|
||||
loadModules()
|
||||
|
1
layouts/mobile/README.md
Normal file
@ -0,0 +1 @@
|
||||
Min. height for mobile is 360, don't make windows bigger than that
|
108
layouts/mobile/styles/10-scrollbars.otui
Normal file
@ -0,0 +1,108 @@
|
||||
ScrollBarSlider < UIButton
|
||||
id: sliderButton
|
||||
anchors.centerIn: parent
|
||||
size: 16 20
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 26 13 13
|
||||
image-border: 2
|
||||
image-color: #ffffffff
|
||||
$hover:
|
||||
image-clip: 13 26 13 13
|
||||
$pressed:
|
||||
image-clip: 26 26 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
ScrollBarValueLabel < Label
|
||||
id: valueLabel
|
||||
anchors.fill: parent
|
||||
color: white
|
||||
text-align: center
|
||||
|
||||
VerticalScrollBar < UIScrollBar
|
||||
orientation: vertical
|
||||
width: 16
|
||||
height: 39
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 39 0 13 65
|
||||
image-border: 1
|
||||
pixels-scroll: true
|
||||
|
||||
UIButton
|
||||
id: decrementButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 0 13 13
|
||||
image-color: #ffffffff
|
||||
size: 16 16
|
||||
$hover:
|
||||
image-clip: 13 0 13 13
|
||||
$pressed:
|
||||
image-clip: 26 0 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
UIButton
|
||||
id: incrementButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
size: 16 16
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 13 13 13
|
||||
image-color: #ffffffff
|
||||
$hover:
|
||||
image-clip: 13 13 13 13
|
||||
$pressed:
|
||||
image-clip: 26 13 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
ScrollBarSlider
|
||||
|
||||
ScrollBarValueLabel
|
||||
|
||||
HorizontalScrollBar < UIScrollBar
|
||||
orientation: horizontal
|
||||
height: 16
|
||||
width: 39
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 65 52 13
|
||||
image-border: 1
|
||||
|
||||
$disabled:
|
||||
color: #bbbbbb88
|
||||
|
||||
UIButton
|
||||
id: decrementButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 39 13 13
|
||||
image-color: #ffffffff
|
||||
size: 16 16
|
||||
$hover:
|
||||
image-clip: 13 39 13 13
|
||||
$pressed:
|
||||
image-clip: 26 39 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
UIButton
|
||||
id: incrementButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
size: 16 16
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 52 13 13
|
||||
image-color: #ffffffff
|
||||
$hover:
|
||||
image-clip: 13 52 13 13
|
||||
$pressed:
|
||||
image-clip: 26 52 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
ScrollBarSlider
|
||||
|
||||
ScrollBarValueLabel
|
60
layouts/mobile/styles/20-smallscrollbar.otui
Normal file
@ -0,0 +1,60 @@
|
||||
SmallScrollBar < UIScrollBar
|
||||
orientation: vertical
|
||||
margin-bottom: 1
|
||||
step: 20
|
||||
width: 16
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 39 0 13 65
|
||||
image-border: 1
|
||||
pixels-scroll: true
|
||||
|
||||
UIButton
|
||||
id: decrementButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 0 13 13
|
||||
image-color: #ffffffff
|
||||
size: 16 16
|
||||
$hover:
|
||||
image-clip: 13 0 13 13
|
||||
$pressed:
|
||||
image-clip: 26 0 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
UIButton
|
||||
id: incrementButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
size: 16 16
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 13 13 13
|
||||
image-color: #ffffffff
|
||||
$hover:
|
||||
image-clip: 13 13 13 13
|
||||
$pressed:
|
||||
image-clip: 26 13 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
UIButton
|
||||
id: sliderButton
|
||||
anchors.centerIn: parent
|
||||
size: 16 20
|
||||
image-source: /images/ui/scrollbar
|
||||
image-clip: 0 26 13 13
|
||||
image-border: 2
|
||||
image-color: #ffffffff
|
||||
$hover:
|
||||
image-clip: 13 26 13 13
|
||||
$pressed:
|
||||
image-clip: 26 26 13 13
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
|
||||
Label
|
||||
id: valueLabel
|
||||
anchors.fill: parent
|
||||
color: white
|
||||
text-align: center
|
128
layouts/mobile/styles/30-miniwindow.otui
Normal file
@ -0,0 +1,128 @@
|
||||
MiniWindow < UIMiniWindow
|
||||
font: verdana-11px-antialised
|
||||
icon-rect: 4 4 16 16
|
||||
width: 192
|
||||
height: 200
|
||||
text-offset: 24 5
|
||||
text-align: topLeft
|
||||
image-source: /images/ui/miniwindow
|
||||
image-border: 4
|
||||
image-border-top: 23
|
||||
image-border-bottom: 4
|
||||
focusable: false
|
||||
&minimizedHeight: 24
|
||||
|
||||
$on:
|
||||
image-border-bottom: 2
|
||||
|
||||
UIWidget
|
||||
id: miniwindowTopBar
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
margin-right: 3
|
||||
margin-left: 3
|
||||
margin-top: 3
|
||||
size: 258 18
|
||||
phantom: true
|
||||
|
||||
UIButton
|
||||
id: closeButton
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
margin-top: 5
|
||||
margin-right: 5
|
||||
size: 14 14
|
||||
image-source: /images/ui/miniwindow_buttons
|
||||
image-clip: 28 0 14 14
|
||||
|
||||
$hover:
|
||||
image-clip: 28 14 14 14
|
||||
|
||||
$pressed:
|
||||
image-clip: 28 28 14 14
|
||||
|
||||
UIButton
|
||||
id: minimizeButton
|
||||
anchors.top: closeButton.top
|
||||
anchors.right: closeButton.left
|
||||
margin-right: 3
|
||||
size: 14 14
|
||||
image-source: /images/ui/miniwindow_buttons
|
||||
image-clip: 0 0 14 14
|
||||
|
||||
$hover:
|
||||
image-clip: 0 14 14 14
|
||||
|
||||
$pressed:
|
||||
image-clip: 0 28 14 14
|
||||
|
||||
$on:
|
||||
image-clip: 14 0 14 14
|
||||
|
||||
$on hover:
|
||||
image-clip: 14 14 14 14
|
||||
|
||||
$on pressed:
|
||||
image-clip: 14 28 14 14
|
||||
|
||||
UIButton
|
||||
id: lockButton
|
||||
anchors.top: minimizeButton.top
|
||||
anchors.right: minimizeButton.left
|
||||
margin-right: 3
|
||||
size: 14 14
|
||||
image-source: /images/ui/miniwindow_buttons
|
||||
image-clip: 112 0 14 14
|
||||
|
||||
$hover:
|
||||
image-clip: 112 14 14 14
|
||||
|
||||
$pressed:
|
||||
image-clip: 112 28 14 14
|
||||
|
||||
$on:
|
||||
image-clip: 98 0 14 14
|
||||
|
||||
$on hover:
|
||||
image-clip: 98 14 14 14
|
||||
|
||||
$on pressed:
|
||||
image-clip: 98 28 14 14
|
||||
|
||||
VerticalScrollBar
|
||||
id: miniwindowScrollBar
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
step: 14
|
||||
margin-top: 22
|
||||
margin-right: 3
|
||||
margin-bottom: 3
|
||||
pixels-scroll: true
|
||||
|
||||
$!on:
|
||||
width: 0
|
||||
|
||||
ResizeBorder
|
||||
id: bottomResizeBorder
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 8
|
||||
minimum: 48
|
||||
margin-left: 3
|
||||
margin-right: 3
|
||||
background: #ffffff88
|
||||
|
||||
MiniWindowContents < ScrollablePanel
|
||||
id: contentsPanel
|
||||
anchors.fill: parent
|
||||
anchors.right: miniwindowScrollBar.left
|
||||
margin-left: 3
|
||||
margin-bottom: 8
|
||||
margin-top: 22
|
||||
margin-right: 1
|
||||
vertical-scrollbar: miniwindowScrollBar
|
||||
|
||||
HeadlessMiniWindow < MiniWindow
|
165
layouts/mobile/styles/40-console.otui
Normal file
@ -0,0 +1,165 @@
|
||||
ConsoleLabel < UITextEdit
|
||||
font: verdana-11px-antialised
|
||||
height: 14
|
||||
color: yellow
|
||||
margin-left: 2
|
||||
text-wrap: true
|
||||
text-auto-resize: true
|
||||
selection-color: #111416
|
||||
selection-background-color: #999999
|
||||
change-cursor-image: false
|
||||
cursor-visible: false
|
||||
editable: false
|
||||
draggable: true
|
||||
selectable: false
|
||||
focusable: false
|
||||
|
||||
ConsolePhantomLabel < UILabel
|
||||
font: verdana-11px-antialised
|
||||
height: 14
|
||||
color: yellow
|
||||
text-wrap: true
|
||||
text-auto-resize: true
|
||||
selection-color: #111416
|
||||
selection-background-color: #999999
|
||||
|
||||
ConsoleTabBar < MoveableTabBar
|
||||
height: 22
|
||||
|
||||
ConsoleTabBarPanel < MoveableTabBarPanel
|
||||
id: consoleTab
|
||||
|
||||
ScrollablePanel
|
||||
id: consoleBuffer
|
||||
anchors.fill: parent
|
||||
margin-right: 12
|
||||
vertical-scrollbar: consoleScrollBar
|
||||
layout:
|
||||
type: verticalBox
|
||||
align-bottom: true
|
||||
border-width: 1
|
||||
border-color: #202327
|
||||
background: #00000066
|
||||
inverted-scroll: true
|
||||
padding: 1
|
||||
|
||||
VerticalScrollBar
|
||||
id: consoleScrollBar
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
step: 14
|
||||
pixels-scroll: true
|
||||
|
||||
ConsoleTabBarButton < MoveableTabBarButton
|
||||
height: 22
|
||||
padding: 5
|
||||
|
||||
ConsolePanel < Panel
|
||||
image-source: /images/ui/panel_bottom
|
||||
image-border: 4
|
||||
|
||||
$first:
|
||||
anchors.fill: parent
|
||||
|
||||
$!first:
|
||||
anchors.top: prev.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
CheckBox
|
||||
id: toggleChat
|
||||
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
@onCheckChange: toggleChat()
|
||||
visible: false
|
||||
|
||||
TabButton
|
||||
id: prevChannelButton
|
||||
icon: /images/game/console/leftarrow
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
ConsoleTabBar
|
||||
id: consoleTabBar
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
anchors.right: next.left
|
||||
tab-spacing: 2
|
||||
movable: true
|
||||
|
||||
TabButton
|
||||
id: nextChannelButton
|
||||
icon: /images/game/console/rightarrow
|
||||
anchors.right: next.left
|
||||
anchors.top: parent.top
|
||||
|
||||
TabButton
|
||||
id: closeChannelButton
|
||||
!tooltip: tr('Close this channel') .. ' (Ctrl+E)'
|
||||
icon: /images/game/console/closechannel
|
||||
anchors.right: next.left
|
||||
anchors.top: parent.top
|
||||
enabled: false
|
||||
@onClick: removeCurrentTab()
|
||||
|
||||
TabButton
|
||||
id: clearChannelButton
|
||||
!tooltip: tr('Clear current message window')
|
||||
icon: /images/game/console/clearchannel
|
||||
anchors.right: next.left
|
||||
anchors.top: parent.top
|
||||
@onClick: |
|
||||
local consoleTabBar = self:getParent():getChildById('consoleTabBar')
|
||||
clearChannel(consoleTabBar)
|
||||
|
||||
TabButton
|
||||
id: channelsButton
|
||||
!tooltip: tr('Open new channel') .. ' (Ctrl+O)'
|
||||
icon: /images/game/console/channels
|
||||
anchors.right: next.left
|
||||
anchors.top: parent.top
|
||||
@onClick: g_game.requestChannels()
|
||||
|
||||
TabButton
|
||||
id: ignoreButton
|
||||
!tooltip: tr('Ignore players')
|
||||
icon: /images/game/console/ignore
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
@onClick: onClickIgnoreButton()
|
||||
|
||||
Panel
|
||||
id: consoleContentPanel
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: consoleTextEdit.top
|
||||
padding: 1
|
||||
focusable: false
|
||||
phantom: true
|
||||
|
||||
TabButton
|
||||
id: sayModeButton
|
||||
icon: /images/game/console/say
|
||||
!tooltip: tr('Adjust volume')
|
||||
&sayMode: 2
|
||||
size: 22 22
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
margin-left: 6
|
||||
@onClick: sayModeChange()
|
||||
|
||||
TextEdit
|
||||
id: consoleTextEdit
|
||||
anchors.left: sayModeButton.right
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 22
|
||||
margin-right: 6
|
||||
margin-left: 6
|
||||
shift-navigation: true
|
||||
max-length: 255
|
||||
text-auto-submit: true
|
299
layouts/mobile/styles/40-inventory.otui
Normal file
@ -0,0 +1,299 @@
|
||||
InventoryItem < Item
|
||||
$on:
|
||||
image-source: /images/ui/item-blessed
|
||||
|
||||
HeadSlot < InventoryItem
|
||||
id: slot1
|
||||
image-source: /images/game/slots/head
|
||||
&position: {x=65535, y=1, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/head-blessed
|
||||
|
||||
BodySlot < InventoryItem
|
||||
id: slot4
|
||||
image-source: /images/game/slots/body
|
||||
&position: {x=65535, y=4, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/body-blessed
|
||||
|
||||
LegSlot < InventoryItem
|
||||
id: slot7
|
||||
image-source: /images/game/slots/legs
|
||||
&position: {x=65535, y=7, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/legs-blessed
|
||||
|
||||
FeetSlot < InventoryItem
|
||||
id: slot8
|
||||
image-source: /images/game/slots/feet
|
||||
&position: {x=65535, y=8, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/feet-blessed
|
||||
|
||||
NeckSlot < InventoryItem
|
||||
id: slot2
|
||||
image-source: /images/game/slots/neck
|
||||
&position: {x=65535, y=2, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/neck-blessed
|
||||
|
||||
LeftSlot < InventoryItem
|
||||
id: slot6
|
||||
image-source: /images/game/slots/left-hand
|
||||
&position: {x=65535, y=6, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/left-hand-blessed
|
||||
|
||||
FingerSlot < InventoryItem
|
||||
id: slot9
|
||||
image-source: /images/game/slots/finger
|
||||
&position: {x=65535, y=9, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/finger-blessed
|
||||
|
||||
BackSlot < InventoryItem
|
||||
id: slot3
|
||||
image-source: /images/game/slots/back
|
||||
&position: {x=65535, y=3, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/back-blessed
|
||||
|
||||
RightSlot < InventoryItem
|
||||
id: slot5
|
||||
image-source: /images/game/slots/right-hand
|
||||
&position: {x=65535, y=5, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/right-hand-blessed
|
||||
|
||||
AmmoSlot < InventoryItem
|
||||
id: slot10
|
||||
image-source: /images/game/slots/ammo
|
||||
&position: {x=65535, y=10, z=0}
|
||||
$on:
|
||||
image-source: /images/game/slots/ammo-blessed
|
||||
|
||||
PurseButton < UIButton
|
||||
id: purseButton
|
||||
size: 34 12
|
||||
!tooltip: tr('Open purse')
|
||||
icon-source: /images/game/slots/purse
|
||||
icon-clip: 0 0 34 12
|
||||
|
||||
$on:
|
||||
icon-clip: 0 12 34 12
|
||||
|
||||
$pressed:
|
||||
icon-clip: 0 12 34 12
|
||||
|
||||
CombatBox < UICheckBox
|
||||
size: 20 20
|
||||
image-clip: 0 0 20 20
|
||||
margin-left: 4
|
||||
|
||||
$checked:
|
||||
image-clip: 0 20 20 20
|
||||
|
||||
|
||||
InventoryButton < Button
|
||||
font: verdana-11px-antialised
|
||||
height: 18
|
||||
margin-top: 2
|
||||
text-align: center
|
||||
|
||||
SoulCapLabel < GameLabel
|
||||
text-align: center
|
||||
color: #FFFFFF
|
||||
font: cipsoftFont
|
||||
margin-top: 4
|
||||
text-offset: 0 3
|
||||
width: 36
|
||||
height: 20
|
||||
icon-source: /images/game/slots/soulcap
|
||||
|
||||
FightOffensiveBox < CombatBox
|
||||
image-source: /images/game/combatmodes/fightoffensive
|
||||
FightBalancedBox < CombatBox
|
||||
image-source: /images/game/combatmodes/fightbalanced
|
||||
FightDefensiveBox < CombatBox
|
||||
image-source: /images/game/combatmodes/fightdefensive
|
||||
ChaseModeBox < CombatBox
|
||||
image-source: /images/game/combatmodes/chasemode
|
||||
SafeFightBox < CombatBox
|
||||
image-source: /images/game/combatmodes/safefight
|
||||
|
||||
MountButton < CombatBox
|
||||
image-source: /images/game/combatmodes/mount
|
||||
|
||||
InventoryWindow < MiniWindow
|
||||
icon: /images/topbuttons/inventory
|
||||
height: 200
|
||||
id: inventoryWindow
|
||||
@onClose: modules.game_inventory.onMiniWindowClose()
|
||||
&save: true
|
||||
&autoOpen: 3
|
||||
|
||||
MiniWindowContents
|
||||
anchors.left: parent.left
|
||||
|
||||
Panel
|
||||
id: inventoryPanel
|
||||
margin-right: 63
|
||||
margin-top: 2
|
||||
anchors.fill: parent
|
||||
|
||||
HeadSlot
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
BodySlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
LegSlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
FeetSlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
NeckSlot
|
||||
anchors.top: slot1.top
|
||||
anchors.right: slot1.left
|
||||
margin-top: 13
|
||||
margin-right: 5
|
||||
|
||||
LeftSlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
FingerSlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
BackSlot
|
||||
anchors.top: slot1.top
|
||||
anchors.left: slot1.right
|
||||
margin-top: 13
|
||||
margin-left: 5
|
||||
|
||||
RightSlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
AmmoSlot
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
margin-top: 3
|
||||
|
||||
SoulCapLabel
|
||||
id: soulLabel
|
||||
anchors.top: slot10.bottom
|
||||
anchors.horizontalCenter: slot10.horizontalCenter
|
||||
|
||||
SoulCapLabel
|
||||
id: capLabel
|
||||
anchors.top: slot9.bottom
|
||||
anchors.horizontalCenter: slot9.horizontalCenter
|
||||
|
||||
PurseButton
|
||||
anchors.left: slot3.left
|
||||
anchors.bottom: slot3.top
|
||||
margin-bottom: 3
|
||||
|
||||
Panel
|
||||
id: conditionPanel
|
||||
layout:
|
||||
type: horizontalBox
|
||||
height: 22
|
||||
padding: 2
|
||||
anchors.top: slot8.bottom
|
||||
anchors.left: slot6.left
|
||||
anchors.right: slot5.right
|
||||
margin-top: 4
|
||||
border-width: 1
|
||||
border-color: #00000077
|
||||
background-color: #ffffff22
|
||||
|
||||
Panel
|
||||
margin-top: 5
|
||||
anchors.fill: parent
|
||||
anchors.left: prev.right
|
||||
|
||||
FightOffensiveBox
|
||||
id: fightOffensiveBox
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
margin-left: 8
|
||||
|
||||
ChaseModeBox
|
||||
id: chaseModeBox
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
|
||||
FightBalancedBox
|
||||
id: fightBalancedBox
|
||||
margin-top: 22
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
margin-left: 8
|
||||
|
||||
SafeFightBox
|
||||
id: safeFightBox
|
||||
margin-top: 22
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
|
||||
FightDefensiveBox
|
||||
id: fightDefensiveBox
|
||||
margin-top: 44
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
margin-left: 8
|
||||
|
||||
MountButton
|
||||
id: mountButton
|
||||
margin-top: 44
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
|
||||
Panel
|
||||
id: buttonsPanel
|
||||
margin-top: 4
|
||||
margin-right: 5
|
||||
anchors.fill: parent
|
||||
anchors.top: prev.bottom
|
||||
layout:
|
||||
type: verticalBox
|
||||
|
||||
UIButton
|
||||
id: buttonPvp
|
||||
height: 20
|
||||
icon: /images/game/combatmodes/pvp
|
||||
icon-clip: 0 0 42 20
|
||||
|
||||
$on:
|
||||
icon-clip: 0 20 42 20
|
||||
|
||||
InventoryButton
|
||||
!text: tr('Stop')
|
||||
@onClick: g_game.stop(); g_game.cancelAttackAndFollow()
|
||||
|
||||
InventoryButton
|
||||
!text: tr('Options')
|
||||
@onClick: modules.client_options.toggle()
|
||||
|
||||
InventoryButton
|
||||
!text: tr('Quests')
|
||||
@onClick: g_game.requestQuestLog()
|
||||
|
||||
InventoryButton
|
||||
!text: tr('Logout')
|
||||
@onClick: modules.game_interface.tryLogout()
|
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 822 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1009 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 962 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 952 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 571 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 871 B After Width: | Height: | Size: 1.7 KiB |
@ -216,3 +216,4 @@ ConsolePanel < Panel
|
||||
margin-bottom: 2
|
||||
shift-navigation: true
|
||||
max-length: 255
|
||||
text-auto-submit: true
|
||||
|
BIN
libEGL.dll
BIN
libGLESv2.dll
@ -49,7 +49,6 @@
|
||||
#include <framework/graphics/graphics.h>
|
||||
#include <framework/graphics/atlas.h>
|
||||
#include <framework/platform/platformwindow.h>
|
||||
#include <framework/graphics/particlemanager.h>
|
||||
#include <framework/graphics/fontmanager.h>
|
||||
#include <framework/ui/ui.h>
|
||||
#include <framework/input/mouse.h>
|
||||
@ -94,14 +93,14 @@ void Application::registerLuaFunctions()
|
||||
return ret;
|
||||
try {
|
||||
std::smatch m;
|
||||
std::regex e(exp);
|
||||
while (std::regex_search (s,m,e)) {
|
||||
ret.push_back(std::vector<std::string>());
|
||||
for (auto x:m)
|
||||
ret[ret.size() - 1].push_back(x);
|
||||
s = m.suffix().str();
|
||||
if (--limit == 0)
|
||||
return ret;
|
||||
std::regex e(exp, std::regex::ECMAScript);
|
||||
while (std::regex_search (s,m,e)) {
|
||||
ret.push_back(std::vector<std::string>());
|
||||
for (auto x:m)
|
||||
ret[ret.size() - 1].push_back(x);
|
||||
s = m.suffix().str();
|
||||
if (--limit == 0)
|
||||
return ret;
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
@ -129,6 +128,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_platform", "getUserName", &Platform::getUserName, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "getDlls", &Platform::getDlls, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "getProcesses", &Platform::getProcesses, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "getWindows", &Platform::getWindows, &g_platform);
|
||||
|
||||
// Application
|
||||
g_lua.registerSingletonClass("g_app");
|
||||
@ -151,6 +151,8 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_app", "getStartupOptions", &Application::getStartupOptions, static_cast<Application*>(&g_app));
|
||||
g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app));
|
||||
g_lua.bindSingletonFunction("g_app", "quick_exit", &Application::quick_exit, static_cast<Application*>(&g_app));
|
||||
g_lua.bindSingletonFunction("g_app", "isMobile", &Application::isMobile, static_cast<Application*>(&g_app));
|
||||
g_lua.bindSingletonFunction("g_app", "restart", &Application::restart, static_cast<Application*>(&g_app));
|
||||
|
||||
// Crypt
|
||||
g_lua.registerSingletonClass("g_crypt");
|
||||
@ -161,6 +163,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_crypt", "decrypt", &Crypt::decrypt, &g_crypt);
|
||||
g_lua.bindSingletonFunction("g_crypt", "sha1Encode", &Crypt::sha1Encode, &g_crypt);
|
||||
g_lua.bindSingletonFunction("g_crypt", "md5Encode", &Crypt::md5Encode, &g_crypt);
|
||||
g_lua.bindSingletonFunction("g_crypt", "crc32", &Crypt::crc32, &g_crypt);
|
||||
g_lua.bindSingletonFunction("g_crypt", "rsaGenerateKey", &Crypt::rsaGenerateKey, &g_crypt);
|
||||
g_lua.bindSingletonFunction("g_crypt", "rsaSetPublicKey", &Crypt::rsaSetPublicKey, &g_crypt);
|
||||
g_lua.bindSingletonFunction("g_crypt", "rsaSetPrivateKey", &Crypt::rsaSetPrivateKey, &g_crypt);
|
||||
@ -226,7 +229,6 @@ void Application::registerLuaFunctions()
|
||||
|
||||
g_lua.registerSingletonClass("g_atlas");
|
||||
g_lua.bindSingletonFunction("g_atlas", "getStats", &Atlas::getStats, &g_atlas);
|
||||
g_lua.bindSingletonFunction("g_atlas", "reset", &Atlas::reset, &g_atlas);
|
||||
|
||||
// ModuleManager
|
||||
g_lua.registerSingletonClass("g_modules");
|
||||
@ -259,16 +261,18 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_resources", "guessFilePath", &ResourceManager::guessFilePath, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "makeDir", &ResourceManager::makeDir, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "deleteFile", &ResourceManager::deleteFile, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "readCrashLog", &ResourceManager::readCrashLog, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "deleteCrashLog", &ResourceManager::deleteCrashLog, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "readCrashLog", [] { return std::string(); });
|
||||
g_lua.bindSingletonFunction("g_resources", "deleteCrashLog", [] { return std::string(); });
|
||||
|
||||
g_lua.bindSingletonFunction("g_resources", "resolvePath", &ResourceManager::resolvePath, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "isLoadedFromMemory", &ResourceManager::isLoadedFromMemory, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "isLoadedFromArchive", &ResourceManager::isLoadedFromArchive, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "listUpdateableFiles", &ResourceManager::listUpdateableFiles, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "listUpdateableFiles", [] { return std::list<std::string>(); } );
|
||||
g_lua.bindSingletonFunction("g_resources", "fileChecksum", &ResourceManager::fileChecksum, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "filesChecksums", &ResourceManager::filesChecksums, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "selfChecksum", &ResourceManager::selfChecksum, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "updateClient", &ResourceManager::updateClient, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "updateData", &ResourceManager::updateData, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "updateExecutable", &ResourceManager::updateExecutable, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "setLayout", &ResourceManager::setLayout, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "getLayout", &ResourceManager::getLayout, &g_resources);
|
||||
|
||||
@ -326,8 +330,13 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_app", "setMaxFps", &GraphicalApplication::setMaxFps, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "getMaxFps", &GraphicalApplication::getMaxFps, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "getFps", &GraphicalApplication::getFps, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "getGraphicsFps", &GraphicalApplication::getGraphicsFps, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "getProcessingFps", &GraphicalApplication::getProcessingFps, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "isOnInputEvent", &GraphicalApplication::isOnInputEvent, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "doScreenshot", &GraphicalApplication::doScreenshot, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "scaleDown", &GraphicalApplication::scaleDown, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "scaleUp", &GraphicalApplication::scaleUp, &g_app);
|
||||
g_lua.bindSingletonFunction("g_app", "scale", &GraphicalApplication::scale, &g_app);
|
||||
|
||||
// AdaptiveRenderer
|
||||
g_lua.registerSingletonClass("g_adaptiveRenderer");
|
||||
@ -341,7 +350,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_window", "resize", &PlatformWindow::resize, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "show", &PlatformWindow::show, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "hide", &PlatformWindow::hide, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "poll", &PlatformWindow::poll, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "poll", [] {}); // for backward compability
|
||||
g_lua.bindSingletonFunction("g_window", "maximize", &PlatformWindow::maximize, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "restoreMouseCursor", &PlatformWindow::restoreMouseCursor, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "showMouse", &PlatformWindow::showMouse, &g_window);
|
||||
@ -374,7 +383,8 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_window", "isFullscreen", &PlatformWindow::isFullscreen, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "isMaximized", &PlatformWindow::isMaximized, &g_window);
|
||||
g_lua.bindSingletonFunction("g_window", "hasFocus", &PlatformWindow::hasFocus, &g_window);
|
||||
|
||||
g_lua.bindSingletonFunction("g_window", "showTextEditor", &PlatformWindow::showTextEditor, &g_window);
|
||||
|
||||
// Input
|
||||
g_lua.registerSingletonClass("g_mouse");
|
||||
g_lua.bindSingletonFunction("g_mouse", "loadCursors", &Mouse::loadCursors, &g_mouse);
|
||||
@ -386,14 +396,10 @@ void Application::registerLuaFunctions()
|
||||
|
||||
// Graphics
|
||||
g_lua.registerSingletonClass("g_graphics");
|
||||
g_lua.bindSingletonFunction("g_graphics", "canCacheBackbuffer", &Graphics::canCacheBackbuffer, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "canUseShaders", &Graphics::canUseShaders, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "shouldUseShaders", &Graphics::shouldUseShaders, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "setShouldUseShaders", &Graphics::setShouldUseShaders, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "getViewportSize", &Graphics::getViewportSize, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "getVendor", &Graphics::getVendor, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "getRenderer", &Graphics::getRenderer, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "getVersion", &Graphics::getVersion, &g_graphics);
|
||||
g_lua.bindSingletonFunction("g_graphics", "getExtensions", &Graphics::getExtensions, &g_graphics);
|
||||
|
||||
// Textures
|
||||
g_lua.registerSingletonClass("g_textures");
|
||||
@ -428,10 +434,9 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_fonts", "fontExists", &FontManager::fontExists, &g_fonts);
|
||||
g_lua.bindSingletonFunction("g_fonts", "setDefaultFont", &FontManager::setDefaultFont, &g_fonts);
|
||||
|
||||
// ParticleManager
|
||||
// Particles, for backward compability
|
||||
g_lua.registerSingletonClass("g_particles");
|
||||
g_lua.bindSingletonFunction("g_particles", "importParticle", &ParticleManager::importParticle, &g_particles);
|
||||
g_lua.bindSingletonFunction("g_particles", "getEffectsTypes", &ParticleManager::getEffectsTypes, &g_particles);
|
||||
g_lua.bindSingletonFunction("g_particles", "importParticle", [](const std::string& v) {});
|
||||
|
||||
// UIWidget
|
||||
g_lua.registerClass<UIWidget>();
|
||||
@ -510,7 +515,6 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIWidget>("backwardsGetWidgetById", &UIWidget::backwardsGetWidgetById);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("resize", &UIWidget::resize);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("move", &UIWidget::move);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("rotate", &UIWidget::rotate);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("hide", &UIWidget::hide);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("show", &UIWidget::show);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("disable", &UIWidget::disable);
|
||||
@ -695,6 +699,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIWidget>("resizeToText", &UIWidget::resizeToText);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("clearText", &UIWidget::clearText);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setText", &UIWidget::setText);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setColoredText", &UIWidget::setColoredText);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setTextAlign", &UIWidget::setTextAlign);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setTextOffset", &UIWidget::setTextOffset);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setTextWrap", &UIWidget::setTextWrap);
|
||||
@ -822,17 +827,6 @@ void Application::registerLuaFunctions()
|
||||
g_lua.registerClass<ShaderProgram>();
|
||||
g_lua.registerClass<PainterShaderProgram>();
|
||||
g_lua.bindClassMemberFunction<PainterShaderProgram>("addMultiTexture", &PainterShaderProgram::addMultiTexture);
|
||||
|
||||
// ParticleEffect
|
||||
g_lua.registerClass<ParticleEffectType>();
|
||||
g_lua.bindClassStaticFunction<ParticleEffectType>("create", []{ return ParticleEffectTypePtr(new ParticleEffectType); });
|
||||
g_lua.bindClassMemberFunction<ParticleEffectType>("getName", &ParticleEffectType::getName);
|
||||
g_lua.bindClassMemberFunction<ParticleEffectType>("getDescription", &ParticleEffectType::getDescription);
|
||||
|
||||
// UIParticles
|
||||
g_lua.registerClass<UIParticles, UIWidget>();
|
||||
g_lua.bindClassStaticFunction<UIParticles>("create", []{ return UIParticlesPtr(new UIParticles); } );
|
||||
g_lua.bindClassMemberFunction<UIParticles>("addEffect", &UIParticles::addEffect);
|
||||
#endif
|
||||
|
||||
#ifdef FW_NET
|
@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "luavaluecasts.h"
|
||||
#include "luavaluecasts_client.h"
|
||||
#include "game.h"
|
||||
#include "tile.h"
|
||||
#include "houses.h"
|
||||
@ -162,11 +162,12 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_map", "isForcingAnimations", &Map::isForcingAnimations, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "isShowingAnimations", &Map::isShowingAnimations, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "setShowAnimations", &Map::setShowAnimations, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "beginGhostMode", &Map::beginGhostMode, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "endGhostMode", &Map::endGhostMode, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "findItemsById", &Map::findItemsById, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "getAwareRange", &Map::getAwareRangeAsSize, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "findEveryPath", &Map::findEveryPath, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "getMinimapColor", &Map::getMinimapColor, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "isPatchable", &Map::isPatchable, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "isWalkable", &Map::isWalkable, &g_map);
|
||||
|
||||
g_lua.registerSingletonClass("g_minimap");
|
||||
g_lua.bindSingletonFunction("g_minimap", "clean", &Minimap::clean, &g_minimap);
|
||||
@ -329,6 +330,9 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_game", "applyImbuement", &Game::applyImbuement, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "clearImbuement", &Game::clearImbuement, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "closeImbuingWindow", &Game::closeImbuingWindow, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "setTibiaCoins", &Game::setTibiaCoins, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "getTibiaCoins", &Game::getTibiaCoins, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "getTransferableTibiaCoins", &Game::getTransferableTibiaCoins, &g_game);
|
||||
|
||||
g_lua.bindSingletonFunction("g_game", "getMaxPreWalkingSteps", &Game::getMaxPreWalkingSteps, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "setMaxPreWalkingSteps", &Game::setMaxPreWalkingSteps, &g_game);
|
||||
@ -339,7 +343,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_game", "getRecivedPacketsCount", &Game::getRecivedPacketsCount, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "getRecivedPacketsSize", &Game::getRecivedPacketsSize, &g_game);
|
||||
|
||||
g_lua.registerSingletonClass("g_shaders");
|
||||
/* g_lua.registerSingletonClass("g_shaders");
|
||||
g_lua.bindSingletonFunction("g_shaders", "createShader", &ShaderManager::createShader, &g_shaders);
|
||||
g_lua.bindSingletonFunction("g_shaders", "createFragmentShader", &ShaderManager::createFragmentShader, &g_shaders);
|
||||
g_lua.bindSingletonFunction("g_shaders", "createFragmentShaderFromCode", &ShaderManager::createFragmentShaderFromCode, &g_shaders);
|
||||
@ -347,7 +351,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_shaders", "createMapShader", &ShaderManager::createMapShader, &g_shaders);
|
||||
g_lua.bindSingletonFunction("g_shaders", "getDefaultItemShader", &ShaderManager::getDefaultItemShader, &g_shaders);
|
||||
g_lua.bindSingletonFunction("g_shaders", "getDefaultMapShader", &ShaderManager::getDefaultMapShader, &g_shaders);
|
||||
g_lua.bindSingletonFunction("g_shaders", "getShader", &ShaderManager::getShader, &g_shaders);
|
||||
g_lua.bindSingletonFunction("g_shaders", "getShader", &ShaderManager::getShader, &g_shaders); */
|
||||
|
||||
g_lua.bindGlobalFunction("getOutfitColor", Outfit::getColor);
|
||||
g_lua.bindGlobalFunction("getAngleFromPos", Position::getAngleFromPositions);
|
||||
@ -496,6 +500,9 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<Creature>("getSkull", &Creature::getSkull);
|
||||
g_lua.bindClassMemberFunction<Creature>("getShield", &Creature::getShield);
|
||||
g_lua.bindClassMemberFunction<Creature>("getEmblem", &Creature::getEmblem);
|
||||
g_lua.bindClassMemberFunction<Creature>("setSkull", &Creature::setSkull);
|
||||
g_lua.bindClassMemberFunction<Creature>("setShield", &Creature::setShield);
|
||||
g_lua.bindClassMemberFunction<Creature>("setEmblem", &Creature::setEmblem);
|
||||
g_lua.bindClassMemberFunction<Creature>("getType", &Creature::getType);
|
||||
g_lua.bindClassMemberFunction<Creature>("getIcon", &Creature::getIcon);
|
||||
g_lua.bindClassMemberFunction<Creature>("setOutfit", &Creature::setOutfit);
|
||||
@ -526,6 +533,10 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<Creature>("resetInformationColor", &Creature::resetInformationColor);
|
||||
g_lua.bindClassMemberFunction<Creature>("setInformationOffset", &Creature::setInformationOffset);
|
||||
g_lua.bindClassMemberFunction<Creature>("getInformationOffset", &Creature::getInformationOffset);
|
||||
g_lua.bindClassMemberFunction<Creature>("setText", &Creature::setText);
|
||||
g_lua.bindClassMemberFunction<Creature>("getText", &Creature::getText);
|
||||
g_lua.bindClassMemberFunction<Creature>("clearText", &Creature::clearText);
|
||||
|
||||
// widgets
|
||||
g_lua.bindClassMemberFunction<Creature>("addTopWidget", &Creature::addTopWidget);
|
||||
g_lua.bindClassMemberFunction<Creature>("addBottomWidget", &Creature::addBottomWidget);
|
||||
@ -630,6 +641,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<Item>("setCount", &Item::setCount);
|
||||
g_lua.bindClassMemberFunction<Item>("getCount", &Item::getCount);
|
||||
g_lua.bindClassMemberFunction<Item>("getSubType", &Item::getSubType);
|
||||
g_lua.bindClassMemberFunction<Item>("getCountOrSubType", &Item::getCountOrSubType);
|
||||
g_lua.bindClassMemberFunction<Item>("getId", &Item::getId);
|
||||
g_lua.bindClassMemberFunction<Item>("getServerId", &Item::getServerId);
|
||||
g_lua.bindClassMemberFunction<Item>("getName", &Item::getName);
|
||||
@ -665,6 +677,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.registerClass<StaticText, Thing>();
|
||||
g_lua.bindClassStaticFunction<StaticText>("create", []{ return StaticTextPtr(new StaticText); });
|
||||
g_lua.bindClassMemberFunction<StaticText>("addMessage", &StaticText::addMessage);
|
||||
g_lua.bindClassMemberFunction<StaticText>("addColoredMessage", &StaticText::addColoredMessage);
|
||||
g_lua.bindClassMemberFunction<StaticText>("setText", &StaticText::setText);
|
||||
g_lua.bindClassMemberFunction<StaticText>("setFont", &StaticText::setFont);
|
||||
g_lua.bindClassMemberFunction<StaticText>("setColor", &StaticText::setColor);
|
||||
@ -800,6 +813,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIItem>("getItemId", &UIItem::getItemId);
|
||||
g_lua.bindClassMemberFunction<UIItem>("getItemCount", &UIItem::getItemCount);
|
||||
g_lua.bindClassMemberFunction<UIItem>("getItemSubType", &UIItem::getItemSubType);
|
||||
g_lua.bindClassMemberFunction<UIItem>("getItemCountOrSubType", &UIItem::getItemCountOrSubType);
|
||||
g_lua.bindClassMemberFunction<UIItem>("getItem", &UIItem::getItem);
|
||||
g_lua.bindClassMemberFunction<UIItem>("isVirtual", &UIItem::isVirtual);
|
||||
g_lua.bindClassMemberFunction<UIItem>("isItemVisible", &UIItem::isItemVisible);
|
||||
@ -823,7 +837,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UICreature>("setDirection", &UICreature::setDirection);
|
||||
g_lua.bindClassMemberFunction<UICreature>("setScale", &UICreature::setScale);
|
||||
g_lua.bindClassMemberFunction<UICreature>("getScale", &UICreature::getScale);
|
||||
g_lua.bindClassMemberFunction<UICreature>("setRaw", &UICreature::setRaw);
|
||||
g_lua.bindClassMemberFunction<UICreature>("setOptimized", &UICreature::setOptimized);
|
||||
|
||||
g_lua.registerClass<UIMap, UIWidget>();
|
||||
g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); });
|
||||
@ -850,7 +864,6 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIMap>("setDrawPlayerBars", &UIMap::setDrawPlayerBars);
|
||||
g_lua.bindClassMemberFunction<UIMap>("setAnimated", &UIMap::setAnimated);
|
||||
g_lua.bindClassMemberFunction<UIMap>("setKeepAspectRatio", &UIMap::setKeepAspectRatio);
|
||||
g_lua.bindClassMemberFunction<UIMap>("setMapShader", &UIMap::setMapShader);
|
||||
g_lua.bindClassMemberFunction<UIMap>("setMinimumAmbientLight", &UIMap::setMinimumAmbientLight);
|
||||
g_lua.bindClassMemberFunction<UIMap>("setLimitVisibleRange", &UIMap::setLimitVisibleRange);
|
||||
g_lua.bindClassMemberFunction<UIMap>("setFloorFading", &UIMap::setFloorFading);
|
||||
@ -875,7 +888,6 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIMap>("getMaxZoomIn", &UIMap::getMaxZoomIn);
|
||||
g_lua.bindClassMemberFunction<UIMap>("getMaxZoomOut", &UIMap::getMaxZoomOut);
|
||||
g_lua.bindClassMemberFunction<UIMap>("getZoom", &UIMap::getZoom);
|
||||
g_lua.bindClassMemberFunction<UIMap>("getMapShader", &UIMap::getMapShader);
|
||||
g_lua.bindClassMemberFunction<UIMap>("getMinimumAmbientLight", &UIMap::getMinimumAmbientLight);
|
||||
|
||||
g_lua.registerClass<UIMinimap, UIWidget>();
|
||||
|
@ -57,14 +57,17 @@ function init()
|
||||
connect(g_game, { onGameStart = onGameStart,
|
||||
onGameEnd = onGameEnd })
|
||||
|
||||
g_window.setMinimumSize({ width = 800, height = 600 })
|
||||
if g_sounds ~= nil then
|
||||
--g_sounds.preload(musicFilename)
|
||||
end
|
||||
-- initialize in fullscreen mode on mobile devices
|
||||
if g_window.getPlatformType() == "X11-EGL" then
|
||||
g_window.setFullscreen(true)
|
||||
else
|
||||
|
||||
if not Updater then
|
||||
if g_resources.getLayout() == "mobile" then
|
||||
g_window.setMinimumSize({ width = 640, height = 360 })
|
||||
else
|
||||
g_window.setMinimumSize({ width = 800, height = 640 })
|
||||
end
|
||||
|
||||
-- window size
|
||||
local size = { width = 1024, height = 600 }
|
||||
size = g_settings.getSize('window-size', size)
|
||||
@ -87,12 +90,7 @@ function init()
|
||||
g_window.setTitle(g_app.getName())
|
||||
g_window.setIcon('/images/clienticon')
|
||||
|
||||
-- poll resize events
|
||||
g_window.poll()
|
||||
|
||||
g_keyboard.bindKeyDown('Ctrl+Shift+R', reloadScripts)
|
||||
g_keyboard.bindKeyDown('Ctrl+Shift+[', function() g_extras.setTestMode((g_extras.getTestMode() - 1) % 10) end)
|
||||
g_keyboard.bindKeyDown('Ctrl+Shift+]', function() g_extras.setTestMode((g_extras.getTestMode() + 1) % 10) end)
|
||||
|
||||
-- generate machine uuid, this is a security measure for storing passwords
|
||||
if not g_crypt.setMachineUUID(g_settings.get('uuid')) then
|
||||
|
@ -14,10 +14,11 @@ Module
|
||||
- client_locales
|
||||
- client_topmenu
|
||||
- client_background
|
||||
- client_textedit
|
||||
- client_options
|
||||
- client_entergame
|
||||
- client_entergamev2
|
||||
- client_terminal
|
||||
- client_stats
|
||||
- client_feedback
|
||||
- client_updater
|
||||
- client_mobile
|
||||
|
@ -8,9 +8,8 @@ function init()
|
||||
background:lower()
|
||||
|
||||
clientVersionLabel = background:getChildById('clientVersionLabel')
|
||||
clientVersionLabel:setText(g_app.getName() .. ' ' .. g_app.getVersion() .. '\nMade by:\n' .. g_app.getAuthor() .. "\notclient@otclient.ovh")
|
||||
clientVersionLabel:setText('OTClientV8 ' .. g_app.getVersion() .. '\nMade by:\n' .. g_app.getAuthor() .. "\notclient@otclient.ovh")
|
||||
|
||||
|
||||
if not g_game.isOnline() then
|
||||
addEvent(function() g_effects.fadeIn(clientVersionLabel, 1500) end)
|
||||
end
|
||||
|
@ -5,6 +5,5 @@ Module
|
||||
website: https://github.com/edubart/otclient
|
||||
sandboxed: true
|
||||
scripts: [ background ]
|
||||
dependencies: [ client_topmenu ]
|
||||
@onLoad: init()
|
||||
@onUnload: terminate()
|
||||
|
@ -1,7 +1,11 @@
|
||||
Background
|
||||
UIWidget
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
focusable: false
|
||||
image-source: /images/background
|
||||
image-smooth: true
|
||||
image-fixed-ratio: true
|
||||
margin-top: 1
|
||||
|
||||
UILabel
|
||||
id: clientVersionLabel
|
||||
|
@ -45,12 +45,14 @@ StaticMainWindow
|
||||
id: charactersWindow
|
||||
!text: tr('Character List')
|
||||
visible: false
|
||||
size: 350 400
|
||||
$mobile:
|
||||
size: 350 280
|
||||
@onEnter: CharacterList.doLogin()
|
||||
@onEscape: CharacterList.hide(true)
|
||||
@onSetup: |
|
||||
g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self)
|
||||
g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self)
|
||||
self:setSize({width = 350, height = 400})
|
||||
g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self)
|
||||
|
||||
TextList
|
||||
id: characters
|
||||
|
@ -17,6 +17,8 @@ local serverHostTextEdit
|
||||
local rememberPasswordBox
|
||||
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "870", "961", "1077", "1090", "1096", "1098", "1099", "1100"}
|
||||
|
||||
local checkedByUpdater = {}
|
||||
|
||||
-- private functions
|
||||
local function onProtocolError(protocol, message, errorCode)
|
||||
if errorCode then
|
||||
@ -129,11 +131,7 @@ local function onHTTPResult(data, err)
|
||||
local incorrectThings = validateThings(things)
|
||||
if #incorrectThings > 0 then
|
||||
g_logger.info(incorrectThings)
|
||||
if Updater then
|
||||
return Updater.updateThings(things, incorrectThings)
|
||||
else
|
||||
return EnterGame.onError(incorrectThings)
|
||||
end
|
||||
return EnterGame.onError(incorrectThings)
|
||||
end
|
||||
|
||||
-- custom protocol
|
||||
@ -264,9 +262,6 @@ end
|
||||
|
||||
function EnterGame.show()
|
||||
if not enterGame then return end
|
||||
if Updater and Updater.isVisible() or g_game.isOnline() then
|
||||
return EnterGame.hide()
|
||||
end
|
||||
enterGame:show()
|
||||
enterGame:raise()
|
||||
enterGame:focus()
|
||||
@ -313,9 +308,6 @@ function EnterGame.onServerChange()
|
||||
end
|
||||
|
||||
function EnterGame.doLogin()
|
||||
if Updater and Updater.isVisible() then
|
||||
return
|
||||
end
|
||||
if g_game.isOnline() then
|
||||
local errorBox = displayErrorBox(tr('Login Error'), tr('Cannot login while already in game.'))
|
||||
connect(errorBox, { onOk = EnterGame.show })
|
||||
@ -339,23 +331,20 @@ function EnterGame.doLogin()
|
||||
g_settings.set('client-version', G.clientVersion)
|
||||
g_settings.save()
|
||||
|
||||
if G.host:find("ws://") ~= nil or G.host:find("wss://") ~= nil then
|
||||
return EnterGame.doLoginWs()
|
||||
end
|
||||
if G.host:find("http") ~= nil then
|
||||
return EnterGame.doLoginHttp()
|
||||
end
|
||||
|
||||
local server_params = G.host:split(":")
|
||||
if #server_params < 2 then
|
||||
return EnterGame.onError("Invalid server, it should be in format IP:PORT or it should be http url to login script")
|
||||
end
|
||||
local server_ip = server_params[1]
|
||||
local server_port = tonumber(server_params[2])
|
||||
local server_port = 7171
|
||||
if #server_params >= 2 then
|
||||
server_port = tonumber(server_params[2])
|
||||
end
|
||||
if #server_params >= 3 then
|
||||
G.clientVersion = tonumber(server_params[3])
|
||||
end
|
||||
if not server_port or not G.clientVersion then
|
||||
if type(server_ip) ~= 'string' or server_ip:len() <= 3 or not server_port or not G.clientVersion then
|
||||
return EnterGame.onError("Invalid server, it should be in format IP:PORT or it should be http url to login script")
|
||||
end
|
||||
|
||||
@ -367,8 +356,12 @@ function EnterGame.doLogin()
|
||||
local incorrectThings = validateThings(things)
|
||||
if #incorrectThings > 0 then
|
||||
g_logger.error(incorrectThings)
|
||||
if Updater then
|
||||
return Updater.updateThings(things, incorrectThings)
|
||||
if Updater and not checkedByUpdater[G.clientVersion] then
|
||||
checkedByUpdater[G.clientVersion] = true
|
||||
return Updater.check({
|
||||
version = G.clientVersion,
|
||||
host = G.host
|
||||
})
|
||||
else
|
||||
return EnterGame.onError(incorrectThings)
|
||||
end
|
||||
|
@ -6,4 +6,7 @@ Module
|
||||
scripts: [ entergame, characterlist ]
|
||||
@onLoad: EnterGame.init() CharacterList.init()
|
||||
@onUnload: EnterGame.terminate() CharacterList.terminate()
|
||||
|
||||
|
||||
load-later:
|
||||
- game_things
|
||||
- game_features
|
||||
|
@ -4,19 +4,7 @@ dofile 'neededtranslations'
|
||||
local defaultLocaleName = 'en'
|
||||
local installedLocales
|
||||
local currentLocale
|
||||
|
||||
function sendLocale(localeName)
|
||||
if not g_game.getFeature(GameExtendedOpcode) then
|
||||
return
|
||||
end
|
||||
|
||||
local protocolGame = g_game.getProtocolGame()
|
||||
if protocolGame then
|
||||
protocolGame:sendExtendedOpcode(ExtendedIds.Locale, localeName)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
local missingTranslations = {}
|
||||
|
||||
function createWindow()
|
||||
localesWindow = g_ui.displayUI('locales')
|
||||
@ -51,18 +39,6 @@ function selectFirstLocale(name)
|
||||
g_settings.save()
|
||||
end
|
||||
|
||||
-- hooked functions
|
||||
function onGameStart()
|
||||
sendLocale(currentLocale.name)
|
||||
end
|
||||
|
||||
function onExtendedLocales(protocol, opcode, buffer)
|
||||
local locale = installedLocales[buffer]
|
||||
if locale and setLocale(locale.name) then
|
||||
g_modules.reloadModules()
|
||||
end
|
||||
end
|
||||
|
||||
-- public functions
|
||||
function init()
|
||||
installedLocales = {}
|
||||
@ -76,18 +52,13 @@ function init()
|
||||
setLocale(defaultLocaleName)
|
||||
--connect(g_app, { onRun = createWindow })
|
||||
end
|
||||
|
||||
ProtocolGame.registerExtendedOpcode(ExtendedIds.Locale, onExtendedLocales)
|
||||
connect(g_game, { onGameStart = onGameStart })
|
||||
end
|
||||
|
||||
function terminate()
|
||||
installedLocales = nil
|
||||
currentLocale = nil
|
||||
|
||||
ProtocolGame.unregisterExtendedOpcode(ExtendedIds.Locale)
|
||||
disconnect(g_app, { onRun = createWindow })
|
||||
disconnect(g_game, { onGameStart = onGameStart })
|
||||
--disconnect(g_app, { onRun = createWindow })
|
||||
end
|
||||
|
||||
function generateNewTranslationTable(localename)
|
||||
@ -154,9 +125,6 @@ function setLocale(name)
|
||||
pwarning("Locale " .. name .. ' does not exist.')
|
||||
return false
|
||||
end
|
||||
if currentLocale then
|
||||
sendLocale(locale.name)
|
||||
end
|
||||
currentLocale = locale
|
||||
g_settings.set('locale', name)
|
||||
if onLocaleChanged then onLocaleChanged(name) end
|
||||
@ -194,7 +162,10 @@ function _G.tr(text, ...)
|
||||
if not translation then
|
||||
if translation == nil then
|
||||
if currentLocale.name ~= defaultLocaleName then
|
||||
pdebug('Unable to translate: \"' .. text .. '\"')
|
||||
if not missingTranslations[text] then
|
||||
pdebug('Unable to translate: \"' .. text .. '\"')
|
||||
missingTranslations[text] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
translation = text
|
||||
|
84
modules/client_mobile/mobile.lua
Normal file
@ -0,0 +1,84 @@
|
||||
local overlay
|
||||
local touchStart = 0
|
||||
local updateCursorEvent = nil
|
||||
local zoomInButton
|
||||
local zoomOutButton
|
||||
|
||||
-- public functions
|
||||
function init()
|
||||
if not g_app.isMobile() then return end
|
||||
overlay = g_ui.displayUI('mobile')
|
||||
overlay:raise()
|
||||
|
||||
zoomInButton = modules.client_topmenu.addLeftButton('zoomInButton', 'Zoom In', '/images/topbuttons/zoomin', function() g_app.scaleUp() end)
|
||||
zoomOutButton = modules.client_topmenu.addLeftButton('zoomOutButton', 'Zoom Out', '/images/topbuttons/zoomout', function() g_app.scaleDown() end)
|
||||
scheduleEvent(function()
|
||||
g_app.scale(5.0)
|
||||
end, 10)
|
||||
|
||||
connect(overlay, {
|
||||
onMousePress = onMousePress,
|
||||
onMouseRelease = onMouseRelease,
|
||||
onTouchPress = onMousePress,
|
||||
onTouchRelease = onMouseRelease,
|
||||
onMouseMove = onMouseMove
|
||||
})
|
||||
end
|
||||
|
||||
function terminate()
|
||||
if not g_app.isMobile() then return end
|
||||
disconnect(overlay, {
|
||||
onMousePress = onMousePress,
|
||||
onMouseRelease = onMouseRelease,
|
||||
onTouchPress = onMousePress,
|
||||
onTouchRelease = onMouseRelease,
|
||||
onMouseMove = onMouseMove
|
||||
})
|
||||
zoomInButton:destroy()
|
||||
zoomOutButton:destroy()
|
||||
overlay:destroy()
|
||||
overlay = nil
|
||||
end
|
||||
|
||||
function hide()
|
||||
overlay:hide()
|
||||
end
|
||||
|
||||
function show()
|
||||
overlay:show()
|
||||
end
|
||||
|
||||
function onMouseMove(widget, pos, offset)
|
||||
|
||||
end
|
||||
|
||||
function onMousePress(widget, pos, button)
|
||||
overlay:raise()
|
||||
if button == 4 then -- touch
|
||||
overlay:raise()
|
||||
overlay.cursor:show()
|
||||
overlay.cursor:setPosition({x=pos.x - 32, y = pos.y - 32})
|
||||
touchStart = g_clock.millis()
|
||||
updateCursor()
|
||||
else
|
||||
overlay.cursor:hide()
|
||||
removeEvent(updateCursorEvent)
|
||||
end
|
||||
end
|
||||
|
||||
function onMouseRelease(widget, pos, button)
|
||||
overlay.cursor:hide()
|
||||
removeEvent(updateCursorEvent)
|
||||
end
|
||||
|
||||
function updateCursor()
|
||||
removeEvent(updateCursorEvent)
|
||||
local percent = 100 - math.max(0, math.min(100, (g_clock.millis() - touchStart) / 5)) -- 500 ms
|
||||
overlay.cursor:setPercent(percent)
|
||||
if percent > 0 then
|
||||
overlay.cursor:setOpacity(0.5)
|
||||
updateCursorEvent = scheduleEvent(updateCursor, 10)
|
||||
else
|
||||
overlay.cursor:setOpacity(0.8)
|
||||
end
|
||||
end
|
9
modules/client_mobile/mobile.otmod
Normal file
@ -0,0 +1,9 @@
|
||||
Module
|
||||
name: client_mobile
|
||||
description: Handles the mobile interface for smartphones
|
||||
author: otclient@otclient.ovh
|
||||
website: http://otclient.net
|
||||
sandboxed: true
|
||||
scripts: [ mobile ]
|
||||
@onLoad: init()
|
||||
@onUnload: terminate()
|
15
modules/client_mobile/mobile.otui
Normal file
@ -0,0 +1,15 @@
|
||||
UIWidget
|
||||
anchors.fill: parent
|
||||
focusable: false
|
||||
phantom: true
|
||||
|
||||
UIProgressRect
|
||||
id: cursor
|
||||
size: 64 64
|
||||
background: #FF5858
|
||||
percent: 100
|
||||
visible: false
|
||||
x: 0
|
||||
y: 0
|
||||
focusable: false
|
||||
phantom: true
|
@ -1,4 +1,4 @@
|
||||
Panel
|
||||
OptionPanel
|
||||
OptionCheckBox
|
||||
id: enableAudio
|
||||
!text: tr('Enable audio')
|
||||
@ -10,9 +10,6 @@ Panel
|
||||
Label
|
||||
id: musicSoundVolumeLabel
|
||||
!text: tr('Music volume: %d', 100)
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('musicSoundVolume')
|
||||
@ -20,9 +17,6 @@ Panel
|
||||
|
||||
OptionScrollbar
|
||||
id: musicSoundVolume
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
@ -30,9 +24,6 @@ Panel
|
||||
Label
|
||||
id: botSoundVolumeLabel
|
||||
!text: tr('Bot sound volume: %d', 100)
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('botSoundVolume')
|
||||
@ -40,9 +31,6 @@ Panel
|
||||
|
||||
OptionScrollbar
|
||||
id: botSoundVolume
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
|
@ -1,4 +1,4 @@
|
||||
Panel
|
||||
OptionPanel
|
||||
OptionCheckBox
|
||||
id: showInfoMessagesInConsole
|
||||
!text: tr('Show info messages in console')
|
||||
|
@ -1,8 +1,11 @@
|
||||
Panel
|
||||
OptionPanel
|
||||
OptionCheckBox
|
||||
id: classicControl
|
||||
!text: tr('Classic control')
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: autoChaseOverride
|
||||
!text: tr('Allow auto chase override')
|
||||
@ -15,6 +18,8 @@ Panel
|
||||
id: wsadWalking
|
||||
!text: tr('Enable WSAD walking')
|
||||
!tooltip: tr('Disable chat and allow walk using WSAD keys')
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: dash
|
||||
@ -27,9 +32,6 @@ Panel
|
||||
!tooltip: tr('Will detect when to use diagonal step based on the\nkeys you are pressing')
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
id: hotkeyDelayLabel
|
||||
margin-top: 10
|
||||
!tooltip: tr('Give you some time to make a turn while walking if you press many keys simultaneously')
|
||||
@ -39,114 +41,107 @@ Panel
|
||||
|
||||
OptionScrollbar
|
||||
id: hotkeyDelay
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
minimum: 5
|
||||
maximum: 50
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
id: walkFirstStepDelayLabel
|
||||
margin-top: 10
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('walkFirstStepDelay')
|
||||
self:setText(tr('Walk delay after first step: %s ms', value))
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionScrollbar
|
||||
id: walkFirstStepDelay
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
minimum: 50
|
||||
maximum: 300
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
id: walkTurnDelayLabel
|
||||
margin-top: 10
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('walkTurnDelay')
|
||||
self:setText(tr('Walk delay after turn: %s ms', value))
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionScrollbar
|
||||
id: walkTurnDelay
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 300
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
id: walkCtrlTurnDelayLabel
|
||||
margin-top: 10
|
||||
$mobile:
|
||||
visible: false
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('walkTurnDelay')
|
||||
self:setText(tr('Walk delay after ctrl turn: %s ms', value))
|
||||
|
||||
OptionScrollbar
|
||||
id: walkCtrlTurnDelay
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 300
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
id: walkStairsDelayLabel
|
||||
margin-top: 10
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('walkStairsDelay')
|
||||
self:setText(tr('Walk delay after floor change: %s ms', value))
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionScrollbar
|
||||
id: walkStairsDelay
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 300
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
id: walkTeleportDelayLabel
|
||||
margin-top: 10
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('walkTeleportDelay')
|
||||
self:setText(tr('Walk delay after teleport: %s ms', value))
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionScrollbar
|
||||
id: walkTeleportDelay
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 300
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
Button
|
||||
id: changeLocale
|
||||
!text: tr('Change language')
|
||||
@onClick: modules.client_locales.createWindow()
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
margin-top: 12
|
||||
width: 120
|
||||
Panel
|
||||
height: 30
|
||||
margin-top: 10
|
||||
|
||||
Button
|
||||
id: changeLocale
|
||||
!text: tr('Change language')
|
||||
@onClick: modules.client_locales.createWindow()
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: 150
|
||||
|
@ -1,36 +1,22 @@
|
||||
Panel
|
||||
OptionPanel
|
||||
Label
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
text-wrap: false
|
||||
@onSetup: |
|
||||
self:setText(tr("GPU: ") .. g_graphics.getRenderer())
|
||||
|
||||
Label
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
text-wrap: false
|
||||
@onSetup: |
|
||||
self:setText(tr("Version: ") .. g_graphics.getVersion())
|
||||
|
||||
HorizontalSeparator
|
||||
id: separator
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin: 5 5 5 5
|
||||
|
||||
OptionCheckBox
|
||||
id: vsync
|
||||
!text: tr('Enable vertical synchronization')
|
||||
!tooltip: tr('Limits FPS (usually to 60)')
|
||||
@onSetup: |
|
||||
if g_window.getPlatformType() == 'WIN32-EGL' then
|
||||
self:setEnabled(false)
|
||||
self:setText(tr('Enable vertical synchronization') .. " " .. tr('(OpenGL only)'))
|
||||
end
|
||||
|
||||
OptionCheckBox
|
||||
id: showFps
|
||||
@ -47,17 +33,11 @@ Panel
|
||||
|
||||
Label
|
||||
margin-top: 12
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
id: optimizationLevelLabel
|
||||
!text: tr("Optimization level")
|
||||
|
||||
ComboBox
|
||||
id: optimizationLevel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
margin-right: 2
|
||||
margin-left: 2
|
||||
@ -70,12 +50,13 @@ Panel
|
||||
self:addOption("High")
|
||||
self:addOption("Maximum")
|
||||
|
||||
Label
|
||||
!text: tr('High/Maximum optimization level may cause visual defects.')
|
||||
margin-top: 5
|
||||
|
||||
Label
|
||||
id: backgroundFrameRateLabel
|
||||
!text: tr('Game framerate limit: %s', 'max')
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 12
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('backgroundFrameRate')
|
||||
@ -87,18 +68,12 @@ Panel
|
||||
|
||||
OptionScrollbar
|
||||
id: backgroundFrameRate
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
minimum: 10
|
||||
maximum: 201
|
||||
|
||||
Label
|
||||
id: ambientLightLabel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('ambientLight')
|
||||
@ -106,9 +81,6 @@ Panel
|
||||
|
||||
OptionScrollbar
|
||||
id: ambientLight
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
@ -116,10 +88,9 @@ Panel
|
||||
Label
|
||||
id: tips
|
||||
margin-top: 20
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
text-auto-resize: true
|
||||
text-align: left
|
||||
text-wrap: true
|
||||
!text: tr("If you have FPS issues:\n- Use OpenGL version (_gl)\n- Disable vertical synchronization\n- Set higher optimization level\n- Lower screen resolution\nOr report it via email to otclient@otclient.ovh")
|
||||
!text: tr("If you have FPS issues:\n- Use OpenGL version (_gl)\n- Disable vertical synchronization\n- Set higher optimization level\n- Lower screen resolution\nOr report it on forum: http://otclient.net")
|
||||
$mobile:
|
||||
visible: false
|
@ -1,20 +1,18 @@
|
||||
Panel
|
||||
OptionPanel
|
||||
Label
|
||||
width: 130
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
id: layoutLabel
|
||||
!text: tr("Layout (change requries client restart)")
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
ComboBox
|
||||
id: layout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
margin-right: 2
|
||||
margin-left: 2
|
||||
$mobile:
|
||||
visible: false
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self:getCurrentOption().text)
|
||||
@onSetup: |
|
||||
self:addOption("Default")
|
||||
@ -28,11 +26,17 @@ Panel
|
||||
id: classicView
|
||||
!text: tr('Classic view')
|
||||
margin-top: 5
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: cacheMap
|
||||
!text: tr('Cache map (for non-classic view)')
|
||||
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: actionBar1
|
||||
!text: tr("Show first action bar")
|
||||
@ -57,6 +61,8 @@ Panel
|
||||
OptionCheckBox
|
||||
id: displayHealthOnTop
|
||||
!text: tr('Display creature health bars above texts')
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: hidePlayerBars
|
||||
@ -65,6 +71,8 @@ Panel
|
||||
OptionCheckBox
|
||||
id: displayMana
|
||||
!text: tr('Show player mana bar')
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: topHealtManaBar
|
||||
@ -73,93 +81,92 @@ Panel
|
||||
OptionCheckBox
|
||||
id: showHealthManaCircle
|
||||
!text: tr('Show health and mana circle')
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
OptionCheckBox
|
||||
id: highlightThingsUnderCursor
|
||||
!text: tr('Highlight things under cursor')
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
width: 90
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
id: leftPanelsLabel
|
||||
!text: tr("Left panels")
|
||||
|
||||
Label
|
||||
width: 90
|
||||
anchors.left: prev.right
|
||||
anchors.top: prev.top
|
||||
id: rightPanelsLabel
|
||||
!text: tr("Right panels")
|
||||
|
||||
Label
|
||||
width: 130
|
||||
anchors.left: prev.right
|
||||
anchors.top: prev.top
|
||||
id: backpackPanelLabel
|
||||
!text: tr("Container's panel")
|
||||
!tooltip: tr("Open new containers in selected panel")
|
||||
|
||||
ComboBox
|
||||
id: leftPanels
|
||||
anchors.left: leftPanelsLabel.left
|
||||
anchors.right: leftPanelsLabel.right
|
||||
anchors.top: leftPanelsLabel.bottom
|
||||
Panel
|
||||
height: 40
|
||||
margin-top: 3
|
||||
margin-right: 20
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
|
||||
@onSetup: |
|
||||
self:addOption("0")
|
||||
self:addOption("1")
|
||||
self:addOption("2")
|
||||
self:addOption("3")
|
||||
self:addOption("4")
|
||||
|
||||
Label
|
||||
width: 90
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
id: leftPanelsLabel
|
||||
!text: tr("Left panels")
|
||||
|
||||
ComboBox
|
||||
id: rightPanels
|
||||
anchors.left: rightPanelsLabel.left
|
||||
anchors.right: rightPanelsLabel.right
|
||||
anchors.top: rightPanelsLabel.bottom
|
||||
margin-top: 3
|
||||
margin-right: 20
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
|
||||
@onSetup: |
|
||||
self:addOption("1")
|
||||
self:addOption("2")
|
||||
self:addOption("3")
|
||||
self:addOption("4")
|
||||
Label
|
||||
width: 90
|
||||
anchors.left: prev.right
|
||||
anchors.top: prev.top
|
||||
id: rightPanelsLabel
|
||||
!text: tr("Right panels")
|
||||
|
||||
ComboBox
|
||||
id: containerPanel
|
||||
anchors.left: backpackPanelLabel.left
|
||||
anchors.right: backpackPanelLabel.right
|
||||
anchors.top: backpackPanelLabel.bottom
|
||||
margin-top: 3
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
|
||||
@onSetup: |
|
||||
self:addOption("1st left panel")
|
||||
self:addOption("2nd left panel")
|
||||
self:addOption("3rd left panel")
|
||||
self:addOption("4th left panel")
|
||||
self:addOption("1st right panel")
|
||||
self:addOption("2nd right panel")
|
||||
self:addOption("3rd right panel")
|
||||
self:addOption("4th right panel")
|
||||
Label
|
||||
width: 130
|
||||
anchors.left: prev.right
|
||||
anchors.top: prev.top
|
||||
id: backpackPanelLabel
|
||||
!text: tr("Container's panel")
|
||||
!tooltip: tr("Open new containers in selected panel")
|
||||
|
||||
ComboBox
|
||||
id: leftPanels
|
||||
anchors.left: leftPanelsLabel.left
|
||||
anchors.right: leftPanelsLabel.right
|
||||
anchors.top: leftPanelsLabel.bottom
|
||||
margin-top: 3
|
||||
margin-right: 20
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
|
||||
@onSetup: |
|
||||
self:addOption("0")
|
||||
self:addOption("1")
|
||||
self:addOption("2")
|
||||
self:addOption("3")
|
||||
self:addOption("4")
|
||||
|
||||
ComboBox
|
||||
id: rightPanels
|
||||
anchors.left: rightPanelsLabel.left
|
||||
anchors.right: rightPanelsLabel.right
|
||||
anchors.top: rightPanelsLabel.bottom
|
||||
margin-top: 3
|
||||
margin-right: 20
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
|
||||
@onSetup: |
|
||||
self:addOption("1")
|
||||
self:addOption("2")
|
||||
self:addOption("3")
|
||||
self:addOption("4")
|
||||
|
||||
ComboBox
|
||||
id: containerPanel
|
||||
anchors.left: backpackPanelLabel.left
|
||||
anchors.right: backpackPanelLabel.right
|
||||
anchors.top: backpackPanelLabel.bottom
|
||||
margin-top: 3
|
||||
@onOptionChange: modules.client_options.setOption(self:getId(), self.currentIndex)
|
||||
@onSetup: |
|
||||
self:addOption("1st left panel")
|
||||
self:addOption("2nd left panel")
|
||||
self:addOption("3rd left panel")
|
||||
self:addOption("4th left panel")
|
||||
self:addOption("1st right panel")
|
||||
self:addOption("2nd right panel")
|
||||
self:addOption("3rd right panel")
|
||||
self:addOption("4th right panel")
|
||||
|
||||
Label
|
||||
margin-top: 3
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
id: crosshairLabel
|
||||
!text: tr("Crosshair")
|
||||
|
||||
ComboBox
|
||||
id: crosshair
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
margin-right: 2
|
||||
margin-left: 2
|
||||
@ -171,9 +178,6 @@ Panel
|
||||
|
||||
Label
|
||||
id: floorFadingLabel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
@onSetup: |
|
||||
local value = modules.client_options.getOption('floorFading')
|
||||
@ -181,17 +185,11 @@ Panel
|
||||
|
||||
OptionScrollbar
|
||||
id: floorFading
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 3
|
||||
minimum: 0
|
||||
maximum: 2000
|
||||
|
||||
Label
|
||||
id: floorFadingLabel2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
!text: (tr('Floor fading doesn\'t work with enabled light'))
|
||||
|
@ -4,9 +4,9 @@ local defaultOptions = {
|
||||
showFps = true,
|
||||
showPing = true,
|
||||
fullscreen = false,
|
||||
classicView = true,
|
||||
classicView = not g_app.isMobile(),
|
||||
cacheMap = false,
|
||||
classicControl = true,
|
||||
classicControl = not g_app.isMobile(),
|
||||
smartWalk = false,
|
||||
dash = false,
|
||||
autoChaseOverride = true,
|
||||
@ -18,9 +18,9 @@ local defaultOptions = {
|
||||
showPrivateMessagesInConsole = true,
|
||||
showPrivateMessagesOnScreen = true,
|
||||
rightPanels = 1,
|
||||
leftPanels = 2,
|
||||
leftPanels = g_app.isMobile() and 1 or 2,
|
||||
containerPanel = 8,
|
||||
backgroundFrameRate = 100,
|
||||
backgroundFrameRate = 60,
|
||||
enableAudio = true,
|
||||
enableMusicSound = false,
|
||||
musicSoundVolume = 100,
|
||||
@ -42,10 +42,7 @@ local defaultOptions = {
|
||||
dontStretchShrink = false,
|
||||
turnDelay = 30,
|
||||
hotkeyDelay = 30,
|
||||
|
||||
ignoreServerDirection = true,
|
||||
realDirection = false,
|
||||
|
||||
|
||||
wsadWalking = false,
|
||||
walkFirstStepDelay = 200,
|
||||
walkTurnDelay = 100,
|
||||
@ -104,20 +101,23 @@ function init()
|
||||
audioPanel = g_ui.loadUI('audio')
|
||||
optionsTabBar:addTab(tr('Audio'), audioPanel, '/images/optionstab/audio')
|
||||
|
||||
extrasPanel = g_ui.createWidget('Panel')
|
||||
extrasPanel = g_ui.createWidget('OptionPanel')
|
||||
for _, v in ipairs(g_extras.getAll()) do
|
||||
local extrasButton = g_ui.createWidget('OptionCheckBox')
|
||||
extrasButton:setId(v)
|
||||
extrasButton:setText(g_extras.getDescription(v))
|
||||
extrasPanel:addChild(extrasButton)
|
||||
end
|
||||
if not g_game.getFeature(GameNoDebug) then
|
||||
if not g_game.getFeature(GameNoDebug) and not g_app.isMobile() then
|
||||
optionsTabBar:addTab(tr('Extras'), extrasPanel, '/images/optionstab/extras')
|
||||
end
|
||||
|
||||
optionsButton = modules.client_topmenu.addLeftButton('optionsButton', tr('Options'), '/images/topbuttons/options', toggle)
|
||||
audioButton = modules.client_topmenu.addLeftButton('audioButton', tr('Audio'), '/images/topbuttons/audio', function() toggleOption('enableAudio') end)
|
||||
|
||||
if g_app.isMobile() then
|
||||
audioButton:hide()
|
||||
end
|
||||
|
||||
addEvent(function() setup() end)
|
||||
|
||||
connect(g_game, { onGameStart = online,
|
||||
@ -316,10 +316,6 @@ function setOption(key, value, force)
|
||||
if modules.game_console and modules.game_console.consoleToggleChat:isChecked() ~= value then
|
||||
modules.game_console.consoleToggleChat:setChecked(value)
|
||||
end
|
||||
--elseif key == 'ignoreServerDirection' then
|
||||
-- g_game.ignoreServerDirection(value)
|
||||
--elseif key == 'realDirection' then
|
||||
-- g_game.showRealDirection(value)
|
||||
elseif key == 'hotkeyDelay' then
|
||||
generalPanel:getChildById('hotkeyDelayLabel'):setText(tr('Hotkey delay: %s ms', value))
|
||||
elseif key == 'walkFirstStepDelay' then
|
||||
|
@ -2,25 +2,23 @@ OptionCheckBox < CheckBox
|
||||
@onCheckChange: modules.client_options.setOption(self:getId(), self:isChecked())
|
||||
height: 16
|
||||
|
||||
$first:
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
|
||||
$!first:
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
|
||||
OptionScrollbar < HorizontalScrollBar
|
||||
step: 1
|
||||
@onValueChange: modules.client_options.setOption(self:getId(), self:getValue())
|
||||
|
||||
OptionPanel < Panel
|
||||
layout:
|
||||
type: verticalBox
|
||||
|
||||
MainWindow
|
||||
id: optionsWindow
|
||||
!text: tr('Options')
|
||||
size: 480 460
|
||||
size: 490 500
|
||||
$mobile:
|
||||
size: 490 360
|
||||
|
||||
@onEnter: modules.client_options.hide()
|
||||
@onEscape: modules.client_options.hide()
|
||||
|
@ -57,7 +57,7 @@ function terminate()
|
||||
removeEvent(monitorEvent)
|
||||
end
|
||||
|
||||
function onMiniWindowClose()
|
||||
function onClose()
|
||||
statsButton:setOn(false)
|
||||
end
|
||||
|
||||
@ -67,6 +67,8 @@ function toggle()
|
||||
statsButton:setOn(false)
|
||||
else
|
||||
statsWindow:show()
|
||||
statsWindow:raise()
|
||||
statsWindow:focus()
|
||||
statsButton:setOn(true)
|
||||
end
|
||||
end
|
||||
@ -173,7 +175,7 @@ function update()
|
||||
return
|
||||
end
|
||||
|
||||
statsWindow.debugPanel.sleepTime:setText("Sleep: " .. math.round(g_stats.getSleepTime() / math.max(1, g_clock.micros() - lastSleepTimeReset), 2) .. "%, Packets: " .. g_game.getRecivedPacketsCount() .. " , " .. (g_game.getRecivedPacketsSize() / 1024) .. " KB")
|
||||
statsWindow.debugPanel.sleepTime:setText("GFPS: " .. g_app.getGraphicsFps() .. " PFPS: " .. g_app.getProcessingFps() .. " Packets: " .. g_game.getRecivedPacketsCount() .. " , " .. (g_game.getRecivedPacketsSize() / 1024) .. " KB")
|
||||
statsWindow.debugPanel.luaRamUsage:setText("Ram usage by lua: " .. gcinfo() .. " kb")
|
||||
local adaptive = "Adaptive: " .. g_adaptiveRenderer.getLevel() .. " | " .. g_adaptiveRenderer.getDebugInfo()
|
||||
adaptiveRender:setText(adaptive)
|
||||
|
@ -24,6 +24,12 @@ MainWindow
|
||||
margin: 0 0 0 0
|
||||
padding: 25 3 3 3
|
||||
opacity: 0.9
|
||||
|
||||
@onEnter: modules.client_stats.toggle()
|
||||
@onEscape: modules.client_stats.toggle()
|
||||
|
||||
$mobile:
|
||||
size: 550 300
|
||||
|
||||
ScrollablePanel
|
||||
id: debugPanel
|
||||
@ -42,17 +48,6 @@ MainWindow
|
||||
id: luaRamUsage
|
||||
text: -
|
||||
|
||||
DebugLabel
|
||||
!text: tr('Render')
|
||||
|
||||
DebugText
|
||||
id: adaptiveRender
|
||||
text: -
|
||||
|
||||
DebugText
|
||||
id: render
|
||||
text: -
|
||||
|
||||
DebugText
|
||||
id: atlas
|
||||
text: -
|
||||
@ -71,6 +66,17 @@ MainWindow
|
||||
id: mainStats
|
||||
text: -
|
||||
|
||||
DebugLabel
|
||||
!text: tr('Render')
|
||||
|
||||
DebugText
|
||||
id: adaptiveRender
|
||||
text: -
|
||||
|
||||
DebugText
|
||||
id: render
|
||||
text: -
|
||||
|
||||
DebugLabel
|
||||
!text: tr('Dispatcher')
|
||||
|
||||
|
@ -34,7 +34,7 @@ function init()
|
||||
loaded_files = {}
|
||||
for _,file in pairs(files) do
|
||||
if g_resources.isFileType(file, 'otfont') then
|
||||
g_ui.importFont('/layouts/' .. layout .. '/fonts/' .. file)
|
||||
g_fonts.importFont('/layouts/' .. layout .. '/fonts/' .. file)
|
||||
loaded_files[file] = true
|
||||
end
|
||||
end
|
||||
@ -47,24 +47,6 @@ function init()
|
||||
end
|
||||
end
|
||||
|
||||
if layout:len() > 0 then
|
||||
files = g_resources.listDirectoryFiles('/layouts/' .. layout .. '/particles')
|
||||
loaded_files = {}
|
||||
for _,file in pairs(files) do
|
||||
if g_resources.isFileType(file, 'otps') then
|
||||
g_ui.importParticle('/layouts/' .. layout .. '/particles/' .. file)
|
||||
loaded_files[file] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
files = g_resources.listDirectoryFiles('/data/particles')
|
||||
for _,file in pairs(files) do
|
||||
if g_resources.isFileType(file, 'otps') and not loaded_files[file] then
|
||||
g_particles.importParticle('/data/particles/' .. file)
|
||||
end
|
||||
end
|
||||
|
||||
g_mouse.loadCursors('/data/cursors/cursors')
|
||||
if layout:len() > 0 and g_resources.directoryExists('/layouts/' .. layout .. '/cursors/cursors') then
|
||||
g_mouse.loadCursors('/layouts/' .. layout .. '/cursors/cursors')
|
||||
|
@ -313,6 +313,13 @@ function addLine(text, color)
|
||||
table.insert(cachedLines, {text=text, color=color})
|
||||
end
|
||||
|
||||
function terminalPrint(value)
|
||||
if type(value) == "table" then
|
||||
return print(json.encode(value, 2))
|
||||
end
|
||||
print(tostring(value))
|
||||
end
|
||||
|
||||
function executeCommand(command)
|
||||
if command == nil or #string.gsub(command, '\n', '') == 0 then return end
|
||||
|
||||
@ -337,7 +344,7 @@ function executeCommand(command)
|
||||
-- detect and convert commands with simple syntax
|
||||
local realCommand
|
||||
if string.sub(command, 1, 1) == '=' then
|
||||
realCommand = 'print(tostring(' .. string.sub(command,2) .. '))'
|
||||
realCommand = 'modules.client_terminal.terminalPrint(' .. string.sub(command,2) .. ')'
|
||||
else
|
||||
realCommand = command
|
||||
end
|
||||
|
@ -94,6 +94,7 @@ UIWindow
|
||||
border-width-left: 0
|
||||
border-width-top: 0
|
||||
multiline: false
|
||||
text-auto-submit: true
|
||||
|
||||
$on:
|
||||
border-width-left: 1
|
||||
|
@ -42,7 +42,7 @@ function show(text, options, callback) -- callback = function(newText)
|
||||
elseif type(text) == 'nil' then
|
||||
text = ''
|
||||
elseif type(text) ~= 'string' then
|
||||
return error("Invalid text type for game_textedit: " .. type(text))
|
||||
return error("Invalid text type for client_textedit: " .. type(text))
|
||||
end
|
||||
if type(options) == 'function' then
|
||||
local tmp = callback
|
||||
@ -113,20 +113,21 @@ function show(text, options, callback) -- callback = function(newText)
|
||||
window.text:setCursorPos(-1)
|
||||
end
|
||||
end
|
||||
if type(options.range) == 'table' or (type(options.validation) == 'string' and options.validation:len() > 0) then
|
||||
window.buttons.ok:disable()
|
||||
window.text.onTextChange = function(widget, text)
|
||||
if validate(text) then
|
||||
window.buttons.ok:enable()
|
||||
else
|
||||
window.buttons.ok:disable()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
window.text:setText(text)
|
||||
window.text:setCursorPos(-1)
|
||||
|
||||
window.text.onTextChange = function(widget, text)
|
||||
if validate(text) then
|
||||
window.buttons.ok:enable()
|
||||
if g_app.isMobile() then
|
||||
doneFunc()
|
||||
end
|
||||
else
|
||||
window.buttons.ok:disable()
|
||||
end
|
||||
end
|
||||
|
||||
if type(options.width) == 'number' then
|
||||
window:setWidth(options.width)
|
||||
end
|
||||
@ -134,6 +135,14 @@ function show(text, options, callback) -- callback = function(newText)
|
||||
activeWindow = window
|
||||
activeWindow:raise()
|
||||
activeWindow:focus()
|
||||
if g_app.isMobile() then
|
||||
window.text:focus()
|
||||
local flags = 0
|
||||
if options.multiline then
|
||||
flags = 1
|
||||
end
|
||||
g_window.showTextEditor(window:getText(), window.description:getText(), window.text:getText(), flags)
|
||||
end
|
||||
return activeWindow
|
||||
end
|
||||
|
@ -1,10 +1,9 @@
|
||||
Module
|
||||
name: game_textedit
|
||||
description: Allow to edit text
|
||||
name: client_textedit
|
||||
description: Shows window which allows to edit text
|
||||
author: OTClientV8
|
||||
website: https://github.com/OTCv8/otclientv8
|
||||
sandboxed: true
|
||||
dependencies: [ game_interface ]
|
||||
scripts: [ textedit ]
|
||||
@onLoad: init()
|
||||
@onUnload: terminate()
|
@ -27,7 +27,6 @@ TextEditWindow < MainWindow
|
||||
Label
|
||||
id: description
|
||||
text-align: center
|
||||
text: description
|
||||
margin-bottom: 5
|
||||
visible: false
|
||||
text-wrap: true
|
||||
@ -48,10 +47,14 @@ SinglelineTextEditWindow < TextEditWindow
|
||||
|
||||
MultilineTextEditWindow < TextEditWindow
|
||||
width: 600
|
||||
$mobile:
|
||||
width: 500
|
||||
|
||||
Panel
|
||||
id: textPanel
|
||||
height: 400
|
||||
$mobile:
|
||||
height: 300
|
||||
|
||||
MultilineTextEdit
|
||||
id: text
|
@ -1,316 +0,0 @@
|
||||
Updater = { }
|
||||
|
||||
Updater.maxRetries = 5
|
||||
|
||||
--[[
|
||||
HOW IT WORKS:
|
||||
1. init
|
||||
2. show
|
||||
3. generateChecksum and get checksums from url
|
||||
4. compareChecksums
|
||||
5. download files with different chekcums
|
||||
6. call c++ update function
|
||||
]]--
|
||||
|
||||
local filesUrl = ""
|
||||
|
||||
local updaterWindow = nil
|
||||
local initialPanel = nil
|
||||
local updatePanel = nil
|
||||
local progressBar = nil
|
||||
local updateProgressBar = nil
|
||||
local downloadStatusLabel = nil
|
||||
local downloadProgressBar = nil
|
||||
local downloadRetries = 0
|
||||
|
||||
local generateChecksumsEvent = nil
|
||||
local updateableFiles = nil
|
||||
local binaryChecksum = nil
|
||||
local binaryFile = ""
|
||||
local fileChecksums = {}
|
||||
local checksumIter = 0
|
||||
local downloadIter = 0
|
||||
local aborted = false
|
||||
local statusData = nil
|
||||
local thingsUpdate = {}
|
||||
local toUpdate = {}
|
||||
local thingsUpdateOptionalError = nil
|
||||
|
||||
local function onDownload(path, checksum, err)
|
||||
if aborted then
|
||||
return
|
||||
end
|
||||
|
||||
if err then
|
||||
if downloadRetries > Updater.maxRetries then
|
||||
return updateError("Can't download file: " .. path .. ".\nError: " .. err)
|
||||
else
|
||||
downloadRetries = downloadRetries + 1
|
||||
return downloadNextFile(true)
|
||||
end
|
||||
end
|
||||
if statusData["files"][path] == nil then
|
||||
return updateError("Invalid file path: " .. path)
|
||||
elseif statusData["files"][path] ~= checksum then
|
||||
return updateError("Invalid file checksum.\nFile: " .. path .. "\nShould be:\n" .. statusData["files"][path] .. "\nIs:\n" .. checksum)
|
||||
end
|
||||
downloadIter = downloadIter + 1
|
||||
updateProgressBar:setPercent(math.ceil((100 * downloadIter) / #toUpdate))
|
||||
downloadProgressBar:setPercent(100)
|
||||
downloadProgressBar:setText("")
|
||||
downloadNextFile(false)
|
||||
end
|
||||
|
||||
local function onDownloadProgress(progress, speed)
|
||||
downloadProgressBar:setPercent(progress)
|
||||
downloadProgressBar:setText(speed .. " kbps")
|
||||
end
|
||||
|
||||
local function gotStatus(data, err)
|
||||
if err then
|
||||
return updateError(err)
|
||||
end
|
||||
if data["error"] ~= nil and data["error"]:len() > 0 then
|
||||
return updateError(data["error"])
|
||||
end
|
||||
if data["url"] == nil or data["files"] == nil or data["binary"] == nil then
|
||||
return updateError("Invalid json data from server")
|
||||
end
|
||||
if data["things"] ~= nil then
|
||||
for file, checksum in pairs(data["things"]) do
|
||||
if #checksum > 1 then
|
||||
for thingtype, thingdata in pairs(thingsUpdate) do
|
||||
if string.match(file:lower(), thingdata[1]:lower()) then
|
||||
data["files"][file] = checksum
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
statusData = data
|
||||
if checksumIter == 100 then
|
||||
compareChecksums()
|
||||
end
|
||||
end
|
||||
|
||||
-- public functions
|
||||
function Updater.init()
|
||||
updaterWindow = g_ui.displayUI('updater')
|
||||
updaterWindow:hide()
|
||||
|
||||
initialPanel = updaterWindow:getChildById('initialPanel')
|
||||
updatePanel = updaterWindow:getChildById('updatePanel')
|
||||
progressBar = initialPanel:getChildById('progressBar')
|
||||
updateProgressBar = updatePanel:getChildById('updateProgressBar')
|
||||
downloadStatusLabel = updatePanel:getChildById('downloadStatusLabel')
|
||||
downloadProgressBar = updatePanel:getChildById('downloadProgressBar')
|
||||
updatePanel:hide()
|
||||
|
||||
scheduleEvent(Updater.show, 200)
|
||||
end
|
||||
|
||||
function Updater.terminate()
|
||||
updaterWindow:destroy()
|
||||
updaterWindow = nil
|
||||
|
||||
removeEvent(generateChecksumsEvent)
|
||||
end
|
||||
|
||||
local function clear()
|
||||
removeEvent(generateChecksumsEvent)
|
||||
|
||||
updateableFiles = nil
|
||||
binaryChecksum = nil
|
||||
binaryFile = ""
|
||||
fileChecksums = {}
|
||||
checksumIter = 0
|
||||
downloadIter = 0
|
||||
aborted = false
|
||||
statusData = nil
|
||||
toUpdate = {}
|
||||
progressBar:setPercent(0)
|
||||
updateProgressBar:setPercent(0)
|
||||
downloadProgressBar:setPercent(0)
|
||||
downloadProgressBar:setText("")
|
||||
end
|
||||
|
||||
function Updater.show()
|
||||
if not g_resources.isLoadedFromArchive() or Services.updater == nil or Services.updater:len() < 4 then
|
||||
return Updater.hide()
|
||||
end
|
||||
if updaterWindow:isVisible() then
|
||||
return
|
||||
end
|
||||
updaterWindow:show()
|
||||
updaterWindow:raise()
|
||||
updaterWindow:focus()
|
||||
if EnterGame then
|
||||
EnterGame.hide()
|
||||
end
|
||||
|
||||
clear()
|
||||
|
||||
updateableFiles = g_resources.listUpdateableFiles()
|
||||
if #updateableFiles < 1 then
|
||||
return updateError("Can't get list of files")
|
||||
end
|
||||
binaryChecksum = g_resources.selfChecksum():lower()
|
||||
if binaryChecksum:len() ~= 32 then
|
||||
return updateError("Invalid binary checksum: " .. binaryChecksum)
|
||||
end
|
||||
|
||||
local data = {
|
||||
version = APP_VERSION,
|
||||
platform = g_window.getPlatformType(),
|
||||
uid = G.UUID,
|
||||
build_version = g_app.getVersion(),
|
||||
build_revision = g_app.getBuildRevision(),
|
||||
build_commit = g_app.getBuildCommit(),
|
||||
build_date = g_app.getBuildDate(),
|
||||
os = g_app.getOs(),
|
||||
os_name = g_platform.getOSName()
|
||||
}
|
||||
HTTP.postJSON(Services.updater, data, gotStatus)
|
||||
if generateChecksumsEvent == nil then
|
||||
generateChecksumsEvent = scheduleEvent(generateChecksum, 5)
|
||||
end
|
||||
end
|
||||
|
||||
function Updater.isVisible()
|
||||
return updaterWindow:isVisible()
|
||||
end
|
||||
|
||||
function Updater.updateThings(things, optionalError)
|
||||
thingsUpdate = things
|
||||
thingsUpdateOptionalError = optionalError
|
||||
Updater:show()
|
||||
end
|
||||
|
||||
function Updater.hide()
|
||||
updaterWindow:hide()
|
||||
if thingsUpdateOptionalError then
|
||||
local msgbox = displayErrorBox("Updater error", thingsUpdateOptionalError:trim())
|
||||
msgbox.onOk = function() if EnterGame then EnterGame.show() end end
|
||||
thingsUpdateOptionalError = nil
|
||||
elseif EnterGame then
|
||||
EnterGame.show()
|
||||
end
|
||||
end
|
||||
|
||||
function Updater.abort()
|
||||
aborted = true
|
||||
Updater:hide()
|
||||
end
|
||||
|
||||
function generateChecksum()
|
||||
local entries = #updateableFiles
|
||||
local fromEntry = math.floor((checksumIter) * (entries / 100))
|
||||
local toEntry = math.floor((checksumIter + 1) * (entries / 100))
|
||||
if checksumIter == 99 then
|
||||
toEntry = #updateableFiles
|
||||
end
|
||||
for i=fromEntry+1,toEntry do
|
||||
local fileName = updateableFiles[i]
|
||||
fileChecksums[fileName] = g_resources.fileChecksum(fileName):lower()
|
||||
end
|
||||
|
||||
checksumIter = checksumIter + 1
|
||||
if checksumIter == 100 then
|
||||
generateChecksumsEvent = nil
|
||||
gotChecksums()
|
||||
else
|
||||
progressBar:setPercent(math.ceil(checksumIter * 0.95))
|
||||
generateChecksumsEvent = scheduleEvent(generateChecksum, 5)
|
||||
end
|
||||
end
|
||||
|
||||
function gotChecksums()
|
||||
if statusData ~= nil then
|
||||
compareChecksums()
|
||||
end
|
||||
end
|
||||
|
||||
function compareChecksums()
|
||||
for file, checksum in pairs(statusData["files"]) do
|
||||
checksum = checksum:lower()
|
||||
if file == statusData["binary"] then
|
||||
if binaryChecksum ~= checksum then
|
||||
binaryFile = file
|
||||
table.insert(toUpdate, binaryFile)
|
||||
end
|
||||
else
|
||||
local localChecksum = fileChecksums[file]
|
||||
if localChecksum ~= checksum then
|
||||
table.insert(toUpdate, file)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #toUpdate == 0 then
|
||||
return upToDate()
|
||||
end
|
||||
-- outdated
|
||||
filesUrl = statusData["url"]
|
||||
initialPanel:hide()
|
||||
updatePanel:show()
|
||||
updatePanel:getChildById('updateStatusLabel'):setText(tr("Updating %i files", #toUpdate))
|
||||
updaterWindow:setHeight(190)
|
||||
downloadNextFile(false)
|
||||
end
|
||||
|
||||
function upToDate()
|
||||
Updater.hide()
|
||||
end
|
||||
|
||||
function updateError(err)
|
||||
Updater.hide()
|
||||
local msgbox = displayErrorBox("Updater error", err)
|
||||
msgbox.onOk = function() if EnterGame then EnterGame.show() end end
|
||||
end
|
||||
|
||||
function urlencode(url)
|
||||
url = url:gsub("\n", "\r\n")
|
||||
url = url:gsub("([^%w ])", function(c) string.format("%%%02X", string.byte(c)) end)
|
||||
url = url:gsub(" ", "+")
|
||||
return url
|
||||
end
|
||||
|
||||
function downloadNextFile(retry)
|
||||
if aborted then
|
||||
return
|
||||
end
|
||||
|
||||
updaterWindow:show()
|
||||
updaterWindow:raise()
|
||||
updaterWindow:focus()
|
||||
|
||||
if downloadIter == #toUpdate then
|
||||
return downloadingFinished()
|
||||
end
|
||||
|
||||
if retry then
|
||||
retry = " (" .. downloadRetries .. " retry)"
|
||||
else
|
||||
retry = ""
|
||||
end
|
||||
|
||||
local file = toUpdate[downloadIter + 1]
|
||||
downloadStatusLabel:setText(tr("Downloading %i of %i%s:\n%s", downloadIter + 1, #toUpdate, retry, file))
|
||||
downloadProgressBar:setPercent(0)
|
||||
downloadProgressBar:setText("")
|
||||
HTTP.download(filesUrl .. urlencode(file), file, onDownload, onDownloadProgress)
|
||||
end
|
||||
|
||||
function downloadingFinished()
|
||||
thingsUpdateOptionalError = nil
|
||||
UIMessageBox.display(tr("Success"), tr("Download complate.\nUpdating client..."), {}, nil, nil)
|
||||
scheduleEvent(function()
|
||||
local files = {}
|
||||
for file, checksum in pairs(statusData["files"]) do
|
||||
table.insert(files, file)
|
||||
end
|
||||
g_settings.save()
|
||||
g_resources.updateClient(files, binaryFile)
|
||||
g_app.quick_exit()
|
||||
end, 1000)
|
||||
end
|
@ -1,75 +0,0 @@
|
||||
StaticMainWindow
|
||||
id: updaterWindow
|
||||
!text: tr('Updater')
|
||||
height: 125
|
||||
width: 300
|
||||
|
||||
Panel
|
||||
id: initialPanel
|
||||
layout:
|
||||
type: verticalBox
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
margin: 0 5 5 5
|
||||
|
||||
Label
|
||||
id: statusLabel
|
||||
!text: tr('Checking for updates')
|
||||
text-align: center
|
||||
|
||||
ProgressBar
|
||||
id: progressBar
|
||||
height: 15
|
||||
background-color: #4444ff
|
||||
margin-bottom: 10
|
||||
margin-top: 10
|
||||
|
||||
Button
|
||||
!text: tr('Cancel')
|
||||
margin-left: 70
|
||||
margin-right: 70
|
||||
@onClick: Updater.abort()
|
||||
|
||||
Panel
|
||||
id: updatePanel
|
||||
layout:
|
||||
type: verticalBox
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
margin: 0 5 5 5
|
||||
|
||||
Label
|
||||
id: updateStatusLabel
|
||||
!text: tr('Updating')
|
||||
text-align: center
|
||||
|
||||
ProgressBar
|
||||
id: updateProgressBar
|
||||
height: 15
|
||||
background-color: #4444ff
|
||||
margin-bottom: 10
|
||||
margin-top: 10
|
||||
|
||||
Label
|
||||
id: downloadStatusLabel
|
||||
!text: tr('Downloading:')
|
||||
text-align: center
|
||||
margin-top: 5
|
||||
height: 25
|
||||
|
||||
ProgressBar
|
||||
id: downloadProgressBar
|
||||
height: 15
|
||||
background-color: #4444ff
|
||||
margin-bottom: 10
|
||||
margin-top: 10
|
||||
|
||||
Button
|
||||
!text: tr('Cancel')
|
||||
margin-left: 70
|
||||
margin-right: 70
|
||||
@onClick: Updater.abort()
|
176
modules/corelib/base64.lua
Normal file
@ -0,0 +1,176 @@
|
||||
--[[
|
||||
|
||||
base64 -- v1.5.1 public domain Lua base64 encoder/decoder
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
Needs bit32.extract function. If not present it's implemented using BitOp
|
||||
or Lua 5.3 native bit operators. For Lua 5.1 fallbacks to pure Lua
|
||||
implementation inspired by Rici Lake's post:
|
||||
http://ricilake.blogspot.co.uk/2007/10/iterating-bits-in-lua.html
|
||||
|
||||
author: Ilya Kolbin (iskolbin@gmail.com)
|
||||
url: github.com/iskolbin/lbase64
|
||||
|
||||
COMPATIBILITY
|
||||
|
||||
Lua 5.1, 5.2, 5.3, LuaJIT
|
||||
|
||||
LICENSE
|
||||
|
||||
See end of file for license information.
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
base64 = {}
|
||||
|
||||
local extract = _G.bit32 and _G.bit32.extract
|
||||
if not extract then
|
||||
if _G.bit then
|
||||
local shl, shr, band = _G.bit.lshift, _G.bit.rshift, _G.bit.band
|
||||
extract = function( v, from, width )
|
||||
return band( shr( v, from ), shl( 1, width ) - 1 )
|
||||
end
|
||||
elseif _G._VERSION >= "Lua 5.3" then
|
||||
extract = load[[return function( v, from, width )
|
||||
return ( v >> from ) & ((1 << width) - 1)
|
||||
end]]()
|
||||
else
|
||||
extract = function( v, from, width )
|
||||
local w = 0
|
||||
local flag = 2^from
|
||||
for i = 0, width-1 do
|
||||
local flag2 = flag + flag
|
||||
if v % flag2 >= flag then
|
||||
w = w + 2^i
|
||||
end
|
||||
flag = flag2
|
||||
end
|
||||
return w
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function base64.makeencoder( s62, s63, spad )
|
||||
local encoder = {}
|
||||
for b64code, char in pairs{[0]='A','B','C','D','E','F','G','H','I','J',
|
||||
'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y',
|
||||
'Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n',
|
||||
'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2',
|
||||
'3','4','5','6','7','8','9',s62 or '+',s63 or'/',spad or'='} do
|
||||
encoder[b64code] = char:byte()
|
||||
end
|
||||
return encoder
|
||||
end
|
||||
|
||||
function base64.makedecoder( s62, s63, spad )
|
||||
local decoder = {}
|
||||
for b64code, charcode in pairs( base64.makeencoder( s62, s63, spad )) do
|
||||
decoder[charcode] = b64code
|
||||
end
|
||||
return decoder
|
||||
end
|
||||
|
||||
local DEFAULT_ENCODER = base64.makeencoder()
|
||||
local DEFAULT_DECODER = base64.makedecoder()
|
||||
|
||||
local char, concat = string.char, table.concat
|
||||
|
||||
function base64.encode( str, encoder, usecaching )
|
||||
encoder = encoder or DEFAULT_ENCODER
|
||||
local t, k, n = {}, 1, #str
|
||||
local lastn = n % 3
|
||||
local cache = {}
|
||||
for i = 1, n-lastn, 3 do
|
||||
local a, b, c = str:byte( i, i+2 )
|
||||
local v = a*0x10000 + b*0x100 + c
|
||||
local s
|
||||
if usecaching then
|
||||
s = cache[v]
|
||||
if not s then
|
||||
s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)])
|
||||
cache[v] = s
|
||||
end
|
||||
else
|
||||
s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)])
|
||||
end
|
||||
t[k] = s
|
||||
k = k + 1
|
||||
end
|
||||
if lastn == 2 then
|
||||
local a, b = str:byte( n-1, n )
|
||||
local v = a*0x10000 + b*0x100
|
||||
t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[64])
|
||||
elseif lastn == 1 then
|
||||
local v = str:byte( n )*0x10000
|
||||
t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[64], encoder[64])
|
||||
end
|
||||
return concat( t )
|
||||
end
|
||||
|
||||
function base64.decode( b64, decoder, usecaching )
|
||||
decoder = decoder or DEFAULT_DECODER
|
||||
local pattern = '[^%w%+%/%=]'
|
||||
if decoder then
|
||||
local s62, s63
|
||||
for charcode, b64code in pairs( decoder ) do
|
||||
if b64code == 62 then s62 = charcode
|
||||
elseif b64code == 63 then s63 = charcode
|
||||
end
|
||||
end
|
||||
pattern = ('[^%%w%%%s%%%s%%=]'):format( char(s62), char(s63) )
|
||||
end
|
||||
b64 = b64:gsub( pattern, '' )
|
||||
local cache = usecaching and {}
|
||||
local t, k = {}, 1
|
||||
local n = #b64
|
||||
local padding = b64:sub(-2) == '==' and 2 or b64:sub(-1) == '=' and 1 or 0
|
||||
for i = 1, padding > 0 and n-4 or n, 4 do
|
||||
local a, b, c, d = b64:byte( i, i+3 )
|
||||
local s
|
||||
if usecaching then
|
||||
local v0 = a*0x1000000 + b*0x10000 + c*0x100 + d
|
||||
s = cache[v0]
|
||||
if not s then
|
||||
local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d]
|
||||
s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8))
|
||||
cache[v0] = s
|
||||
end
|
||||
else
|
||||
local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d]
|
||||
s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8))
|
||||
end
|
||||
t[k] = s
|
||||
k = k + 1
|
||||
end
|
||||
if padding == 1 then
|
||||
local a, b, c = b64:byte( n-3, n-1 )
|
||||
local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40
|
||||
t[k] = char( extract(v,16,8), extract(v,8,8))
|
||||
elseif padding == 2 then
|
||||
local a, b = b64:byte( n-3, n-2 )
|
||||
local v = decoder[a]*0x40000 + decoder[b]*0x1000
|
||||
t[k] = char( extract(v,16,8))
|
||||
end
|
||||
return concat( t )
|
||||
end
|
||||
|
||||
--[[
|
||||
Copyright (c) 2018 Ilya Kolbin
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
--]]
|
@ -28,6 +28,7 @@ Module
|
||||
dofile 'outputmessage'
|
||||
dofile 'orderedtable'
|
||||
|
||||
dofile 'base64'
|
||||
dofile 'json'
|
||||
dofile 'http'
|
||||
|
@ -108,6 +108,7 @@ function HTTP.cancel(operationId)
|
||||
if not g_http or not g_http.cancel then
|
||||
return
|
||||
end
|
||||
HTTP.operations[operationId] = nil
|
||||
return g_http.cancel(operationId)
|
||||
end
|
||||
|
||||
|
@ -249,7 +249,7 @@ function table.encodeStringPairList(t)
|
||||
|
||||
function table.decodeStringPairList(l)
|
||||
local ret = {}
|
||||
local r = regexMatch(l, "^([^:^\n]{1,20}):?(.*)$")
|
||||
local r = regexMatch(l, "(?:^|\\n)([^:^\n]{1,20}):?(.*)(?:$|\\n)")
|
||||
local multiline = ""
|
||||
local multilineKey = ""
|
||||
local multilineActive = false
|
||||
|
@ -46,11 +46,11 @@ function UIPopupMenu:onGeometryChange(oldRect, newRect)
|
||||
local ymax = parent:getY() + parent:getHeight()
|
||||
local xmax = parent:getX() + parent:getWidth()
|
||||
if newRect.y + newRect.height > ymax then
|
||||
local newy = newRect.y - newRect.height
|
||||
local newy = ymax - newRect.height
|
||||
if newy > 0 and newy + newRect.height < ymax then self:setY(newy) end
|
||||
end
|
||||
if newRect.x + newRect.width > xmax then
|
||||
local newx = newRect.x - newRect.width
|
||||
local newx = xmax - newRect.width
|
||||
if newx > 0 and newx + newRect.width < xmax then self:setX(newx) end
|
||||
end
|
||||
self:bindRectToParent()
|
||||
|
@ -29,6 +29,9 @@ local function calcValues(self)
|
||||
end
|
||||
|
||||
local px = math.max(proportion * pxrange, 6)
|
||||
if g_app.isMobile() then
|
||||
px = math.max(proportion * pxrange, 24)
|
||||
end
|
||||
px = px - px % 2 + 1
|
||||
|
||||
local offset = 0
|
||||
|
@ -38,7 +38,7 @@ function exit()
|
||||
end
|
||||
|
||||
function quit()
|
||||
g_app.quit()
|
||||
g_app.exit()
|
||||
end
|
||||
|
||||
function connect(object, arg1, arg2, arg3)
|
||||
|
22
modules/crash_reporter/crash_reporter.lua
Normal file
@ -0,0 +1,22 @@
|
||||
local CRASH_FILE = "exception.dmp"
|
||||
|
||||
function init()
|
||||
if g_resources.fileExists(CRASH_FILE) then
|
||||
local crashLog = g_resources.readFileContents(CRASH_FILE)
|
||||
local clientLog = g_logger.getLastLog()
|
||||
HTTP.post(Services.crash, {
|
||||
version = APP_VERSION,
|
||||
build = g_app.getVersion(),
|
||||
os = g_app.getOs(),
|
||||
platform = g_window.getPlatformType(),
|
||||
crash = base64.encode(crashLog),
|
||||
log = base64.encode(clientLog)
|
||||
}, function(data, err)
|
||||
if err then
|
||||
return g_logger.error("Error while reporting crash report: " .. err)
|
||||
end
|
||||
g_resources.deleteFile(CRASH_FILE)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
8
modules/crash_reporter/crash_reporter.otmod
Normal file
@ -0,0 +1,8 @@
|
||||
Module
|
||||
name: crash_reporter
|
||||
description: Sends crash log to remote server
|
||||
author: otclient@otclient.ovh
|
||||
website: otclient.ovh
|
||||
reloadable: false
|
||||
scripts: [ crash_reporter ]
|
||||
@onLoad: init()
|
@ -24,7 +24,7 @@ ActionColors = {
|
||||
}
|
||||
|
||||
function init()
|
||||
local bottomPanel = modules.game_interface.getBottomPanel()
|
||||
local bottomPanel = modules.game_interface.getActionPanel()
|
||||
actionPanel1 = g_ui.loadUI('actionbar', bottomPanel)
|
||||
bottomPanel:moveChildToIndex(actionPanel1, 1)
|
||||
actionPanel2 = g_ui.loadUI('actionbar', bottomPanel)
|
||||
@ -167,6 +167,7 @@ function setupAction(action)
|
||||
action.text:setText(config.text)
|
||||
action:setBorderColor(ActionColors.text)
|
||||
action.item:setOn(true) -- removes background
|
||||
action.item:setItemId(0)
|
||||
if Spells then
|
||||
local spell, profile = Spells.getSpellByWords(config.text:lower())
|
||||
action.spell = spell
|
||||
@ -225,7 +226,7 @@ end
|
||||
|
||||
function updateAction(action, newConfig)
|
||||
local config = action.config
|
||||
if newConfig.hotkey and type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
|
||||
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
|
||||
local gameRootPanel = modules.game_interface.getRootPanel()
|
||||
g_keyboard.unbindKeyPress(config.hotkey, action.callback, gameRootPanel)
|
||||
end
|
||||
@ -236,7 +237,7 @@ function updateAction(action, newConfig)
|
||||
end
|
||||
|
||||
function actionOnMouseRelease(action, mousePosition, mouseButton)
|
||||
if mouseButton == MouseRightButton then
|
||||
if mouseButton == MouseRightButton or not action.item:isOn() then
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
if action.item:getItemId() > 0 then
|
||||
@ -256,7 +257,7 @@ function actionOnMouseRelease(action, mousePosition, mouseButton)
|
||||
end
|
||||
menu:addSeparator()
|
||||
menu:addOption(tr('Set text'), function()
|
||||
modules.game_textedit.singlelineEditor(action.config.text or "", function(newText)
|
||||
modules.client_textedit.singlelineEditor(action.config.text or "", function(newText)
|
||||
updateAction(action, {text=newText, item=0})
|
||||
end)
|
||||
end)
|
||||
@ -362,7 +363,31 @@ function executeAction(action, ticks)
|
||||
local actionType = action.config.actionType
|
||||
|
||||
if type(action.config.text) == 'string' and action.config.text:len() > 0 then
|
||||
modules.game_console.sendMessage(action.config.text)
|
||||
if g_app.isMobile() then -- turn to direction of targer
|
||||
local target = g_game.getAttackingCreature()
|
||||
if target then
|
||||
local pos = g_game.getLocalPlayer():getPosition()
|
||||
local tpos = target:getPosition()
|
||||
if pos and tpos then
|
||||
local offx = tpos.x - pos.x
|
||||
local offy = tpos.y - pos.y
|
||||
if offy < 0 and offx <= 0 and math.abs(offx) < math.abs(offy) then
|
||||
g_game.turn(Directions.North)
|
||||
elseif offy > 0 and offx >= 0 and math.abs(offx) < math.abs(offy) then
|
||||
g_game.turn(Directions.South)
|
||||
elseif offx < 0 and offy <= 0 and math.abs(offx) > math.abs(offy) then
|
||||
g_game.turn(Directions.West)
|
||||
elseif offx > 0 and offy >= 0 and math.abs(offx) > math.abs(offy) then
|
||||
g_game.turn(Directions.East)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if modules.game_interface.isChatVisible() then
|
||||
modules.game_console.sendMessage(action.config.text)
|
||||
else
|
||||
g_game.talk(action.config.text)
|
||||
end
|
||||
action.actionDelayTo = g_clock.millis() + actionDelay
|
||||
elseif action.item:getItemId() > 0 then
|
||||
if actionType == ActionTypes.USE then
|
||||
|
@ -55,17 +55,11 @@ ActionButton < Panel
|
||||
|
||||
Panel
|
||||
id: actionBar
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
image-source: /images/ui/panel_map
|
||||
focusable: false
|
||||
image-source: /images/ui/panel_side
|
||||
image-border: 4
|
||||
margin-top: -1
|
||||
|
||||
$first:
|
||||
anchors.top: parent.top
|
||||
|
||||
$!first:
|
||||
anchors.top: prev.bottom
|
||||
|
||||
$on:
|
||||
height: 40
|
||||
visible: true
|
||||
@ -78,9 +72,12 @@ Panel
|
||||
id: prevButton
|
||||
icon: /images/game/console/leftarrow
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
margin-left: 1
|
||||
|
||||
margin-top: 1
|
||||
margin-bottom: 2
|
||||
|
||||
Panel
|
||||
id: tabBar
|
||||
anchors.top: parent.top
|
||||
@ -89,15 +86,17 @@ Panel
|
||||
anchors.right: next.left
|
||||
margin-right: 3
|
||||
margin-top: 2
|
||||
clipping: true
|
||||
|
||||
clipping: true
|
||||
|
||||
TabButton
|
||||
id: nextButton
|
||||
icon: /images/game/console/rightarrow
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
margin-right: 1
|
||||
margin-top: 1
|
||||
margin-bottom: 2
|
||||
|
||||
|
||||
ActionAssignWindow < MainWindow
|
||||
|
@ -1,3 +1,2 @@
|
||||
BattleButton < CreatureButton
|
||||
&isBattleButton: true
|
||||
optimized: true
|
@ -1,70 +0,0 @@
|
||||
TargetBot.Creature.attack = function(params, targets, isLooting) -- params {config, creature, danger, priority}
|
||||
if player:isWalking() then
|
||||
lastWalk = now
|
||||
end
|
||||
|
||||
local config = params.config
|
||||
local creature = params.creature
|
||||
|
||||
if g_game.getAttackingCreature() ~= creature then
|
||||
g_game.attack(creature)
|
||||
end
|
||||
|
||||
if not isLooting then -- walk only when not looting
|
||||
TargetBot.Creature.walk(creature, config, targets)
|
||||
end
|
||||
|
||||
-- attacks
|
||||
local mana = player:getMana()
|
||||
if config.useGroupAttack and config.groupAttackSpell:len() > 1 and mana > config.minManaGroup then
|
||||
local creatures = g_map.getSpectatorsInRange(player:getPosition(), false, config.groupAttackRadius, config.groupAttackRadius)
|
||||
local playersAround = false
|
||||
local monsters = 0
|
||||
for _, creature in ipairs(creatures) do
|
||||
if not creature:isLocalPlayer() and creature:isPlayer() then
|
||||
playersAround = true
|
||||
elseif creature:isMonster() then
|
||||
monsters = monsters + 1
|
||||
end
|
||||
end
|
||||
if monsters >= config.groupAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then
|
||||
if TargetBot.sayAttackSpell(config.groupAttackSpell, config.groupAttackDelay) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
if config.useSpellAttack and config.attackSpell:len() > 1 and mana > config.minMana then
|
||||
if TargetBot.sayAttackSpell(config.attackSpell, config.attackSpellDelay) then
|
||||
return
|
||||
end
|
||||
end
|
||||
if config.useRuneAttack and config.attackRune > 100 then
|
||||
if TargetBot.useAttackItem(config.attackRune, 0, creature, config.attackRuneDelay) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TargetBot.Creature.walk = function(creature, config, targets)
|
||||
-- luring
|
||||
if config.lure and not (config.chase and creature:getHealthPercent() < 30) then
|
||||
local monsters = 0
|
||||
if targets < config.lureCount then
|
||||
local path = findPath(player:getPosition(), creature:getPosition(), 5, {ignoreNonPathable=true, precision=2})
|
||||
if path then
|
||||
return TargetBot.walkTo(creature:getPosition(), 10, {marginMin=5, marginMax=6, ignoreNonPathable=true})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local currentDistance = findPath(player:getPosition(), creature:getPosition(), 10, {ignoreCreatures=true, ignoreNonPathable=true, ignoreCost=true})
|
||||
if config.chase and (creature:getHealthPercent() < 30 or not config.keepDistance) then
|
||||
if #currentDistance > 1 then
|
||||
return TargetBot.walkTo(creature:getPosition(), 10, {ignoreNonPathable=true, precision=1})
|
||||
end
|
||||
elseif config.keepDistance then
|
||||
if #currentDistance ~= config.keepDistanceRange and #currentDistance ~= config.keepDistanceRange + 1 then
|
||||
return TargetBot.walkTo(creature:getPosition(), 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1})
|
||||
end
|
||||
end
|
||||
end
|
@ -21,7 +21,7 @@ end
|
||||
local actionRetries = 0
|
||||
local prevActionResult = true
|
||||
cavebotMacro = macro(20, function()
|
||||
if TargetBot and TargetBot.isActive() then
|
||||
if TargetBot and TargetBot.isActive() and not TargetBot.isCaveBotActionAllowed() then
|
||||
return -- target bot or looting is working, wait
|
||||
end
|
||||
|