diff --git a/README.md b/README.md index 06ad816..aa9ce29 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,22 @@ # OTClientV8 -Tibia client designed for versions 7.40 - 11.00. -It's based on https://github.com/edubart/otclient and it's not backward compatible. +OTClientV8 is highly optimized tile based 2d game engine built with c++, lua, physfs, OpenGL ES 2.0 and OpenAL. +It works well even on 12 years old computers. In 2020 it has been used by more than 90k unique players. +Supported platforms: +- Windows (min. Windows 7) +- Linux +- Android (min. 5.0) +Planned support: +- Mac OS +- iOS +- WebAssembly + +On this GitHub you can find free version of OTClientV8. It comes without c++ sources, there are prebuilt executables instead. +In many cases, you won't need access to sources, you can add a lot of custom features in lua. +If you're interested in buying access to sources, contact otclient@otclient.ovh or kondrah#7945 on discord. ## DISCORD: https://discord.gg/feySup6 -## Forum: https://otland.net/forums/otclient.494/ -## Open Tibia Login Server: https://github.com/OTCv8/OpenTibiaLoginServer +## Forum: http://otclient.net # FEATURES - Rewritten and optimized rendering (60 fps on 11 years old computer) @@ -43,9 +54,6 @@ It's based on https://github.com/edubart/otclient and it's not backward compatib ### There's github repo of tfs 1.3 with otclientv8 features: https://github.com/OTCv8/otclientv8-tfs -# Paid version -The difference between paid version and this one is that the 1st one comes with c++ sources and has better support. You may need c++ source if you want to add some more advanced modifications, better encryption, bot protection or some other things. The free version doesn't offer technical support, you need to follow tutorials and in case of any bug or problem you should submit an issue on github. Visit http://otclient.ovh if you want to know more about paid version and other extra services. - # Quick Start for players Download whole repository and run one of binary file. diff --git a/api/crash.php b/api/crash.php deleted file mode 100644 index fc84d0b..0000000 --- a/api/crash.php +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/api/feedback.php b/api/feedback.php deleted file mode 100644 index a2c6ad1..0000000 --- a/api/feedback.php +++ /dev/null @@ -1,14 +0,0 @@ -player->name) .": ". ($json->text) ."\n".$data."\n\n\n", FILE_APPEND); - -echo "OK"; -?> \ No newline at end of file diff --git a/api/updater.php b/api/updater.php index e2da2e2..f263a13 100644 --- a/api/updater.php +++ b/api/updater.php @@ -1,92 +1,68 @@ 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)); + ?> \ No newline at end of file diff --git a/api/updater_advanced.php b/api/updater_advanced.php new file mode 100644 index 0000000..788e091 --- /dev/null +++ b/api/updater_advanced.php @@ -0,0 +1,76 @@ + "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)); + +?> \ No newline at end of file diff --git a/d3dcompiler_46.dll b/d3dcompiler_46.dll deleted file mode 100644 index c706328..0000000 Binary files a/d3dcompiler_46.dll and /dev/null differ diff --git a/d3dcompiler_47.dll b/d3dcompiler_47.dll new file mode 100644 index 0000000..d52042a Binary files /dev/null and b/d3dcompiler_47.dll differ diff --git a/data/fonts/cipsoftFont.otfont b/data/fonts/cipsoftFont.otfont index 1e9e2fe..6fb2f70 100644 --- a/data/fonts/cipsoftFont.otfont +++ b/data/fonts/cipsoftFont.otfont @@ -4,4 +4,3 @@ Font height: 8 glyph-size: 8 8 space-width: 2 - default: true diff --git a/data/images/game/mobile/attack.png b/data/images/game/mobile/attack.png new file mode 100644 index 0000000..bea4701 Binary files /dev/null and b/data/images/game/mobile/attack.png differ diff --git a/data/images/game/mobile/chat.png b/data/images/game/mobile/chat.png new file mode 100644 index 0000000..ae3002b Binary files /dev/null and b/data/images/game/mobile/chat.png differ diff --git a/data/images/game/mobile/follow.png b/data/images/game/mobile/follow.png new file mode 100644 index 0000000..760487c Binary files /dev/null and b/data/images/game/mobile/follow.png differ diff --git a/data/images/game/mobile/look.png b/data/images/game/mobile/look.png new file mode 100644 index 0000000..bbdf54e Binary files /dev/null and b/data/images/game/mobile/look.png differ diff --git a/data/images/game/mobile/use.png b/data/images/game/mobile/use.png new file mode 100644 index 0000000..596954d Binary files /dev/null and b/data/images/game/mobile/use.png differ diff --git a/data/images/topbuttons/zoomin.png b/data/images/topbuttons/zoomin.png new file mode 100644 index 0000000..bce83ed Binary files /dev/null and b/data/images/topbuttons/zoomin.png differ diff --git a/data/images/topbuttons/zoomout.png b/data/images/topbuttons/zoomout.png new file mode 100644 index 0000000..1dd6f96 Binary files /dev/null and b/data/images/topbuttons/zoomout.png differ diff --git a/data/images/ui/miniwindow.png b/data/images/ui/miniwindow.png index e258304..a8647c8 100644 Binary files a/data/images/ui/miniwindow.png and b/data/images/ui/miniwindow.png differ diff --git a/data/images/ui/otcicon.rc b/data/images/ui/otcicon.rc new file mode 100644 index 0000000..52a9a6e --- /dev/null +++ b/data/images/ui/otcicon.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "otcicon.ico" \ No newline at end of file diff --git a/data/images/ui/panel_bottom.png b/data/images/ui/panel_bottom.png index b56a0c9..8b9da27 100644 Binary files a/data/images/ui/panel_bottom.png and b/data/images/ui/panel_bottom.png differ diff --git a/data/images/ui/panel_container.png b/data/images/ui/panel_container.png index 984672e..23102a3 100644 Binary files a/data/images/ui/panel_container.png and b/data/images/ui/panel_container.png differ diff --git a/data/images/ui/panel_content.png b/data/images/ui/panel_content.png index b56a0c9..209b28f 100644 Binary files a/data/images/ui/panel_content.png and b/data/images/ui/panel_content.png differ diff --git a/data/images/ui/panel_map.png b/data/images/ui/panel_map.png index b56a0c9..8b9da27 100644 Binary files a/data/images/ui/panel_map.png and b/data/images/ui/panel_map.png differ diff --git a/data/images/ui/panel_side.png b/data/images/ui/panel_side.png index b56a0c9..8b9da27 100644 Binary files a/data/images/ui/panel_side.png and b/data/images/ui/panel_side.png differ diff --git a/data/images/ui/window.png b/data/images/ui/window.png index 0b3f3c8..84b3740 100644 Binary files a/data/images/ui/window.png and b/data/images/ui/window.png differ diff --git a/data/images/ui/window_headless.png b/data/images/ui/window_headless.png index d74ec1d..598aa47 100644 Binary files a/data/images/ui/window_headless.png and b/data/images/ui/window_headless.png differ diff --git a/data/particles/particle.png b/data/particles/particle.png deleted file mode 100644 index 044d629..0000000 Binary files a/data/particles/particle.png and /dev/null differ diff --git a/data/particles/particles.otps b/data/particles/particles.otps deleted file mode 100644 index 8424488..0000000 --- a/data/particles/particles.otps +++ /dev/null @@ -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 - diff --git a/data/styles/20-topmenu.otui b/data/styles/20-topmenu.otui index 60574fe..cfed71a 100644 --- a/data/styles/20-topmenu.otui +++ b/data/styles/20-topmenu.otui @@ -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 diff --git a/data/styles/40-background.otui b/data/styles/40-background.otui deleted file mode 100644 index a87feca..0000000 --- a/data/styles/40-background.otui +++ /dev/null @@ -1,5 +0,0 @@ -Background < Panel - image-source: /images/background - image-smooth: true - image-fixed-ratio: true - margin-top: 1 diff --git a/data/styles/40-console.otui b/data/styles/40-console.otui index bc95dc8..c7e160b 100644 --- a/data/styles/40-console.otui +++ b/data/styles/40-console.otui @@ -183,3 +183,4 @@ ConsolePanel < Panel margin-bottom: 6 shift-navigation: true max-length: 255 + text-auto-submit: true diff --git a/data/styles/40-healthinfo.otui b/data/styles/40-healthinfo.otui index 7356f3a..4e45c69 100644 --- a/data/styles/40-healthinfo.otui +++ b/data/styles/40-healthinfo.otui @@ -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 diff --git a/data/things/.gitignore b/data/things/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/data/things/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/init.lua b/init.lua index fcaebf6..ea0af49 100644 --- a/init.lua +++ b/init.lua @@ -1,27 +1,27 @@ -- CONFIG -APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata -APP_VERSION = 1337 -- client version for updater and login to identify outdated client -DEFAULT_LAYOUT = "retro" +APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata +APP_VERSION = 1337 -- client version for updater and login to identify outdated client +DEFAULT_LAYOUT = "retro" -- on android it's forced to "mobile", check code bellow -- If you don't use updater or other service, set it to updater = "" Services = { website = "http://otclient.ovh", -- currently not used - updater = "", + updater = "http://otclient.ovh/api/updater.php", stats = "", crash = "http://otclient.ovh/api/crash.php", - feedback = "", + feedback = "http://otclient.ovh/api/feedback.php", status = "http://otclient.ovh/api/status.php" } -- Servers accept http login url, websocket login url or ip:port:version Servers = { --- OTClientV8c = "otclient.ovh:7171:1099:25:30:80:90", --- OTClientV8 = "http://otclient.ovh/api/login.php", --- OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1", --- OTClientV8Test = "http://otclient.ovh/api/login2.php", +--[[ OTClientV8 = "http://otclient.ovh/api/login.php", + OTClientV8c = "otclient.ovh:7171", + OTClientV8Test = "http://otclient.ovh/api/login2.php", + LocalTestServ = "127.0.0.1:7171:1098:110:30:93" ]] } -USE_NEW_ENERGAME = false -- not done yet +--USE_NEW_ENERGAME = true -- uses entergamev2 based on websockets instead of entergame ALLOW_CUSTOM_SERVERS = true -- if true it shows option ANOTHER on server list g_app.setName("OTCv8") @@ -39,52 +39,50 @@ if not g_resources.directoryExists("/modules") then g_logger.fatal("Modules dir doesn't exist.") end --- send and delete crash report if exist -if Services.crash ~= nil and Services.crash:len() > 4 then - local crashLog = g_resources.readCrashLog(false) - local crashLogTxt = g_resources.readCrashLog(true) - local normalLog = g_logger.getLastLog() - local crashed = false - if crashLog:len() > 0 then - g_http.post(Services.crash .. "?txt=0&version=" .. g_app.getVersion(), crashLog) - crashed = true - end - if crashLogTxt:len() > 0 then - g_http.post(Services.crash .. "?txt=1&version=" .. g_app.getVersion(), crashLogTxt) - crashed = true - end - if crashed and normalLog:len() > 0 then - g_http.post(Services.crash .. "?txt=2&version=" .. g_app.getVersion(), normalLog) - end - g_resources.deleteCrashLog() -end - -- settings g_configs.loadSettings("/config.otml") -- set layout local settings = g_configs.getSettings() local layout = DEFAULT_LAYOUT -if settings:exists('layout') then +if g_app.isMobile() then + layout = "mobile" +elseif settings:exists('layout') then layout = settings:getValue('layout') end g_resources.setLayout(layout) -- load mods g_modules.discoverModules() - --- libraries modules 0-99 -g_modules.autoLoadModules(99) g_modules.ensureModuleLoaded("corelib") -g_modules.ensureModuleLoaded("gamelib") + +local function loadModules() + -- libraries modules 0-99 + g_modules.autoLoadModules(99) + g_modules.ensureModuleLoaded("gamelib") --- client modules 100-499 -g_modules.autoLoadModules(499) -g_modules.ensureModuleLoaded("client") + -- client modules 100-499 + g_modules.autoLoadModules(499) + g_modules.ensureModuleLoaded("client") --- game modules 500-999 -g_modules.autoLoadModules(999) -g_modules.ensureModuleLoaded("game_interface") + -- game modules 500-999 + g_modules.autoLoadModules(999) + g_modules.ensureModuleLoaded("game_interface") --- mods 1000-9999 -g_modules.autoLoadModules(9999) + -- mods 1000-9999 + g_modules.autoLoadModules(9999) +end + +-- report crash +if type(Services.crash) == 'string' and Services.crash:len() > 4 and g_modules.getModule("crash_reporter") then + g_modules.ensureModuleLoaded("crash_reporter") +end + +-- run updater, must use data.zip +if type(Services.updater) == 'string' and Services.updater:len() > 4 + and g_resources.isLoadedFromArchive() and g_modules.getModule("updater") then + g_modules.ensureModuleLoaded("updater") + return Updater.init(loadModules) +end + +loadModules() diff --git a/layouts/mobile/README.md b/layouts/mobile/README.md new file mode 100644 index 0000000..b0367ed --- /dev/null +++ b/layouts/mobile/README.md @@ -0,0 +1 @@ +Min. height for mobile is 360, don't make windows bigger than that \ No newline at end of file diff --git a/layouts/mobile/styles/10-scrollbars.otui b/layouts/mobile/styles/10-scrollbars.otui new file mode 100644 index 0000000..63a0ebf --- /dev/null +++ b/layouts/mobile/styles/10-scrollbars.otui @@ -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 diff --git a/layouts/mobile/styles/20-smallscrollbar.otui b/layouts/mobile/styles/20-smallscrollbar.otui new file mode 100644 index 0000000..e498c0f --- /dev/null +++ b/layouts/mobile/styles/20-smallscrollbar.otui @@ -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 \ No newline at end of file diff --git a/layouts/mobile/styles/30-miniwindow.otui b/layouts/mobile/styles/30-miniwindow.otui new file mode 100644 index 0000000..41ad5b0 --- /dev/null +++ b/layouts/mobile/styles/30-miniwindow.otui @@ -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 diff --git a/layouts/mobile/styles/40-console.otui b/layouts/mobile/styles/40-console.otui new file mode 100644 index 0000000..25f9730 --- /dev/null +++ b/layouts/mobile/styles/40-console.otui @@ -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 diff --git a/layouts/mobile/styles/40-inventory.otui b/layouts/mobile/styles/40-inventory.otui new file mode 100644 index 0000000..d414065 --- /dev/null +++ b/layouts/mobile/styles/40-inventory.otui @@ -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() diff --git a/layouts/retro/images/topbuttons/audio.png b/layouts/retro/images/topbuttons/audio.png index 7bac9e9..d4c6dc6 100644 Binary files a/layouts/retro/images/topbuttons/audio.png and b/layouts/retro/images/topbuttons/audio.png differ diff --git a/layouts/retro/images/topbuttons/audio_mute.png b/layouts/retro/images/topbuttons/audio_mute.png index a7dda9a..33863ad 100644 Binary files a/layouts/retro/images/topbuttons/audio_mute.png and b/layouts/retro/images/topbuttons/audio_mute.png differ diff --git a/layouts/retro/images/topbuttons/bot.png b/layouts/retro/images/topbuttons/bot.png index c9871fd..508b2b7 100644 Binary files a/layouts/retro/images/topbuttons/bot.png and b/layouts/retro/images/topbuttons/bot.png differ diff --git a/layouts/retro/images/topbuttons/debug.png b/layouts/retro/images/topbuttons/debug.png index f8000f6..a04c2fe 100644 Binary files a/layouts/retro/images/topbuttons/debug.png and b/layouts/retro/images/topbuttons/debug.png differ diff --git a/layouts/retro/images/topbuttons/login.png b/layouts/retro/images/topbuttons/login.png index 55ec697..3019fae 100644 Binary files a/layouts/retro/images/topbuttons/login.png and b/layouts/retro/images/topbuttons/login.png differ diff --git a/layouts/retro/images/topbuttons/modulemanager.png b/layouts/retro/images/topbuttons/modulemanager.png index 8089991..2a5fc48 100644 Binary files a/layouts/retro/images/topbuttons/modulemanager.png and b/layouts/retro/images/topbuttons/modulemanager.png differ diff --git a/layouts/retro/images/topbuttons/particles.png b/layouts/retro/images/topbuttons/particles.png index a52cc24..2452eed 100644 Binary files a/layouts/retro/images/topbuttons/particles.png and b/layouts/retro/images/topbuttons/particles.png differ diff --git a/layouts/retro/images/topbuttons/terminal.png b/layouts/retro/images/topbuttons/terminal.png index f55133f..3d113ea 100644 Binary files a/layouts/retro/images/topbuttons/terminal.png and b/layouts/retro/images/topbuttons/terminal.png differ diff --git a/layouts/retro/styles/40-console.otui b/layouts/retro/styles/40-console.otui index b9369e4..555752b 100644 --- a/layouts/retro/styles/40-console.otui +++ b/layouts/retro/styles/40-console.otui @@ -216,3 +216,4 @@ ConsolePanel < Panel margin-bottom: 2 shift-navigation: true max-length: 255 + text-auto-submit: true diff --git a/libEGL.dll b/libEGL.dll index 768b27b..2d85f16 100644 Binary files a/libEGL.dll and b/libEGL.dll differ diff --git a/libGLESv2.dll b/libGLESv2.dll index d18c051..470fbe7 100644 Binary files a/libGLESv2.dll and b/libGLESv2.dll differ diff --git a/lua_functions/luafunctions_framework.cpp b/lua_functions/luafunctions.cpp similarity index 97% rename from lua_functions/luafunctions_framework.cpp rename to lua_functions/luafunctions.cpp index 03b6f2d..2aa8b01 100644 --- a/lua_functions/luafunctions_framework.cpp +++ b/lua_functions/luafunctions.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -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()); - 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()); + 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(&g_app)); g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast(&g_app)); g_lua.bindSingletonFunction("g_app", "quick_exit", &Application::quick_exit, static_cast(&g_app)); + g_lua.bindSingletonFunction("g_app", "isMobile", &Application::isMobile, static_cast(&g_app)); + g_lua.bindSingletonFunction("g_app", "restart", &Application::restart, static_cast(&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(); } ); 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(); @@ -510,7 +515,6 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("backwardsGetWidgetById", &UIWidget::backwardsGetWidgetById); g_lua.bindClassMemberFunction("resize", &UIWidget::resize); g_lua.bindClassMemberFunction("move", &UIWidget::move); - g_lua.bindClassMemberFunction("rotate", &UIWidget::rotate); g_lua.bindClassMemberFunction("hide", &UIWidget::hide); g_lua.bindClassMemberFunction("show", &UIWidget::show); g_lua.bindClassMemberFunction("disable", &UIWidget::disable); @@ -695,6 +699,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("resizeToText", &UIWidget::resizeToText); g_lua.bindClassMemberFunction("clearText", &UIWidget::clearText); g_lua.bindClassMemberFunction("setText", &UIWidget::setText); + g_lua.bindClassMemberFunction("setColoredText", &UIWidget::setColoredText); g_lua.bindClassMemberFunction("setTextAlign", &UIWidget::setTextAlign); g_lua.bindClassMemberFunction("setTextOffset", &UIWidget::setTextOffset); g_lua.bindClassMemberFunction("setTextWrap", &UIWidget::setTextWrap); @@ -822,17 +827,6 @@ void Application::registerLuaFunctions() g_lua.registerClass(); g_lua.registerClass(); g_lua.bindClassMemberFunction("addMultiTexture", &PainterShaderProgram::addMultiTexture); - - // ParticleEffect - g_lua.registerClass(); - g_lua.bindClassStaticFunction("create", []{ return ParticleEffectTypePtr(new ParticleEffectType); }); - g_lua.bindClassMemberFunction("getName", &ParticleEffectType::getName); - g_lua.bindClassMemberFunction("getDescription", &ParticleEffectType::getDescription); - - // UIParticles - g_lua.registerClass(); - g_lua.bindClassStaticFunction("create", []{ return UIParticlesPtr(new UIParticles); } ); - g_lua.bindClassMemberFunction("addEffect", &UIParticles::addEffect); #endif #ifdef FW_NET diff --git a/lua_functions/luafunctions_client.cpp b/lua_functions/luafunctions_client.cpp index 820640b..7290a21 100644 --- a/lua_functions/luafunctions_client.cpp +++ b/lua_functions/luafunctions_client.cpp @@ -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("getSkull", &Creature::getSkull); g_lua.bindClassMemberFunction("getShield", &Creature::getShield); g_lua.bindClassMemberFunction("getEmblem", &Creature::getEmblem); + g_lua.bindClassMemberFunction("setSkull", &Creature::setSkull); + g_lua.bindClassMemberFunction("setShield", &Creature::setShield); + g_lua.bindClassMemberFunction("setEmblem", &Creature::setEmblem); g_lua.bindClassMemberFunction("getType", &Creature::getType); g_lua.bindClassMemberFunction("getIcon", &Creature::getIcon); g_lua.bindClassMemberFunction("setOutfit", &Creature::setOutfit); @@ -526,6 +533,10 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("resetInformationColor", &Creature::resetInformationColor); g_lua.bindClassMemberFunction("setInformationOffset", &Creature::setInformationOffset); g_lua.bindClassMemberFunction("getInformationOffset", &Creature::getInformationOffset); + g_lua.bindClassMemberFunction("setText", &Creature::setText); + g_lua.bindClassMemberFunction("getText", &Creature::getText); + g_lua.bindClassMemberFunction("clearText", &Creature::clearText); + // widgets g_lua.bindClassMemberFunction("addTopWidget", &Creature::addTopWidget); g_lua.bindClassMemberFunction("addBottomWidget", &Creature::addBottomWidget); @@ -630,6 +641,7 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("setCount", &Item::setCount); g_lua.bindClassMemberFunction("getCount", &Item::getCount); g_lua.bindClassMemberFunction("getSubType", &Item::getSubType); + g_lua.bindClassMemberFunction("getCountOrSubType", &Item::getCountOrSubType); g_lua.bindClassMemberFunction("getId", &Item::getId); g_lua.bindClassMemberFunction("getServerId", &Item::getServerId); g_lua.bindClassMemberFunction("getName", &Item::getName); @@ -665,6 +677,7 @@ void Client::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return StaticTextPtr(new StaticText); }); g_lua.bindClassMemberFunction("addMessage", &StaticText::addMessage); + g_lua.bindClassMemberFunction("addColoredMessage", &StaticText::addColoredMessage); g_lua.bindClassMemberFunction("setText", &StaticText::setText); g_lua.bindClassMemberFunction("setFont", &StaticText::setFont); g_lua.bindClassMemberFunction("setColor", &StaticText::setColor); @@ -800,6 +813,7 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("getItemId", &UIItem::getItemId); g_lua.bindClassMemberFunction("getItemCount", &UIItem::getItemCount); g_lua.bindClassMemberFunction("getItemSubType", &UIItem::getItemSubType); + g_lua.bindClassMemberFunction("getItemCountOrSubType", &UIItem::getItemCountOrSubType); g_lua.bindClassMemberFunction("getItem", &UIItem::getItem); g_lua.bindClassMemberFunction("isVirtual", &UIItem::isVirtual); g_lua.bindClassMemberFunction("isItemVisible", &UIItem::isItemVisible); @@ -823,7 +837,7 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("setDirection", &UICreature::setDirection); g_lua.bindClassMemberFunction("setScale", &UICreature::setScale); g_lua.bindClassMemberFunction("getScale", &UICreature::getScale); - g_lua.bindClassMemberFunction("setRaw", &UICreature::setRaw); + g_lua.bindClassMemberFunction("setOptimized", &UICreature::setOptimized); g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return UIMapPtr(new UIMap); }); @@ -850,7 +864,6 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("setDrawPlayerBars", &UIMap::setDrawPlayerBars); g_lua.bindClassMemberFunction("setAnimated", &UIMap::setAnimated); g_lua.bindClassMemberFunction("setKeepAspectRatio", &UIMap::setKeepAspectRatio); - g_lua.bindClassMemberFunction("setMapShader", &UIMap::setMapShader); g_lua.bindClassMemberFunction("setMinimumAmbientLight", &UIMap::setMinimumAmbientLight); g_lua.bindClassMemberFunction("setLimitVisibleRange", &UIMap::setLimitVisibleRange); g_lua.bindClassMemberFunction("setFloorFading", &UIMap::setFloorFading); @@ -875,7 +888,6 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("getMaxZoomIn", &UIMap::getMaxZoomIn); g_lua.bindClassMemberFunction("getMaxZoomOut", &UIMap::getMaxZoomOut); g_lua.bindClassMemberFunction("getZoom", &UIMap::getZoom); - g_lua.bindClassMemberFunction("getMapShader", &UIMap::getMapShader); g_lua.bindClassMemberFunction("getMinimumAmbientLight", &UIMap::getMinimumAmbientLight); g_lua.registerClass(); diff --git a/modules/client/client.lua b/modules/client/client.lua index a051bb0..7dfafb2 100644 --- a/modules/client/client.lua +++ b/modules/client/client.lua @@ -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 diff --git a/modules/client/client.otmod b/modules/client/client.otmod index 09e5c5c..48124cd 100644 --- a/modules/client/client.otmod +++ b/modules/client/client.otmod @@ -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 diff --git a/modules/client_background/background.lua b/modules/client_background/background.lua index 4fdd175..9ecc98c 100644 --- a/modules/client_background/background.lua +++ b/modules/client_background/background.lua @@ -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 diff --git a/modules/client_background/background.otmod b/modules/client_background/background.otmod index d4c3f09..8fb34cb 100644 --- a/modules/client_background/background.otmod +++ b/modules/client_background/background.otmod @@ -5,6 +5,5 @@ Module website: https://github.com/edubart/otclient sandboxed: true scripts: [ background ] - dependencies: [ client_topmenu ] @onLoad: init() @onUnload: terminate() diff --git a/modules/client_background/background.otui b/modules/client_background/background.otui index ac202da..c90fa1c 100644 --- a/modules/client_background/background.otui +++ b/modules/client_background/background.otui @@ -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 diff --git a/modules/client_entergame/characterlist.otui b/modules/client_entergame/characterlist.otui index 0fedb48..36911e4 100644 --- a/modules/client_entergame/characterlist.otui +++ b/modules/client_entergame/characterlist.otui @@ -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 diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index bb5af60..57fbf77 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -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 diff --git a/modules/client_entergame/entergame.otmod b/modules/client_entergame/entergame.otmod index 517af2d..3b39ae1 100644 --- a/modules/client_entergame/entergame.otmod +++ b/modules/client_entergame/entergame.otmod @@ -6,4 +6,7 @@ Module scripts: [ entergame, characterlist ] @onLoad: EnterGame.init() CharacterList.init() @onUnload: EnterGame.terminate() CharacterList.terminate() - \ No newline at end of file + + load-later: + - game_things + - game_features diff --git a/modules/client_locales/locales.lua b/modules/client_locales/locales.lua index 107c5a8..f221f01 100644 --- a/modules/client_locales/locales.lua +++ b/modules/client_locales/locales.lua @@ -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 diff --git a/modules/client_mobile/mobile.lua b/modules/client_mobile/mobile.lua new file mode 100644 index 0000000..34ef6ec --- /dev/null +++ b/modules/client_mobile/mobile.lua @@ -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 \ No newline at end of file diff --git a/modules/client_mobile/mobile.otmod b/modules/client_mobile/mobile.otmod new file mode 100644 index 0000000..da7a329 --- /dev/null +++ b/modules/client_mobile/mobile.otmod @@ -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() diff --git a/modules/client_mobile/mobile.otui b/modules/client_mobile/mobile.otui new file mode 100644 index 0000000..e5ddb93 --- /dev/null +++ b/modules/client_mobile/mobile.otui @@ -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 diff --git a/modules/client_options/audio.otui b/modules/client_options/audio.otui index 509f09d..4e803b4 100644 --- a/modules/client_options/audio.otui +++ b/modules/client_options/audio.otui @@ -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 diff --git a/modules/client_options/console.otui b/modules/client_options/console.otui index 7740201..a0b0bae 100644 --- a/modules/client_options/console.otui +++ b/modules/client_options/console.otui @@ -1,4 +1,4 @@ -Panel +OptionPanel OptionCheckBox id: showInfoMessagesInConsole !text: tr('Show info messages in console') diff --git a/modules/client_options/game.otui b/modules/client_options/game.otui index 0362e81..b0adb0b 100644 --- a/modules/client_options/game.otui +++ b/modules/client_options/game.otui @@ -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 diff --git a/modules/client_options/graphics.otui b/modules/client_options/graphics.otui index 254b4fc..3f1221f 100644 --- a/modules/client_options/graphics.otui +++ b/modules/client_options/graphics.otui @@ -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") \ No newline at end of file + !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 \ No newline at end of file diff --git a/modules/client_options/interface.otui b/modules/client_options/interface.otui index a49b470..f43b51d 100644 --- a/modules/client_options/interface.otui +++ b/modules/client_options/interface.otui @@ -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')) diff --git a/modules/client_options/options.lua b/modules/client_options/options.lua index 0d1778b..c5905a8 100644 --- a/modules/client_options/options.lua +++ b/modules/client_options/options.lua @@ -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 diff --git a/modules/client_options/options.otui b/modules/client_options/options.otui index 946d7bd..061c46b 100644 --- a/modules/client_options/options.otui +++ b/modules/client_options/options.otui @@ -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() diff --git a/modules/client_stats/stats.lua b/modules/client_stats/stats.lua index 5587e2c..7b7e9ff 100644 --- a/modules/client_stats/stats.lua +++ b/modules/client_stats/stats.lua @@ -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) diff --git a/modules/client_stats/stats.otui b/modules/client_stats/stats.otui index 8ed8acb..3a354ed 100644 --- a/modules/client_stats/stats.otui +++ b/modules/client_stats/stats.otui @@ -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') diff --git a/modules/client_styles/styles.lua b/modules/client_styles/styles.lua index ef6fc24..c8b146d 100644 --- a/modules/client_styles/styles.lua +++ b/modules/client_styles/styles.lua @@ -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') diff --git a/modules/client_terminal/terminal.lua b/modules/client_terminal/terminal.lua index 693b62f..67f0578 100644 --- a/modules/client_terminal/terminal.lua +++ b/modules/client_terminal/terminal.lua @@ -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 diff --git a/modules/client_terminal/terminal.otui b/modules/client_terminal/terminal.otui index 67b8ba0..44e8a54 100644 --- a/modules/client_terminal/terminal.otui +++ b/modules/client_terminal/terminal.otui @@ -94,6 +94,7 @@ UIWindow border-width-left: 0 border-width-top: 0 multiline: false + text-auto-submit: true $on: border-width-left: 1 diff --git a/modules/game_textedit/textedit.lua b/modules/client_textedit/textedit.lua similarity index 86% rename from modules/game_textedit/textedit.lua rename to modules/client_textedit/textedit.lua index 67cbc51..7d41fdc 100644 --- a/modules/game_textedit/textedit.lua +++ b/modules/client_textedit/textedit.lua @@ -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 diff --git a/modules/game_textedit/textedit.otmod b/modules/client_textedit/textedit.otmod similarity index 63% rename from modules/game_textedit/textedit.otmod rename to modules/client_textedit/textedit.otmod index 0396c7d..e8ba2c6 100644 --- a/modules/game_textedit/textedit.otmod +++ b/modules/client_textedit/textedit.otmod @@ -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() diff --git a/modules/game_textedit/textedit.otui b/modules/client_textedit/textedit.otui similarity index 95% rename from modules/game_textedit/textedit.otui rename to modules/client_textedit/textedit.otui index 8267207..86d77aa 100644 --- a/modules/game_textedit/textedit.otui +++ b/modules/client_textedit/textedit.otui @@ -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 diff --git a/modules/client_updater/updater.lua b/modules/client_updater/updater.lua deleted file mode 100644 index 69a4d2b..0000000 --- a/modules/client_updater/updater.lua +++ /dev/null @@ -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 diff --git a/modules/client_updater/updater.otui b/modules/client_updater/updater.otui deleted file mode 100644 index 290cdb2..0000000 --- a/modules/client_updater/updater.otui +++ /dev/null @@ -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() diff --git a/modules/corelib/base64.lua b/modules/corelib/base64.lua new file mode 100644 index 0000000..c220f4b --- /dev/null +++ b/modules/corelib/base64.lua @@ -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. +--]] \ No newline at end of file diff --git a/modules/corelib/corelib.otmod b/modules/corelib/corelib.otmod index 669527e..1c7f068 100644 --- a/modules/corelib/corelib.otmod +++ b/modules/corelib/corelib.otmod @@ -28,6 +28,7 @@ Module dofile 'outputmessage' dofile 'orderedtable' + dofile 'base64' dofile 'json' dofile 'http' \ No newline at end of file diff --git a/modules/corelib/http.lua b/modules/corelib/http.lua index 4a5c7e4..d18eb45 100644 --- a/modules/corelib/http.lua +++ b/modules/corelib/http.lua @@ -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 diff --git a/modules/corelib/table.lua b/modules/corelib/table.lua index 4dd0ad2..60ce55d 100644 --- a/modules/corelib/table.lua +++ b/modules/corelib/table.lua @@ -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 diff --git a/modules/corelib/ui/uipopupmenu.lua b/modules/corelib/ui/uipopupmenu.lua index 2167b83..16aaca0 100644 --- a/modules/corelib/ui/uipopupmenu.lua +++ b/modules/corelib/ui/uipopupmenu.lua @@ -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() diff --git a/modules/corelib/ui/uiscrollbar.lua b/modules/corelib/ui/uiscrollbar.lua index b120dd7..940d3eb 100644 --- a/modules/corelib/ui/uiscrollbar.lua +++ b/modules/corelib/ui/uiscrollbar.lua @@ -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 diff --git a/modules/corelib/util.lua b/modules/corelib/util.lua index 69213ed..910f06f 100644 --- a/modules/corelib/util.lua +++ b/modules/corelib/util.lua @@ -38,7 +38,7 @@ function exit() end function quit() - g_app.quit() + g_app.exit() end function connect(object, arg1, arg2, arg3) diff --git a/modules/crash_reporter/crash_reporter.lua b/modules/crash_reporter/crash_reporter.lua new file mode 100644 index 0000000..1c4057f --- /dev/null +++ b/modules/crash_reporter/crash_reporter.lua @@ -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 + \ No newline at end of file diff --git a/modules/crash_reporter/crash_reporter.otmod b/modules/crash_reporter/crash_reporter.otmod new file mode 100644 index 0000000..8c879a0 --- /dev/null +++ b/modules/crash_reporter/crash_reporter.otmod @@ -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() diff --git a/modules/game_actionbar/actionbar.lua b/modules/game_actionbar/actionbar.lua index 43b88a2..b166208 100644 --- a/modules/game_actionbar/actionbar.lua +++ b/modules/game_actionbar/actionbar.lua @@ -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 diff --git a/modules/game_actionbar/actionbar.otui b/modules/game_actionbar/actionbar.otui index ab66a82..a4e510a 100644 --- a/modules/game_actionbar/actionbar.otui +++ b/modules/game_actionbar/actionbar.otui @@ -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 diff --git a/modules/game_battle/battlebutton.otui b/modules/game_battle/battlebutton.otui index 508db25..38a0158 100644 --- a/modules/game_battle/battlebutton.otui +++ b/modules/game_battle/battlebutton.otui @@ -1,3 +1,2 @@ BattleButton < CreatureButton &isBattleButton: true - optimized: true \ No newline at end of file diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_attack.lua b/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_attack.lua deleted file mode 100644 index ef2b6b8..0000000 --- a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_attack.lua +++ /dev/null @@ -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 diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/actions.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/actions.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/actions.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/actions.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/cavebot.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.lua similarity index 98% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/cavebot.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.lua index 89db97b..910776a 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/cavebot/cavebot.lua +++ b/modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.lua @@ -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 diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/cavebot.otui b/modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.otui similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/cavebot.otui rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/cavebot.otui diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/depositer.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/depositer.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/depositer.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/depositer.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/editor.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/editor.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/editor.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/editor.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/editor.otui b/modules/game_bot/default_configs/cavebot_1.2/cavebot/editor.otui similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/editor.otui rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/editor.otui diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/example_functions.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/example_functions.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/example_functions.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/example_functions.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/extension_template.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/extension_template.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/extension_template.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/extension_template.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/recorder.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/recorder.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/recorder.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/recorder.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/supply.lua b/modules/game_bot/default_configs/cavebot_1.2/cavebot/supply.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/supply.lua rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/supply.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/cavebot/supply.otui b/modules/game_bot/default_configs/cavebot_1.2/cavebot/supply.otui similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/cavebot/supply.otui rename to modules/game_bot/default_configs/cavebot_1.2/cavebot/supply.otui diff --git a/modules/game_bot/default_configs/cavebot_1.1/hp.lua b/modules/game_bot/default_configs/cavebot_1.2/hp.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/hp.lua rename to modules/game_bot/default_configs/cavebot_1.2/hp.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/main.lua b/modules/game_bot/default_configs/cavebot_1.2/main.lua similarity index 79% rename from modules/game_bot/default_configs/cavebot_1.1/main.lua rename to modules/game_bot/default_configs/cavebot_1.2/main.lua index cd976c2..e87ccd2 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/main.lua +++ b/modules/game_bot/default_configs/cavebot_1.2/main.lua @@ -1,5 +1,5 @@ -- main tab -VERSION = "1.1" +VERSION = "1.2" UI.Label("Config version: " .. VERSION) @@ -14,7 +14,7 @@ UI.Button("Discord", function() end) UI.Button("Forum", function() - g_platform.openUrl("https://otland.net/forums/otclient.494/") + g_platform.openUrl("https://otclient.net/") end) UI.Button("Help & Tutorials", function() diff --git a/modules/game_bot/default_configs/cavebot_1.1/mwall_timer.lua b/modules/game_bot/default_configs/cavebot_1.2/mwall_timer.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/mwall_timer.lua rename to modules/game_bot/default_configs/cavebot_1.2/mwall_timer.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/creature.lua rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/creature.lua diff --git a/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_attack.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_attack.lua new file mode 100644 index 0000000..6d6a8f2 --- /dev/null +++ b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_attack.lua @@ -0,0 +1,112 @@ +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() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) 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.useGroupAttackRune and config.groupAttackRune > 100 then + local creatures = g_map.getSpectatorsInRange(creature:getPosition(), false, config.groupRuneAttackRadius, config.groupRuneAttackRadius) + local playersAround = false + local monsters = 0 + for _, creature in ipairs(creatures) do + if not creature:isLocalPlayer() and creature:isPlayer() and (not config.groupAttackIgnoreParty or creature:getShield() <= 2) then + playersAround = true + elseif creature:isMonster() then + monsters = monsters + 1 + end + end + if monsters >= config.groupRuneAttackTargets and (not playersAround or config.groupAttackIgnorePlayers) then + if TargetBot.useAttackItem(config.groupAttackRune, 0, creature, config.groupRuneAttackDelay) 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) + local cpos = creature:getPosition() + local pos = player:getPosition() + + -- luring + if (config.lure or config.lureCavebot) and not (config.chase and creature:getHealthPercent() < 30) then + local monsters = 0 + if targets < config.lureCount then + if config.lureCavebot then + return TargetBot.allowCaveBot(200) + else + local path = findPath(pos, cpos, 5, {ignoreNonPathable=true, precision=2}) + if path then + return TargetBot.walkTo(cpos, 10, {marginMin=5, marginMax=6, ignoreNonPathable=true}) + end + end + end + end + + local currentDistance = findPath(pos, cpos, 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(cpos, 10, {ignoreNonPathable=true, precision=1}) + end + elseif config.keepDistance then + if #currentDistance ~= config.keepDistanceRange and #currentDistance ~= config.keepDistanceRange + 1 then + return TargetBot.walkTo(cpos, 10, {ignoreNonPathable=true, marginMin=config.keepDistanceRange, marginMax=config.keepDistanceRange + 1}) + end + end + + if config.avoidAttacks then + local diffx = cpos.x - pos.x + local diffy = cpos.y - pos.y + local candidates = {} + if math.abs(diffx) == 1 and diffy == 0 then + candidates = {{x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x, y=pos.y+1, z=pos.z}} + elseif diffx == 0 and math.abs(diffy) == 1 then + candidates = {{x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x+1, y=pos.y, z=pos.z}} + end + for _, candidate in ipairs(candidates) do + local tile = g_map.getTile(candidate) + if tile and tile:isWalkable() then + return TargetBot.walkTo(candidate, 2, {ignoreNonPathable=true}) + end + end + end +end diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_editor.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.lua similarity index 87% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_editor.lua rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.lua index cbacc66..2931c2e 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_editor.lua +++ b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.lua @@ -82,18 +82,24 @@ TargetBot.Creature.edit = function(config, callback) -- callback = function(newC addScrollBar("groupAttackRadius", "Radius of group attack spell", 1, 7, 1) addScrollBar("groupAttackDelay", "Group attack spell delay", 200, 60000, 5000) addScrollBar("runeAttackDelay", "Rune attack delay", 200, 5000, 2000) + addScrollBar("groupRuneAttackTargets", "Min. targets for group rune attack", 1, 10, 2) + addScrollBar("groupRuneAttackRadius", "Radius of group rune attack", 1, 7, 1) + addScrollBar("groupRuneAttackDelay", "Group rune attack delay", 200, 60000, 5000) addCheckBox("chase", "Chase", true) addCheckBox("keepDistance", "Keep Distance", false) addCheckBox("lure", "Lure", false) --- addCheckBox("avoidAttacks", "Avoid attacks", true) + addCheckBox("lureCavebot", "Lure using cavebot", false) + addCheckBox("avoidAttacks", "Avoid wave attacks", false) addCheckBox("useSpellAttack", "Use attack spell", false) addTextEdit("attackSpell", "Attack spell", "") - addCheckBox("useGroupAttack", "Use group attack spell", false) - addCheckBox("groupAttackIgnorePlayers", "Ignore players in group attack", false) - addTextEdit("groupAttackSpell", "Group attack spell", "") addCheckBox("useRuneAttack", "Use attack rune", false) addItem("attackRune", "Attack rune:", 0) - + addCheckBox("useGroupAttack", "Use group attack spell", false) + addTextEdit("groupAttackSpell", "Group attack spell", "") + addCheckBox("useGroupAttackRune", "Use group attack rune", false) + addItem("groupAttackRune", "Group attack rune:", 0) + addCheckBox("groupAttackIgnorePlayers", "Ignore players in group attack", false) + addCheckBox("groupAttackIgnoreParty", "Ignore party in group attack", false) end diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_editor.otui b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.otui similarity index 97% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_editor.otui rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.otui index 5f45b73..59b9cc6 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_editor.otui +++ b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_editor.otui @@ -1,6 +1,6 @@ TargetBotCreatureEditorScrollBar < Panel - height: 35 - margin-top: 7 + height: 28 + margin-top: 3 Label id: text @@ -14,7 +14,7 @@ TargetBotCreatureEditorScrollBar < Panel anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom - margin-top: 5 + margin-top: 3 minimum: 0 maximum: 10 step: 1 @@ -64,7 +64,7 @@ TargetBotCreatureEditorCheckBox < BotSwitch TargetBotCreatureEditorWindow < MainWindow text: TargetBot creature editor width: 500 - height: 650 + height: 600 Label anchors.left: parent.left diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_priority.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_priority.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/creature_priority.lua rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/creature_priority.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/looting.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/looting.lua similarity index 97% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/looting.lua rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/looting.lua index a763b08..c1d687e 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/targetbot/looting.lua +++ b/modules/game_bot/default_configs/cavebot_1.2/targetbot/looting.lua @@ -145,14 +145,14 @@ TargetBot.Looting.process = function(targets, dangerLevel) end local container = tile:getTopUseThing() - if not container or container:getId() ~= loot.container then + if not container or not container:isContainer() then table.remove(TargetBot.Looting.list, 1) return true end g_game.open(container) waitTill = now + 1000 -- give it 1s to open - waitingForContainer = loot.container + waitingForContainer = container:getId() loot.tries = loot.tries + 10 return true @@ -213,9 +213,9 @@ TargetBot.Looting.lootContainer = function(lootContainers, container) -- loot items local nextContainer = nil for i, item in ipairs(container:getItems()) do - if item:isContainer() then + if item:isContainer() and not itemsById[item:getId()] then nextContainer = item - elseif itemsById[item:getId()] or ui.everyItem:isOn() then + elseif itemsById[item:getId()] or (ui.everyItem:isOn() and not item:isContainer()) then item.lootTries = (item.lootTries or 0) + 1 if item.lootTries < 5 then -- if can't be looted within 0.5s then skip it return TargetBot.Looting.lootItem(lootContainers, item) diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/looting.otui b/modules/game_bot/default_configs/cavebot_1.2/targetbot/looting.otui similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/looting.otui rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/looting.otui diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/target.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/target.lua similarity index 96% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/target.lua rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/target.lua index 803ea12..c8e8516 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/targetbot/target.lua +++ b/modules/game_bot/default_configs/cavebot_1.2/targetbot/target.lua @@ -1,6 +1,7 @@ local targetbotMacro = nil local config = nil local lastAction = 0 +local cavebotAllowance = 0 -- ui local configWidget = UI.Config() @@ -72,6 +73,8 @@ targetbotMacro = macro(100, function() TargetBot.Creature.attack(highestPriorityParams, targets, looting) if lootingStatus:len() > 0 then TargetBot.setStatus("Attack & " .. lootingStatus) + elseif cavebotAllowance > now then + TargetBot.setStatus("Luring using CaveBot") else TargetBot.setStatus("Attacking") end @@ -148,6 +151,10 @@ TargetBot.isActive = function() -- return true if attacking or looting takes pla return lastAction + 300 > now end +TargetBot.isCaveBotActionAllowed = function() + return cavebotAllowance > now +end + TargetBot.setStatus = function(text) return ui.status.right:setText(text) end @@ -187,6 +194,10 @@ TargetBot.save = function() config.save(data) end +TargetBot.allowCaveBot = function(time) + cavebotAllowance = now + time +end + -- attacks local lastSpell = 0 local lastAttackSpell = 0 diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/target.otui b/modules/game_bot/default_configs/cavebot_1.2/targetbot/target.otui similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/target.otui rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/target.otui diff --git a/modules/game_bot/default_configs/cavebot_1.1/targetbot/walking.lua b/modules/game_bot/default_configs/cavebot_1.2/targetbot/walking.lua similarity index 100% rename from modules/game_bot/default_configs/cavebot_1.1/targetbot/walking.lua rename to modules/game_bot/default_configs/cavebot_1.2/targetbot/walking.lua diff --git a/modules/game_bot/default_configs/cavebot_1.1/tools.lua b/modules/game_bot/default_configs/cavebot_1.2/tools.lua similarity index 75% rename from modules/game_bot/default_configs/cavebot_1.1/tools.lua rename to modules/game_bot/default_configs/cavebot_1.2/tools.lua index 383b25f..7621ab0 100644 --- a/modules/game_bot/default_configs/cavebot_1.1/tools.lua +++ b/modules/game_bot/default_configs/cavebot_1.2/tools.lua @@ -72,6 +72,42 @@ macro(1000, "Stack items", function() end end) +macro(10000, "Anti Kick", function() + local dir = player:getDirection() + turn((dir + 1) % 4) + turn(dir) +end) + +UI.Separator() +UI.Label("Drop items:") +if type(storage.dropItems) ~= "table" then + storage.dropItems = {283, 284, 285} +end + +local foodContainer = UI.Container(function(widget, items) + storage.dropItems = items +end, true) +foodContainer:setHeight(35) +foodContainer:setItems(storage.dropItems) + +macro(5000, "drop items", function() + if not storage.dropItems[1] then return end + if TargetBot and TargetBot.isActive() then return end -- pause when attacking + for _, container in pairs(g_game.getContainers()) do + for __, item in ipairs(container:getItems()) do + for i, dropItem in ipairs(storage.dropItems) do + if item:getId() == dropItem.id then + if item:isStackable() then + return g_game.move(item, player:getPosition(), item:getCount()) + else + return g_game.move(item, player:getPosition(), dropItem.count) -- count is also subtype + end + end + end + end + end +end) + UI.Separator() UI.Label("Mana training") @@ -80,6 +116,7 @@ if type(storage.manaTrain) ~= "table" then end local manatrainmacro = macro(1000, function() + if TargetBot and TargetBot.isActive() then return end -- pause when attacking local mana = math.min(100, math.floor(100 * (player:getMana() / player:getMaxMana()))) if storage.manaTrain.max >= mana and mana >= storage.manaTrain.min then say(storage.manaTrain.text) diff --git a/modules/game_bot/executor.lua b/modules/game_bot/executor.lua index d663dab..b9e4916 100644 --- a/modules/game_bot/executor.lua +++ b/modules/game_bot/executor.lua @@ -81,6 +81,7 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, relo context.gcinfo = gcinfo context.tr = tr context.json = json + context.base64 = base64 context.regexMatch = regexMatch context.getDistanceBetween = function(p1, p2) return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y)) diff --git a/modules/game_bot/functions/map.lua b/modules/game_bot/functions/map.lua index 4a6ee8d..0e9fd17 100644 --- a/modules/game_bot/functions/map.lua +++ b/modules/game_bot/functions/map.lua @@ -1,5 +1,7 @@ local context = G.botContext +context.getMapView = function() return modules.game_interface.getMapPanel() end +context.getMapPanel = context.getMapView context.zoomIn = function() modules.game_interface.getMapPanel():zoomIn() end context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end diff --git a/modules/game_bot/functions/ui_elements.lua b/modules/game_bot/functions/ui_elements.lua index af34bfa..985e1b4 100644 --- a/modules/game_bot/functions/ui_elements.lua +++ b/modules/game_bot/functions/ui_elements.lua @@ -80,7 +80,7 @@ UI.Container = function(callback, unique, parent, widget) for i, child in ipairs(widget.items:getChildren()) do if child:getItemId() >= 100 then if not duplicates[child:getItemId()] or not unique then - table.insert(items, {id=child:getItemId(), count=child:getItemCount()}) + table.insert(items, {id=child:getItemId(), count=child:getItemCountOrSubType()}) duplicates[child:getItemId()] = true end end diff --git a/modules/game_bot/functions/ui_windows.lua b/modules/game_bot/functions/ui_windows.lua index 4795f4b..d066181 100644 --- a/modules/game_bot/functions/ui_windows.lua +++ b/modules/game_bot/functions/ui_windows.lua @@ -14,7 +14,7 @@ UI.EditorWindow = function(text, options, callback) validation = text (regex) examples = {{name, text}, {name, text}} ]]-- - local window = modules.game_textedit.edit(text, options, callback) + local window = modules.client_textedit.edit(text, options, callback) window.botWidget = true return window end diff --git a/modules/game_bot/panels/attacking.lua b/modules/game_bot/panels/attacking.lua index 3833364..ad98548 100644 --- a/modules/game_bot/panels/attacking.lua +++ b/modules/game_bot/panels/attacking.lua @@ -679,7 +679,7 @@ Panel refreshConfig() end ui.add.onClick = function() - modules.game_textedit.multilineEditor("Target list editor", "name:Config name", function(newText) + modules.client_textedit.multilineEditor("Target list editor", "name:Config name", function(newText) table.insert(context.storage.attacking.configs, newText) context.storage.attacking.activeConfig = #context.storage.attacking.configs refreshConfig() @@ -689,7 +689,7 @@ Panel if not context.storage.attacking.activeConfig or not context.storage.attacking.configs[context.storage.attacking.activeConfig] then return end - modules.game_textedit.multilineEditor("Target list editor", context.storage.attacking.configs[context.storage.attacking.activeConfig], function(newText) + modules.client_textedit.multilineEditor("Target list editor", context.storage.attacking.configs[context.storage.attacking.activeConfig], function(newText) context.storage.attacking.configs[context.storage.attacking.activeConfig] = newText refreshConfig() end) diff --git a/modules/game_bot/panels/looting.lua b/modules/game_bot/panels/looting.lua index e665bb0..d6ecd05 100644 --- a/modules/game_bot/panels/looting.lua +++ b/modules/game_bot/panels/looting.lua @@ -266,7 +266,7 @@ Panel refreshConfig() end ui.add.onClick = function() - modules.game_textedit.multilineEditor("Looting editor", "name:Config name", function(newText) + modules.client_textedit.multilineEditor("Looting editor", "name:Config name", function(newText) table.insert(context.storage.looting.configs, newText) context.storage.looting.activeConfig = #context.storage.looting.configs refreshConfig() @@ -276,7 +276,7 @@ Panel if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then return end - modules.game_textedit.multilineEditor("Looting editor", context.storage.looting.configs[context.storage.looting.activeConfig], function(newText) + modules.client_textedit.multilineEditor("Looting editor", context.storage.looting.configs[context.storage.looting.activeConfig], function(newText) context.storage.looting.configs[context.storage.looting.activeConfig] = newText refreshConfig() end) diff --git a/modules/game_bot/panels/waypoints.lua b/modules/game_bot/panels/waypoints.lua index a687a00..bf3190d 100644 --- a/modules/game_bot/panels/waypoints.lua +++ b/modules/game_bot/panels/waypoints.lua @@ -312,7 +312,7 @@ Panel end end ui.add.onClick = function() - modules.game_textedit.multilineEditor("Waypoints editor", "name:Config name\nlabel:start\n", function(newText) + modules.client_textedit.multilineEditor("Waypoints editor", "name:Config name\nlabel:start\n", function(newText) table.insert(context.storage.cavebot.configs, newText) context.storage.cavebot.activeConfig = #context.storage.cavebot.configs refreshConfig() @@ -322,7 +322,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.multilineEditor("Waypoints editor", context.storage.cavebot.configs[context.storage.cavebot.activeConfig], function(newText) + modules.client_textedit.multilineEditor("Waypoints editor", context.storage.cavebot.configs[context.storage.cavebot.activeConfig], function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = newText refreshConfig() end) @@ -428,7 +428,7 @@ Panel return end local pos = context.player:getPosition() - modules.game_textedit.singlelineEditor("" .. pos.x .. "," .. pos.y .. "," .. pos.z, function(newText) + modules.client_textedit.singlelineEditor("" .. pos.x .. "," .. pos.y .. "," .. pos.z, function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\ngoto:" .. newText refreshConfig(true) end) @@ -439,7 +439,7 @@ Panel return end local pos = context.player:getPosition() - modules.game_textedit.singlelineEditor("" .. pos.x .. "," .. pos.y .. "," .. pos.z, function(newText) + modules.client_textedit.singlelineEditor("" .. pos.x .. "," .. pos.y .. "," .. pos.z, function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nuse:" .. newText refreshConfig(true) end) @@ -450,7 +450,7 @@ Panel return end local pos = context.player:getPosition() - modules.game_textedit.singlelineEditor("ITEMID," .. pos.x .. "," .. pos.y .. "," .. pos.z, function(newText) + modules.client_textedit.singlelineEditor("ITEMID," .. pos.x .. "," .. pos.y .. "," .. pos.z, function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nusewith:" .. newText refreshConfig(true) end) @@ -460,7 +460,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.singlelineEditor("1000", function(newText) + modules.client_textedit.singlelineEditor("1000", function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nwait:" .. newText refreshConfig(true) end) @@ -470,7 +470,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.singlelineEditor("text", function(newText) + modules.client_textedit.singlelineEditor("text", function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nsay:" .. newText refreshConfig(true) end) @@ -480,7 +480,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.singlelineEditor("text", function(newText) + modules.client_textedit.singlelineEditor("text", function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nnpc:" .. newText refreshConfig(true) end) @@ -490,7 +490,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.singlelineEditor("label name", function(newText) + modules.client_textedit.singlelineEditor("label name", function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nlabel:" .. newText refreshConfig(true) end) @@ -500,7 +500,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.singlelineEditor("creature name", function(newText) + modules.client_textedit.singlelineEditor("creature name", function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nfollow:" .. newText refreshConfig(true) end) @@ -510,7 +510,7 @@ Panel if not context.storage.cavebot.activeConfig or not context.storage.cavebot.configs[context.storage.cavebot.activeConfig] then return end - modules.game_textedit.multilineEditor("Add function", "function(waypoints)\n -- your lua code, function is executed if previous goto was successful or is just after label\n\n -- must return true to execute next command, otherwise will run in loop till correct return\n return true\nend", function(newText) + modules.client_textedit.multilineEditor("Add function", "function(waypoints)\n -- your lua code, function is executed if previous goto was successful or is just after label\n\n -- must return true to execute next command, otherwise will run in loop till correct return\n return true\nend", function(newText) context.storage.cavebot.configs[context.storage.cavebot.activeConfig] = context.storage.cavebot.configs[context.storage.cavebot.activeConfig] .. "\nfunction:" .. newText refreshConfig(true) end) diff --git a/modules/game_bot/ui/basic.otui b/modules/game_bot/ui/basic.otui index eb8f23e..a1a40fd 100644 --- a/modules/game_bot/ui/basic.otui +++ b/modules/game_bot/ui/basic.otui @@ -29,7 +29,7 @@ BotItem < Item &editable: true BotTextEdit < TextEdit - @onClick: modules.game_textedit.show(self) + @onClick: modules.client_textedit.show(self) text-align: center multiline: false focusable: false @@ -52,6 +52,8 @@ BotPanel < Panel vertical-scrollbar: botPanelScroll layout: type: verticalBox + $mobile: + margin-right: 16 BotSmallScrollBar id: botPanelScroll diff --git a/modules/game_bot/ui/icons.otui b/modules/game_bot/ui/icons.otui index d7e9954..5de4524 100644 --- a/modules/game_bot/ui/icons.otui +++ b/modules/game_bot/ui/icons.otui @@ -22,7 +22,6 @@ BotIcon < UIWidget margin-top: 0 size: 48 48 phantom: true - optimized: true UIWidget id: status diff --git a/modules/game_console/console.lua b/modules/game_console/console.lua index df64ddc..2fe8134 100644 --- a/modules/game_console/console.lua +++ b/modules/game_console/console.lua @@ -194,6 +194,7 @@ function toggleChat() end function enableChat(temporarily) + if g_app.isMobile() then return end if consoleToggleChat:isChecked() then return consoleToggleChat:setChecked(false) end @@ -225,6 +226,7 @@ function enableChat(temporarily) end function disableChat(temporarily) + if g_app.isMobile() then return end if not consoleToggleChat:isChecked() then return consoleToggleChat:setChecked(true) end @@ -591,6 +593,29 @@ function getHighlightedText(text) return tmpData end +function getNewHighlightedText(text, color, highlightColor) + local tmpData = {} + + for i, part in ipairs(text:split("{")) do + if i == 1 then + table.insert(tmpData, part) + table.insert(tmpData, color) + else + for j, part2 in ipairs(part:split("}")) do + if j == 1 then + table.insert(tmpData, part2) + table.insert(tmpData, highlightColor) + else + table.insert(tmpData, part2) + table.insert(tmpData, color) + end + end + end + end + + return tmpData +end + function addTabText(text, speaktype, tab, creatureName) if not tab or tab.locked or not text or #text == 0 then return end @@ -1113,16 +1138,16 @@ function onTalk(name, level, mode, message, channelId, creaturePos) -- Remove curly braces from screen message local staticMessage = message if isNpcMode then - local highlightData = getHighlightedText(staticMessage) - if #highlightData > 0 then - for i = 1, #highlightData / 3 do - local dataBlock = { _start = highlightData[(i-1)*3+1], _end = highlightData[(i-1)*3+2], words = highlightData[(i-1)*3+3] } - staticMessage = staticMessage:gsub("{"..dataBlock.words.."}", dataBlock.words) - end + local highlightData = getNewHighlightedText(staticMessage, speaktype.color, "#1f9ffe") + if #highlightData > 2 then + staticText:addColoredMessage(name, mode, highlightData) + else + staticText:addMessage(name, mode, staticMessage) end staticText:setColor(speaktype.color) + else + staticText:addMessage(name, mode, staticMessage) end - staticText:addMessage(name, mode, staticMessage) g_map.addThing(staticText, creaturePos, -1) end diff --git a/modules/game_healthinfo/healthinfo.lua b/modules/game_healthinfo/healthinfo.lua index 49777b8..3ac4ea4 100644 --- a/modules/game_healthinfo/healthinfo.lua +++ b/modules/game_healthinfo/healthinfo.lua @@ -50,7 +50,11 @@ function init() if not healthInfoWindow.forceOpen then healthInfoButton = modules.client_topmenu.addRightGameToggleButton('healthInfoButton', tr('Health Information'), '/images/topbuttons/healthinfo', toggle) - healthInfoButton:setOn(true) + if g_app.isMobile() then + healthInfoButton:hide() + else + healthInfoButton:setOn(true) + end end healthBar = healthInfoWindow:recursiveGetChildById('healthBar') @@ -89,6 +93,11 @@ function init() hideExperience() healthInfoWindow:setup() + + if g_app.isMobile() then + healthInfoWindow:close() + healthInfoButton:setOn(false) + end end function terminate() @@ -166,8 +175,9 @@ function onHealthChange(localPlayer, health, maxHealth) local healthPercent = math.floor(g_game.getLocalPlayer():getHealthPercent()) local Yhppc = math.floor(208 * (1 - (healthPercent / 100))) - local rect = { x = 0, y = Yhppc, width = 63, height = 208 } + local rect = { x = 0, y = Yhppc, width = 63, height = 208 - Yhppc + 1 } healthCircleFront:setImageClip(rect) + healthCircleFront:setImageRect(rect) if healthPercent > 92 then healthCircleFront:setImageColor("#00BC00FF") @@ -182,14 +192,13 @@ function onHealthChange(localPlayer, health, maxHealth) else healthCircleFront:setImageColor("#850C0CFF") end - - healthCircleFront:setMarginTop(Yhppc) end function onManaChange(localPlayer, mana, maxMana) if mana > maxMana then maxMana = mana end + manaBar:setText(comma_value(mana) .. ' / ' .. comma_value(maxMana)) manaBar:setTooltip(tr(manaTooltip, mana, maxMana)) manaBar:setValue(mana, 0, maxMana) @@ -198,10 +207,10 @@ function onManaChange(localPlayer, mana, maxMana) topManaBar:setTooltip(tr(manaTooltip, mana, maxMana)) topManaBar:setValue(mana, 0, maxMana) - local Ymppc = math.floor(208 * (1 - (math.floor((g_game.getLocalPlayer():getMaxMana() - (g_game.getLocalPlayer():getMaxMana() - g_game.getLocalPlayer():getMana())) * 100 / g_game.getLocalPlayer():getMaxMana()) / 100))) - local rect = { x = 0, y = Ymppc, width = 63, height = 208 } + local Ymppc = math.floor(208 * (1 - (math.floor((maxMana - (maxMana - mana)) * 100 / maxMana) / 100))) + local rect = { x = 0, y = Ymppc, width = 63, height = 208 - Ymppc + 1 } manaCircleFront:setImageClip(rect) - manaCircleFront:setMarginTop(Ymppc) + manaCircleFront:setImageRect(rect) end function onLevelChange(localPlayer, value, percent) @@ -276,8 +285,18 @@ function setExperienceTooltip(tooltip) end function onOverlayGeometryChange() + if g_app.isMobile() then + topHealthBar:setMarginTop(35) + topManaBar:setMarginTop(35) + local width = overlay:getWidth() + local margin = width / 3 + 10 + topHealthBar:setMarginLeft(margin) + topManaBar:setMarginRight(margin) + return + end + local classic = g_settings.getBoolean("classicView") - local minMargin = 100 + local minMargin = 40 if classic then topHealthBar:setMarginTop(15) topManaBar:setMarginTop(15) diff --git a/modules/game_hotkeys/hotkeys_extra.lua b/modules/game_hotkeys/hotkeys_extra.lua index de95d17..01b1f8e 100644 --- a/modules/game_hotkeys/hotkeys_extra.lua +++ b/modules/game_hotkeys/hotkeys_extra.lua @@ -51,9 +51,9 @@ function setupExtraHotkeys(combobox) end local battlePanel = modules.game_battle.battlePanel local attackedCreature = g_game.getAttackingCreature() - local prevChild = battlePanel:getLastChild() + local prevChild = nil for i, child in ipairs(battlePanel:getChildren()) do - if not child.creature or child:isDisabled() then + if not child.creature or child:isDisabled() or child:isHidden() then break end if child.creature == attackedCreature then diff --git a/modules/game_hotkeys/hotkeys_manager.lua b/modules/game_hotkeys/hotkeys_manager.lua index 9060ec8..e9a07c9 100644 --- a/modules/game_hotkeys/hotkeys_manager.lua +++ b/modules/game_hotkeys/hotkeys_manager.lua @@ -43,7 +43,9 @@ configValueChanged = false -- public functions function init() - hotkeysButton = modules.client_topmenu.addLeftGameButton('hotkeysButton', tr('Hotkeys') .. ' (Ctrl+K)', '/images/topbuttons/hotkeys', toggle, false, 7) + if not g_app.isMobile() then + hotkeysButton = modules.client_topmenu.addLeftGameButton('hotkeysButton', tr('Hotkeys') .. ' (Ctrl+K)', '/images/topbuttons/hotkeys', toggle, false, 7) + end g_keyboard.bindKeyDown('Ctrl+K', toggle) hotkeysWindow = g_ui.displayUI('hotkeys_manager') hotkeysWindow:setVisible(false) @@ -104,7 +106,9 @@ function terminate() unload() hotkeysWindow:destroy() - hotkeysButton:destroy() + if hotkeysButton then + hotkeysButton:destroy() + end mouseGrabberWidget:destroy() end diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index 21bff6f..5cd572f 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -3,6 +3,8 @@ gameMapPanel = nil gameRightPanels = nil gameLeftPanels = nil gameBottomPanel = nil +gameActionPanel = nil +gameLeftActions = nil logoutButton = nil mouseGrabberWidget = nil countWindow = nil @@ -44,6 +46,8 @@ function init() gameRightPanels = gameRootPanel:getChildById('gameRightPanels') gameLeftPanels = gameRootPanel:getChildById('gameLeftPanels') gameBottomPanel = gameRootPanel:getChildById('gameBottomPanel') + gameActionPanel = gameRootPanel:getChildById('gameActionPanel') + gameLeftActions = gameRootPanel:getChildById('gameLeftActions') connect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange }) logoutButton = modules.client_topmenu.addLeftButton('logoutButton', tr('Exit'), @@ -52,6 +56,7 @@ function init() gameRightPanels:addChild(g_ui.createWidget('GameSidePanel')) + setupLeftActions() refreshViewMode() bindKeys() @@ -389,12 +394,12 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing) local classic = modules.client_options.getOption('classicControl') local shortcut = nil - if not classic then shortcut = '(Shift)' else shortcut = nil end + if not classic and not g_app.isMobile() then shortcut = '(Shift)' else shortcut = nil end if lookThing then menu:addOption(tr('Look'), function() g_game.look(lookThing) end, shortcut) end - if not classic then shortcut = '(Ctrl)' else shortcut = nil end + if not classic and not g_app.isMobile() then shortcut = '(Ctrl)' else shortcut = nil end if useThing then if useThing:isContainer() then if useThing:getParentContainer() then @@ -470,7 +475,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing) else local localPosition = localPlayer:getPosition() - if not classic then shortcut = '(Alt)' else shortcut = nil end + if not classic and not g_app.isMobile() then shortcut = '(Alt)' else shortcut = nil end if creatureThing:getPosition().z == localPosition.z then if g_game.getAttackingCreature() ~= creatureThing then menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end, shortcut) @@ -565,7 +570,68 @@ end function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, attackCreature, marking) local keyboardModifiers = g_keyboard.getModifiers() - if not modules.client_options.getOption('classicControl') then + if g_app.isMobile() then + if mouseButton == MouseRightButton then + createThingMenu(menuPosition, lookThing, useThing, creatureThing) + return true + end + if mouseButton ~= MouseLeftButton then + return false + end + local action = getLeftAction() + if action == "look" then + if lookThing then + resetLeftActions() + g_game.look(lookThing) + return true + end + return true + elseif action == "use" then + if useThing then + resetLeftActions() + if useThing:isContainer() then + if useThing:getParentContainer() then + g_game.open(useThing, useThing:getParentContainer()) + else + g_game.open(useThing) + end + return true + elseif useThing:isMultiUse() then + startUseWith(useThing) + return true + else + g_game.use(useThing) + return true + end + end + return true + elseif action == "attack" then + if attackCreature and attackCreature ~= player then + resetLeftActions() + g_game.attack(attackCreature) + return true + elseif creatureThing and creatureThing ~= player and creatureThing:getPosition().z == autoWalkPos.z then + resetLeftActions() + g_game.attack(creatureThing) + return true + end + return true + elseif action == "follow" then + if attackCreature and attackCreature ~= player then + resetLeftActions() + g_game.follow(attackCreature) + return true + elseif creatureThing and creatureThing ~= player and creatureThing:getPosition().z == autoWalkPos.z then + resetLeftActions() + g_game.follow(creatureThing) + return true + end + return true + elseif not autoWalkPos and useThing then + createThingMenu(menuPosition, lookThing, useThing, creatureThing) + return true + end + elseif not modules.client_options.getOption('classicControl') then if keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton then createThingMenu(menuPosition, lookThing, useThing, creatureThing) return true @@ -595,9 +661,7 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u g_game.attack(creatureThing) return true end - - -- classic control - else + else -- classic control if useThing and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then local player = g_game.getLocalPlayer() if attackCreature and attackCreature ~= player then @@ -810,8 +874,12 @@ function getBottomPanel() return gameBottomPanel end +function getActionPanel() + return gameActionPanel +end + function refreshViewMode() - local classic = g_settings.getBoolean("classicView") + local classic = g_settings.getBoolean("classicView") and not g_app.isMobile() local rightPanels = g_settings.getNumber("rightPanels") - gameRightPanels:getChildCount() local leftPanels = g_settings.getNumber("leftPanels") - 1 - gameLeftPanels:getChildCount() @@ -838,9 +906,9 @@ function refreshViewMode() return end - local minimumWidth = (g_settings.getNumber("rightPanels") + g_settings.getNumber("leftPanels") - 1) * 200 + 300 - minimumWidth = math.max(minimumWidth, 800) - g_window.setMinimumSize({ width = minimumWidth, height = 600 }) + local minimumWidth = (g_settings.getNumber("rightPanels") + g_settings.getNumber("leftPanels") - 1) * 200 + 200 + minimumWidth = math.max(minimumWidth, g_resources.getLayout() == "mobile" and 640 or 800) + g_window.setMinimumSize({ width = minimumWidth, height = (g_resources.getLayout() == "mobile" and 360 or 600)}) if g_window.getWidth() < minimumWidth then local oldPos = g_window.getPosition() local size = { width = minimumWidth, height = g_window.getHeight() } @@ -868,30 +936,21 @@ function refreshViewMode() gameMapPanel:setMarginLeft(0) gameMapPanel:setMarginRight(0) gameMapPanel:setMarginTop(0) - else - gameLeftPanels:setMarginTop(modules.client_topmenu.getTopMenu():getHeight() - gameLeftPanels:getPaddingTop()) - gameRightPanels:setMarginTop(modules.client_topmenu.getTopMenu():getHeight() - gameRightPanels:getPaddingTop()) end gameMapPanel:setVisibleDimension({ width = 15, height = 11 }) if classic then g_game.changeMapAwareRange(19, 15) - if not modules.client_topmenu.getTopMenu().hideIngame then - gameRootPanel:addAnchor(AnchorTop, 'topMenu', AnchorBottom) - end gameMapPanel:addAnchor(AnchorLeft, 'gameLeftPanels', AnchorRight) gameMapPanel:addAnchor(AnchorRight, 'gameRightPanels', AnchorLeft) - gameMapPanel:addAnchor(AnchorBottom, 'gameBottomPanel', AnchorTop) + gameMapPanel:addAnchor(AnchorBottom, 'gameActionPanel', AnchorTop) gameMapPanel:setKeepAspectRatio(true) gameMapPanel:setLimitVisibleRange(false) gameMapPanel:setZoom(11) + gameMapPanel:setOn(false) -- frame - gameBottomPanel:addAnchor(AnchorLeft, 'gameLeftPanels', AnchorRight) - gameBottomPanel:addAnchor(AnchorRight, 'gameRightPanels', AnchorLeft) - modules.client_topmenu.getTopMenu():setImageColor('white') - gameBottomPanel:setImageColor('white') if modules.game_console then modules.game_console.switchMode(false) @@ -899,13 +958,19 @@ function refreshViewMode() else g_game.changeMapAwareRange(31, 21) gameMapPanel:fill('parent') - gameRootPanel:fill('parent') gameMapPanel:setKeepAspectRatio(false) gameMapPanel:setLimitVisibleRange(false) - gameMapPanel:setZoom(15) + gameMapPanel:setOn(true) + if g_app.isMobile() then + gameMapPanel:setZoom(11) + else + gameMapPanel:setZoom(15) + end modules.client_topmenu.getTopMenu():setImageColor('#ffffff66') - + if g_app.isMobile() then + gameMapPanel:setMarginTop(-32) + end if modules.game_console then modules.game_console.switchMode(true) end @@ -925,7 +990,9 @@ function limitZoom() limitedZoom = true end -function updateSize() +function updateSize() + if g_app.isMobile() then return end + local classic = g_settings.getBoolean("classicView") local height = gameMapPanel:getHeight() local width = gameMapPanel:getWidth() @@ -984,3 +1051,45 @@ function updateSize() gameMapPanel:setMarginLeft(extraMargin) gameMapPanel:setMarginRight(extraMargin) ]] end + +function setupLeftActions() + if not g_app.isMobile() then return end + for _, widget in ipairs(gameLeftActions:getChildren()) do + widget.image:setChecked(false) + widget.onClick = function() + if widget.image:isChecked() then + widget.image:setChecked(false) + return + end + resetLeftActions() + widget.image:setChecked(true) + end + end + if not gameLeftActions.chat then return end + gameLeftActions.chat.onClick = function() + if gameBottomPanel:getHeight() <= 5 then + gameBottomPanel:setHeight(90) + else + gameBottomPanel:setHeight(0) + end + end +end + +function resetLeftActions() + for _, widget in ipairs(gameLeftActions:getChildren()) do + widget.image:setChecked(false) + end +end + +function getLeftAction() + for _, widget in ipairs(gameLeftActions:getChildren()) do + if widget.image:isChecked() then + return widget:getId() + end + end + return "" +end + +function isChatVisible() + return gameBottomPanel:getHeight() >= 5 +end \ No newline at end of file diff --git a/modules/game_interface/gameinterface.otui b/modules/game_interface/gameinterface.otui index 46417d4..533ceb5 100644 --- a/modules/game_interface/gameinterface.otui +++ b/modules/game_interface/gameinterface.otui @@ -1,16 +1,17 @@ GameSidePanel < UIMiniWindowContainer image-source: /images/ui/panel_side image-border: 4 - padding: 4 + padding: 3 padding-top: 0 width: 198 focusable: false on: true layout: type: verticalBox - //spacing: 1 - -GameBottomPanel < Panel + $mobile: + padding: 0 + width: 200 + GameMapPanel < UIGameMap padding: 4 @@ -19,6 +20,24 @@ GameMapPanel < UIGameMap $on: padding: 0 + +GameAction < UIButton + size: 64 64 + phantom: false + + UIButton + id: image + size: 48 48 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + phantom: true + $checked: + opacity: 1.0 + background: #00FF0033 + $!checked: + opacity: 0.6 + background: alpha + UIWidget id: gameRootPanel @@ -33,11 +52,46 @@ UIWidget focusable: false Panel - id: gameLeftPanels + id: gameLeftActions + focusable: false + anchors.bottom: parent.bottom anchors.left: parent.left + width: 64 + + $!mobile: + visible: false + width: 0 + + layout: + type: verticalBox + fit-children: true + + GameAction + id: use + @onSetup: self.image:setImageSource("/images/game/mobile/use") + GameAction + id: attack + @onSetup: self.image:setImageSource("/images/game/mobile/attack") + GameAction + id: follow + @onSetup: self.image:setImageSource("/images/game/mobile/follow") + GameAction + id: look + @onSetup: self.image:setImageSource("/images/game/mobile/look") + GameAction + id: chat + @onSetup: self.image:setImageSource("/images/game/mobile/chat") + + Panel + id: gameLeftPanels + focusable: false anchors.top: parent.top anchors.bottom: parent.bottom - focusable: false + $!mobile: + anchors.left: parent.left + $mobile: + anchors.left: gameLeftActions.right + layout: type: horizontalBox fit-children: true @@ -59,19 +113,48 @@ UIWidget anchors.left: gameLeftPanels.right anchors.right: gameRightPanels.left anchors.bottom: parent.bottom + height: 5 relative-margin: bottom - margin-bottom: 180 - @canUpdateMargin: function(self, newMargin) if modules.client_options.getOption('dontStretchShrink') then return self:getMarginBottom() end return math.max(math.min(newMargin, self:getParent():getHeight() - 300), 80) end - @onGeometryChange: function(self) self:setMarginBottom(math.min(math.max(self:getParent():getHeight() - 300, 80), self:getMarginBottom())) end - - GameBottomPanel - id: gameBottomPanel - anchors.left: bottomSplitter.left - anchors.right: bottomSplitter.right - anchors.top: bottomSplitter.top - anchors.bottom: parent.bottom - margin-top: 3 + margin-bottom: 150 + @canUpdateMargin: function(self, newMargin) if modules.client_options.getOption('dontStretchShrink') then return self:getMarginBottom() end return math.max(math.min(newMargin, self:getParent():getHeight() - 150), 80) end + @onGeometryChange: function(self) self:setMarginBottom(math.min(math.max(self:getParent():getHeight() - 150, 80), self:getMarginBottom())) end + $mobile: + visible: false + + Panel + id: gameActionPanel + phantom: true + focusable: false + + $!mobile: + anchors.left: bottomSplitter.left + anchors.right: bottomSplitter.right + anchors.top: bottomSplitter.top + margin-top: 3 + + $mobile: + anchors.left: gameLeftPanels.right + anchors.right: gameRightPanels.left + anchors.bottom: gameBottomPanel.top + + layout: + type: verticalBox + fit-children: true + + Panel + id: gameBottomPanel + $!mobile: + anchors.left: gameActionPanel.left + anchors.right: gameActionPanel.right + anchors.top: gameActionPanel.bottom + anchors.bottom: parent.bottom + + $mobile: + anchors.left: gameLeftPanels.right + anchors.right: gameRightPanels.left + anchors.bottom: parent.bottom + UIWidget id: mouseGrabber focusable: false diff --git a/modules/game_interface/interface.otmod b/modules/game_interface/interface.otmod index 249fe28..87769b4 100644 --- a/modules/game_interface/interface.otmod +++ b/modules/game_interface/interface.otmod @@ -34,7 +34,7 @@ Module - game_walking - game_shop - game_itemselector - - game_textedit + - client_textedit - game_actionbar - game_prey - game_imbuing diff --git a/modules/game_market/market.lua b/modules/game_market/market.lua index 1d2eb2f..ad42efa 100644 --- a/modules/game_market/market.lua +++ b/modules/game_market/market.lua @@ -1136,10 +1136,12 @@ function Market.loadMarketItems(category) if category == MarketCategory.All then -- loop all categories for category = MarketCategory.First, MarketCategory.Last do - for i = 1, #marketItems[category] do - local item = marketItems[category][i] - if isItemValid(item, category, searchFilter) then - table.insert(currentItems, item) + if marketItems[category] then + for i = 1, #marketItems[category] do + local item = marketItems[category][i] + if isItemValid(item, category, searchFilter) then + table.insert(currentItems, item) + end end end end diff --git a/modules/game_npctrade/npctrade.otui b/modules/game_npctrade/npctrade.otui index bcac605..77938f1 100644 --- a/modules/game_npctrade/npctrade.otui +++ b/modules/game_npctrade/npctrade.otui @@ -18,7 +18,7 @@ NPCItemBox < UICheckBox anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter image-color: #ffffffff - margin-top: 5 + margin-top: 3 $checked on: border-color: #ffffff @@ -33,7 +33,7 @@ NPCItemBox < UICheckBox MainWindow id: npcWindow !text: tr('NPC Trade') - size: 550 515 + size: 550 340 @onEscape: modules.game_npctrade.closeNpcTrade() TabButton @@ -45,8 +45,7 @@ MainWindow anchors.left: parent.left anchors.right: parent.horizontalCenter anchors.top: parent.top - margin-right: 5 - margin-top: 5 + margin-top: 0 TabButton id: sellTab @@ -55,15 +54,13 @@ MainWindow anchors.left: parent.horizontalCenter anchors.right: parent.right anchors.top: parent.top - margin-left: 5 - margin-top: 5 FlatPanel - height: 250 + height: 150 anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom - margin-top: 10 + margin-top: 5 VerticalScrollBar id: itemsPanelListScrollBar @@ -75,7 +72,7 @@ MainWindow ScrollablePanel id: itemsPanel - height: 250 + height: 200 anchors.left: parent.left anchors.right: prev.left anchors.top: parent.top @@ -91,30 +88,19 @@ MainWindow FlatPanel id: setupPanel - height: 140 + height: 105 enabled: false anchors.left: parent.left anchors.right: parent.horizontalCenter anchors.top: prev.bottom - margin-top: 10 + margin-top: 5 margin-right: 5 image-color: #ffffff88 - Label - !text: tr('Id') .. ':' - anchors.left: parent.left - anchors.top: parent.top - margin-top: 5 - margin-left: 5 - width: 85 - - NPCOfferLabel - id: id - Label !text: tr('Name') .. ':' anchors.left: parent.left - anchors.top: prev.bottom + anchors.top: parent.top margin-top: 5 margin-left: 5 width: 85 @@ -122,11 +108,23 @@ MainWindow NPCOfferLabel id: name + Label + !text: tr('Id') .. ':' + anchors.left: parent.left + anchors.top: parent.top + margin-top: 5 + margin-left: 5 + margin-left: 195 + width: 15 + + NPCOfferLabel + id: id + Label !text: tr('Price') .. ':' anchors.left: parent.left anchors.top: prev.bottom - margin-top: 5 + margin-top: 3 margin-left: 5 width: 85 @@ -137,7 +135,7 @@ MainWindow !text: tr('Your Money') .. ':' anchors.left: parent.left anchors.top: prev.bottom - margin-top: 5 + margin-top: 3 margin-left: 5 width: 85 @@ -149,7 +147,7 @@ MainWindow !text: tr('Weight') .. ':' anchors.left: parent.left anchors.top: prev.bottom - margin-top: 5 + margin-top: 3 margin-left: 5 width: 85 @@ -161,7 +159,7 @@ MainWindow !text: tr('Your Capacity') .. ':' anchors.left: parent.left anchors.top: prev.bottom - margin-top: 5 + margin-top: 3 margin-left: 5 width: 85 @@ -173,7 +171,7 @@ MainWindow anchors.left: parent.left anchors.right: parent.right anchors.top: prev.bottom - margin-top: 5 + margin-top: 3 margin-left: 5 margin-right: 5 show-value: true @@ -184,7 +182,7 @@ MainWindow FlatPanel id: buyOptions - height: 140 + height: 80 anchors.top: prev.top anchors.left: parent.horizontalCenter anchors.right: parent.right @@ -217,7 +215,7 @@ MainWindow anchors.left: parent.left anchors.right: parent.right margin-left: 5 - margin-top: 5 + margin-top: 3 @onCheckChange: modules.game_npctrade.onBuyWithBackpackChange() CheckBox @@ -227,7 +225,7 @@ MainWindow anchors.left: parent.left anchors.right: parent.right margin-left: 5 - margin-top: 5 + margin-top: 3 @onCheckChange: modules.game_npctrade.onIgnoreCapacityChange() CheckBox @@ -237,7 +235,7 @@ MainWindow anchors.left: parent.left anchors.right: parent.right margin-left: 5 - margin-top: 5 + margin-top: 3 visible: false checked: true @onCheckChange: modules.game_npctrade.onIgnoreEquippedChange() @@ -249,7 +247,7 @@ MainWindow anchors.left: parent.left anchors.right: parent.right margin-left: 5 - margin-top: 5 + margin-top: 3 visible: false checked: true @onCheckChange: modules.game_npctrade.onShowAllItemsChange() diff --git a/modules/game_outfit/outfitwindow.otui b/modules/game_outfit/outfitwindow.otui index 116f3ba..461ad42 100644 --- a/modules/game_outfit/outfitwindow.otui +++ b/modules/game_outfit/outfitwindow.otui @@ -5,7 +5,7 @@ PrevMountButton < PreviousButton MainWindow !text: tr('Select Outfit') - size: 338 375 + size: 338 355 @onEnter: modules.game_outfit.accept() @onEscape: modules.game_outfit.destroy() @@ -158,24 +158,21 @@ MainWindow num-columns: 19 num-lines: 7 - // Action Button Section + HorizontalSeparator + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: next.top + margin-bottom: 5 + margin-top: 5 Button id: randomizeButton !text: tr('Randomize') !tooltip: tr('Randomize characters outfit') width: 75 - anchors.left: prev.left - anchors.top: prev.bottom - margin-right: 16 - @onClick: modules.game_outfit.randomize() - - HorizontalSeparator anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: next.top - margin-bottom: 10 - margin-top: 5 + anchors.bottom: parent.bottom + @onClick: modules.game_outfit.randomize() Button id: outfitOkButton diff --git a/modules/game_questlog/questlog.lua b/modules/game_questlog/questlog.lua index 8b8973d..f30c6f7 100644 --- a/modules/game_questlog/questlog.lua +++ b/modules/game_questlog/questlog.lua @@ -4,9 +4,11 @@ questLineWindow = nil function init() g_ui.importStyle('questlogwindow') g_ui.importStyle('questlinewindow') - - questLogButton = modules.client_topmenu.addLeftGameButton('questLogButton', tr('Quest Log'), '/images/topbuttons/questlog', function() g_game.requestQuestLog() end, false, 8) - + + if not g_app.isMobile() then + questLogButton = modules.client_topmenu.addLeftGameButton('questLogButton', tr('Quest Log'), '/images/topbuttons/questlog', function() g_game.requestQuestLog() end, false, 8) + end + connect(g_game, { onQuestLog = onGameQuestLog, onQuestLine = onGameQuestLine, onGameEnd = destroyWindows}) @@ -18,7 +20,9 @@ function terminate() onGameEnd = destroyWindows}) destroyWindows() - questLogButton:destroy() + if questLogButton then + questLogButton:destroy() + end end function destroyWindows() diff --git a/modules/game_questlog/questlogwindow.otui b/modules/game_questlog/questlogwindow.otui index cb5df91..c173c16 100644 --- a/modules/game_questlog/questlogwindow.otui +++ b/modules/game_questlog/questlogwindow.otui @@ -23,6 +23,8 @@ QuestLogWindow < MainWindow !text: tr('Quest Log') size: 500 400 @onEscape: self:destroy() + $mobile: + size: 500 350 TextList id: questList diff --git a/modules/game_shop/shop.lua b/modules/game_shop/shop.lua index 5d1b8fc..18c50d7 100644 --- a/modules/game_shop/shop.lua +++ b/modules/game_shop/shop.lua @@ -530,8 +530,8 @@ function buyConfirmed() sendAction("buy", selectedOffer) if g_game.getFeature(GameIngameStore) and selectedOffer.id and not otcv8shop then local offerName = selectedOffer.title:lower() - if string.find(offerName, "name") and string.find(offerName, "change") and modules.game_textedit then - modules.game_textedit.singlelineEditor("", function(newName) + if string.find(offerName, "name") and string.find(offerName, "change") and modules.client_textedit then + modules.client_textedit.singlelineEditor("", function(newName) if newName:len() == 0 then return end diff --git a/modules/game_shop/shop.otui b/modules/game_shop/shop.otui index 9445af0..e70bd41 100644 --- a/modules/game_shop/shop.otui +++ b/modules/game_shop/shop.otui @@ -39,7 +39,6 @@ ShopCategoryCreature < ShopCategory margin-bottom: 2 margin-left: 2 size: 32 32 - raw: true ShopCategoryImage < ShopCategory Label @@ -118,7 +117,6 @@ ShopOfferCreature < ShopOffer margin-bottom: 4 margin-left: 2 size: 48 48 - raw: true ShopOfferImage < ShopOffer Label diff --git a/modules/game_walking/walking.lua b/modules/game_walking/walking.lua index 63f756b..15ec37b 100644 --- a/modules/game_walking/walking.lua +++ b/modules/game_walking/walking.lua @@ -375,8 +375,11 @@ function walk(dir, ticks) elseif player:isServerWalking() then g_game.stop() return + elseif not toTile then + player:lockWalk(100) -- bug fix for missing stairs down on map + return else - player:lockWalk(200) + return -- not walkable tile end end diff --git a/modules/gamelib/gamelib.otmod b/modules/gamelib/gamelib.otmod index ceb9523..ab71364 100644 --- a/modules/gamelib/gamelib.otmod +++ b/modules/gamelib/gamelib.otmod @@ -4,10 +4,6 @@ Module author: OTClient team website: https://github.com/edubart/otclient - dependencies: - - game_features - - game_things - @onLoad: | dofile 'const' dofile 'util' diff --git a/modules/gamelib/protocollogin.lua b/modules/gamelib/protocollogin.lua index bfaad7a..85206b3 100644 --- a/modules/gamelib/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -86,6 +86,13 @@ function ProtocolLogin:sendLoginPacket() if self.getLoginExtendedData then local data = self:getLoginExtendedData() msg:addString(data) + else + msg:addString("OTCv8") + local version = g_app.getVersion():split(" ")[1]:gsub("%.", "") + if version:len() == 2 then + version = version .. "0" + end + msg:addU16(tonumber(version)) end local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset) diff --git a/modules/gamelib/ui/uiitem.lua b/modules/gamelib/ui/uiitem.lua index 089a81b..1c6b1c8 100644 --- a/modules/gamelib/ui/uiitem.lua +++ b/modules/gamelib/ui/uiitem.lua @@ -26,8 +26,11 @@ function UIItem:onDrop(widget, mousePos, forced) if not item or not item:isItem() then return false end if self.selectable then - self:setItem(Item.create(item:getId(), item:getCountOrSubType())) - return + if item:isPickupable() then + self:setItem(Item.create(item:getId(), item:getCountOrSubType())) + return true + end + return false end local toPos = self.position @@ -85,7 +88,7 @@ function UIItem:onMouseRelease(mousePosition, mouseButton) local item = self:getItem() if not item or not self:containsPoint(mousePosition) then return false end - if modules.client_options.getOption('classicControl') and + if modules.client_options.getOption('classicControl') and not g_app.isMobile() and ((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton) or (g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then g_game.look(item) diff --git a/modules/updater/updater.lua b/modules/updater/updater.lua new file mode 100644 index 0000000..2a80ccb --- /dev/null +++ b/modules/updater/updater.lua @@ -0,0 +1,276 @@ +Updater = { } + +Updater.maxRetries = 5 + +--[[ + +]]-- + +local updaterWindow +local loadModulesFunction +local scheduledEvent +local httpOperationId = 0 + +local function onLog(level, message, time) + if level == LogError then + Updater.error(message) + g_logger.setOnLog(nil) + end +end + +local function initAppWindow() + 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) + g_window.resize(size) + + -- window position, default is the screen center + local displaySize = g_window.getDisplaySize() + local defaultPos = { x = (displaySize.width - size.width)/2, + y = (displaySize.height - size.height)/2 } + local pos = g_settings.getPoint('window-pos', defaultPos) + pos.x = math.max(pos.x, 0) + pos.y = math.max(pos.y, 0) + g_window.move(pos) + + -- window maximized? + local maximized = g_settings.getBoolean('window-maximized', false) + if maximized then g_window.maximize() end + + g_window.setTitle(g_app.getName()) + g_window.setIcon('/images/clienticon') + + if g_app.isMobile() then + scheduleEvent(function() + g_app.scale(5.0) + end, 10) + end +end + +local function loadModules() + if loadModulesFunction then + local tmpLoadFunc = loadModulesFunction + loadModulesFunction = nil + tmpLoadFunc() + end +end + +local function downloadFiles(url, files, index, retries, doneCallback) + local entry = files[index] + if not entry then -- finished + return doneCallback() + end + local file = entry[1] + local file_checksum = entry[2] + + if retries > 0 then + updaterWindow.downloadStatus:setText(tr("Downloading (%i retry):\n%s", retries, file)) + else + updaterWindow.downloadStatus:setText(tr("Downloading:\n%s", file)) + end + updaterWindow.downloadProgress:setPercent(0) + updaterWindow.mainProgress:setPercent(math.floor(100 * index / #files)) + + httpOperationId = HTTP.download(url .. file, file, + function (file, checksum, err) + if not err and checksum ~= file_checksum then + err = "Invalid checksum of: " .. file .. ".\nShould be " .. file_checksum .. ", is: " .. checksum + end + if err then + if retries >= Updater.maxRetries then + Updater.error("Can't download file: " .. file .. ".\nError: " .. err) + else + scheduledEvent = scheduleEvent(function() + downloadFiles(url, files, index, retries + 1, doneCallback) + end, 250) + end + return + end + downloadFiles(url, files, index + 1, 0, doneCallback) + end, + function (progress, speed) + updaterWindow.downloadProgress:setPercent(progress) + updaterWindow.downloadProgress:setText(speed .. " kbps") + end) +end + +local function updateFiles(data, keepCurrentFiles) + if type(data) ~= "table" then + return Updater.error("Invalid data from updater api (not table)") + end + if type(data["error"]) == 'string' and data["error"]:len() > 0 then + return Updater.error(data["error"]) + end + if not data["files"] or type(data["url"]) ~= 'string' or data["url"]:len() < 4 then + return Updater.error("Invalid data from updater api: " .. json.encode(data, 2)) + end + if data["keepFiles"] then + keepCurrentFiles = true + end + + local newFiles = false + local finalFiles = {} + local localFiles = g_resources.filesChecksums() + local toUpdate = {} + -- keep all files or files from data/things + for file, checksum in pairs(localFiles) do + if keepCurrentFiles or string.find(file, "data/things") then + table.insert(finalFiles, file) + end + end + -- update files + for file, checksum in pairs(data["files"]) do + table.insert(finalFiles, file) + if not localFiles[file] or localFiles[file] ~= checksum then + table.insert(toUpdate, {file, checksum}) + newFiles = true + end + end + -- update binary + local binary = nil + if type(data["binary"]) == "table" and data["binary"]["file"]:len() > 1 then + local selfChecksum = g_resources.selfChecksum() + if selfChecksum:len() > 0 and selfChecksum ~= data["binary"]["checksum"] then + binary = data["binary"]["file"] + table.insert(toUpdate, {binary, data["binary"]["checksum"]}) + end + end + + if #toUpdate == 0 then -- nothing to update + updaterWindow.mainProgress:setPercent(100) + scheduledEvent = scheduleEvent(Updater.abort, 20) + return + end + + -- update of some files require full client restart + local forceRestart = false + local reloadModules = false + local forceRestartPattern = {"init.lua", "corelib", "updater", "otmod"} + for _, file in ipairs(toUpdate) do + for __, pattern in ipairs(forceRestartPattern) do + if string.find(file[1], pattern) then + forceRestart = true + end + if not string.find(file[1], "data/things") then + reloadModules = true + end + end + end + + updaterWindow.status:setText(tr("Updating %i files", #toUpdate)) + updaterWindow.mainProgress:setPercent(0) + updaterWindow.downloadProgress:setPercent(0) + updaterWindow.downloadProgress:show() + updaterWindow.downloadStatus:show() + updaterWindow.changeUrlButton:hide() + downloadFiles(data["url"], toUpdate, 1, 0, function() + updaterWindow.status:setText(tr("Updating client (may take few seconds)")) + updaterWindow.mainProgress:setPercent(100) + updaterWindow.downloadProgress:hide() + updaterWindow.downloadStatus:hide() + scheduledEvent = scheduleEvent(function() + local restart = binary or (not loadModulesFunction and reloadModules) or forceRestart + if newFiles then + g_resources.updateData(finalFiles, not restart) + end + if binary then + g_resources.updateExecutable(binary) + end + if restart then + g_app.restart() + else + if reloadModules then + g_modules.reloadModules() + end + Updater.abort() + end + end, 100) + end) +end + +-- public functions +function Updater.init(loadModulesFunc) + g_logger.setOnLog(onLog) + loadModulesFunction = loadModulesFunc + initAppWindow() + Updater.check() +end + +function Updater.terminate() + loadModulesFunction = nil + Updater.abort() +end + +function Updater.abort() + HTTP.cancel(httpOperationId) + removeEvent(scheduledEvent) + if updaterWindow then + updaterWindow:destroy() + updaterWindow = nil + end + loadModules() +end + +function Updater.check(args) + if updaterWindow then return end + + updaterWindow = g_ui.displayUI('updater') + updaterWindow:show() + updaterWindow:focus() + updaterWindow:raise() + + local updateData = nil + local function progressUpdater(value) + removeEvent(scheduledEvent) + if value == 100 then + return Updater.error(tr("Timeout")) + end + if updateData and (value > 60 or (not g_app.isMobile() or not ALLOW_CUSTOM_SERVERS or not loadModulesFunc)) then -- gives 3s to set custom updater for mobile version + return updateFiles(updateData) + end + scheduledEvent = scheduleEvent(function() progressUpdater(value + 1) end, 50) + updaterWindow.mainProgress:setPercent(value) + end + progressUpdater(0) + + httpOperationId = HTTP.postJSON(Services.updater, { + version = APP_VERSION, + build = g_app.getVersion(), + os = g_app.getOs(), + platform = g_window.getPlatformType(), + args = args or {} + }, function(data, err) + if err then + return Updater.error(err) + end + updateData = data + end) +end + +function Updater.error(message) + removeEvent(scheduledEvent) + if not updaterWindow then return end + displayErrorBox(tr("Updater Error"), message).onOk = function() + Updater.abort() + end +end + +function Updater.changeUrl() + removeEvent(scheduledEvent) + modules.client_textedit.edit(Services.updater, {title="Enter updater url", width=300}, function(newUrl) + if updaterWindow and newUrl:len() > 4 then + Services.updater = newUrl + end + if updaterWindow then + updaterWindow:destroy() + updaterWindow = nil + end + Updater.check() + end) +end \ No newline at end of file diff --git a/modules/client_updater/updater.otmod b/modules/updater/updater.otmod similarity index 57% rename from modules/client_updater/updater.otmod rename to modules/updater/updater.otmod index 89f4b2b..85d4959 100644 --- a/modules/client_updater/updater.otmod +++ b/modules/updater/updater.otmod @@ -1,9 +1,12 @@ Module - name: client_updater + name: updater description: Updates client author: otclient@otclient.ovh website: otclient.ovh reloadable: false scripts: [ updater ] - @onLoad: Updater.init() + dependencies: [ client_locales, client_styles, client_textedit ] @onUnload: Updater.terminate() + + load-later: + - client_background diff --git a/modules/updater/updater.otui b/modules/updater/updater.otui new file mode 100644 index 0000000..ddb9f95 --- /dev/null +++ b/modules/updater/updater.otui @@ -0,0 +1,53 @@ +StaticMainWindow + id: updaterWindow + !text: tr('Updater') + width: 350 + + layout: + type: verticalBox + fit-children: true + + Label + id: status + !text: tr('Checking for updates') + text-align: center + + ProgressBar + id: mainProgress + height: 15 + background-color: #4444ff + margin-bottom: 5 + margin-top: 5 + + Label + id: downloadStatus + text-align: center + text-auto-resize: true + text-wrap: true + visible: false + + ProgressBar + id: downloadProgress + height: 15 + background-color: #4444ff + margin-bottom: 5 + margin-top: 5 + visible: false + + Button + id: changeUrlButton + margin-left: 50 + margin-right: 50 + margin-top: 5 + margin-bottom: 10 + !text: tr('Change updater URL') + @onClick: Updater.changeUrl() + $!mobile: + visible: false + + Button + margin-left: 90 + margin-right: 90 + !text: tr('Cancel') + @onClick: Updater.abort() + diff --git a/otclient_dx.exe b/otclient_dx.exe index 1cace49..2ff9eb2 100644 Binary files a/otclient_dx.exe and b/otclient_dx.exe differ diff --git a/otclient_gl.exe b/otclient_gl.exe index 8ed210b..142587e 100644 Binary files a/otclient_gl.exe and b/otclient_gl.exe differ diff --git a/otclient_linux b/otclient_linux index 9347d3f..3583833 100644 Binary files a/otclient_linux and b/otclient_linux differ diff --git a/otclientv8.apk b/otclientv8.apk new file mode 100644 index 0000000..1d91d7d Binary files /dev/null and b/otclientv8.apk differ diff --git a/pdb/pdb.7z b/pdb/pdb.7z index 439c5c0..ae99456 100644 Binary files a/pdb/pdb.7z and b/pdb/pdb.7z differ