This commit is contained in:
OTCv8 2020-06-09 18:19:20 +02:00
parent 76d6f2ce7d
commit 541e189d3f
154 changed files with 2540 additions and 1221 deletions

View File

@ -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.

View 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";
?>

View File

@ -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";
?>

View File

@ -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
View 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));
?>

Binary file not shown.

BIN
d3dcompiler_47.dll Normal file

Binary file not shown.

View File

@ -4,4 +4,3 @@ Font
height: 8
glyph-size: 8 8
space-width: 2
default: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "otcicon.ico"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 720 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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

View File

@ -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

View File

@ -1,5 +0,0 @@
Background < Panel
image-source: /images/background
image-smooth: true
image-fixed-ratio: true
margin-top: 1

View File

@ -183,3 +183,4 @@ ConsolePanel < Panel
margin-bottom: 6
shift-navigation: true
max-length: 255
text-auto-submit: true

View File

@ -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

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -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
View File

@ -0,0 +1 @@
Min. height for mobile is 360, don't make windows bigger than that

View 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

View 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

View 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

View 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

View 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()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 814 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 822 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1009 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 962 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 952 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 571 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 871 B

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -216,3 +216,4 @@ ConsolePanel < Panel
margin-bottom: 2
shift-navigation: true
max-length: 255
text-auto-submit: true

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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>();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -5,6 +5,5 @@ Module
website: https://github.com/edubart/otclient
sandboxed: true
scripts: [ background ]
dependencies: [ client_topmenu ]
@onLoad: init()
@onUnload: terminate()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -6,4 +6,7 @@ Module
scripts: [ entergame, characterlist ]
@onLoad: EnterGame.init() CharacterList.init()
@onUnload: EnterGame.terminate() CharacterList.terminate()
load-later:
- game_things
- game_features

View File

@ -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

View 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

View 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()

View 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

View File

@ -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

View File

@ -1,4 +1,4 @@
Panel
OptionPanel
OptionCheckBox
id: showInfoMessagesInConsole
!text: tr('Show info messages in console')

View File

@ -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

View File

@ -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

View File

@ -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'))

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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')

View File

@ -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')

View File

@ -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

View File

@ -94,6 +94,7 @@ UIWindow
border-width-left: 0
border-width-top: 0
multiline: false
text-auto-submit: true
$on:
border-width-left: 1

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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
View 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.
--]]

View File

@ -28,6 +28,7 @@ Module
dofile 'outputmessage'
dofile 'orderedtable'
dofile 'base64'
dofile 'json'
dofile 'http'

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -38,7 +38,7 @@ function exit()
end
function quit()
g_app.quit()
g_app.exit()
end
function connect(object, arg1, arg2, arg3)

View 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

View 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()

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,2 @@
BattleButton < CreatureButton
&isBattleButton: true
optimized: true

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More