diff --git a/.dockerignore b/.dockerignore
index 2f1255d2c0..f46d814c7d 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,2 +1,12 @@
+/.git/
+/.github/
+/.idea/
+/.vs/
+/android/
/build/
+/cmake-build*
+/vc17/
Dockerfile
+*.dll
+*.exe
+*.pdb
diff --git a/.github/workflows/analysis-sonarcloud.yml b/.github/workflows/analysis-sonarcloud.yml
index 6ae1659c6a..9555b18a66 100644
--- a/.github/workflows/analysis-sonarcloud.yml
+++ b/.github/workflows/analysis-sonarcloud.yml
@@ -79,6 +79,7 @@ jobs:
with:
vcpkgGitURL: "https://github.com/microsoft/vcpkg.git"
vcpkgGitCommitId: ${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}
+ vcpkgJsonIgnores: "['**/vcpkg/**', '**/browser/overlay-ports/**']"
- name: Install sonar-scanner
uses: SonarSource/sonarcloud-github-c-cpp@v1
diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml
index a116a8e4b2..448140cd98 100644
--- a/.github/workflows/build-ubuntu.yml
+++ b/.github/workflows/build-ubuntu.yml
@@ -52,6 +52,7 @@ jobs:
uses: lukka/run-vcpkg@main
with:
vcpkgGitCommitId: ${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}
+ vcpkgJsonIgnores: "['**/vcpkg/**', '**/browser/overlay-ports/**']"
- name: Get latest CMake and ninja
uses: lukka/get-cmake@main
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index 4e4f68e7b1..0f40a87a45 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -52,6 +52,7 @@ jobs:
with:
vcpkgGitURL: "https://github.com/microsoft/vcpkg.git"
vcpkgGitCommitId: ${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}
+ vcpkgJsonIgnores: "['**/vcpkg/**', '**/browser/overlay-ports/**']"
- name: Get latest CMake and ninja
uses: lukka/get-cmake@main
diff --git a/README.md b/README.md
index 87d01e242f..27a7425a18 100644
--- a/README.md
+++ b/README.md
@@ -467,6 +467,8 @@ Beyond of it's flexibility with scripts, otclient comes with tons of other featu
- Module Shop
- Module Oufit
- Placeholder
+- UIGraph
+- keybinds
## The Mobile Project
The Mobile Project
diff --git a/browser/include/bitlib/LICENSE b/browser/include/bitlib/LICENSE
new file mode 100644
index 0000000000..bdbdbc8386
--- /dev/null
+++ b/browser/include/bitlib/LICENSE
@@ -0,0 +1,20 @@
+ bitlib
+ ------
+
+ by Reuben Thomas
+ http://luaforge.net/projects/bitlib
+
+
+bitlib is a C library for Lua 5.1 that provides bitwise operations. It
+is copyright Reuben Thomas 2000-2009, and is released under the MIT
+license, like Lua (see http://www.lua.org/copyright.html; it's
+basically the same as the BSD license). There is no warranty.
+
+Please report bugs and make suggestions to the email address above, or
+use the LuaForge trackers.
+
+Thanks to John Passaniti for his bitwise operations library, some of
+whose ideas I used, to Shmuel Zeigerman for the test suite, to
+Thatcher Ulrich for portability fixes, and to Enrico Tassi, John
+Stiles and Eduardo Ochs for bug reports.
+
diff --git a/browser/include/bitlib/bit_limits.h b/browser/include/bitlib/bit_limits.h
new file mode 100644
index 0000000000..943b2fd7e9
--- /dev/null
+++ b/browser/include/bitlib/bit_limits.h
@@ -0,0 +1,4 @@
+#define BITLIB_FLOAT_BITS 53
+#define BITLIB_FLOAT_MAX 0xfffffffffffffL
+#define BITLIB_FLOAT_MIN (-0x10000000000000L)
+#define BITLIB_FLOAT_UMAX 0x1fffffffffffffUL
diff --git a/browser/include/bitlib/lbitlib.c b/browser/include/bitlib/lbitlib.c
new file mode 100644
index 0000000000..87d736387f
--- /dev/null
+++ b/browser/include/bitlib/lbitlib.c
@@ -0,0 +1,129 @@
+/* Bitwise operations library */
+/* (c) Reuben Thomas 2000-2008 */
+/* See README for license */
+
+#include "bit_limits.h"
+
+
+/* FIXME: Assumes lua_Integer is ptrdiff_t */
+#define LUA_INTEGER_MAX PTRDIFF_MAX
+#define LUA_INTEGER_MIN PTRDIFF_MIN
+
+/* FIXME: Assumes size_t is an unsigned lua_Integer */
+typedef size_t lua_UInteger;
+#define LUA_UINTEGER_MAX SIZE_MAX
+
+
+/* Bit type size and limits */
+
+#define BIT_BITS \
+ (CHAR_BIT * sizeof(lua_Integer) > BITLIB_FLOAT_BITS ? \
+ BITLIB_FLOAT_BITS : (CHAR_BIT * sizeof(lua_Integer)))
+
+/* This code may give warnings if BITLIB_FLOAT_* are too big to fit in
+ long, but that doesn't matter since in that case they won't be
+ used. */
+#define BIT_MAX \
+ (CHAR_BIT * sizeof(lua_Integer) > BITLIB_FLOAT_BITS ? BITLIB_FLOAT_MAX : LUA_INTEGER_MAX)
+
+#define BIT_MIN \
+ (CHAR_BIT * sizeof(lua_Integer) > BITLIB_FLOAT_BITS ? BITLIB_FLOAT_MIN : LUA_INTEGER_MIN)
+
+#define BIT_UMAX \
+ (CHAR_BIT * sizeof(lua_Integer) > BITLIB_FLOAT_BITS ? BITLIB_FLOAT_UMAX : LUA_UINTEGER_MAX)
+
+
+/* Define TOBIT to get a bit value */
+#ifdef BUILTIN_CAST
+#define
+#define TOBIT(L, n, res) \
+ ((void)(res), luaL_checkinteger((L), (n)))
+#else
+#include
+#include
+
+/* FIXME: Assumes lua_Number fits in a double (use of fmod). */
+#define TOBIT(L, n, res) \
+ ((lua_Integer)(((res) = fmod(luaL_checknumber(L, (n)), (double)BIT_UMAX + 1.0)), \
+ (res) > BIT_MAX ? ((res) -= (double)BIT_UMAX, (res) -= 1) : \
+ ((res) < BIT_MIN ? ((res) += (double)BIT_UMAX, (res) += 1) : (res))))
+#endif
+
+
+#define BIT_TRUNCATE(i) \
+ ((i) & BIT_UMAX)
+
+
+/* Operations
+
+ The macros MONADIC and VARIADIC only deal with bitwise operations.
+
+ LOGICAL_SHIFT truncates its left-hand operand before shifting so
+ that any extra bits at the most-significant end are not shifted
+ into the result.
+
+ ARITHMETIC_SHIFT does not truncate its left-hand operand, so that
+ the sign bits are not removed and right shift work properly.
+ */
+
+#define MONADIC(name, op) \
+ static int bit_ ## name(lua_State *L) { \
+ lua_Number f; \
+ lua_pushinteger(L, BIT_TRUNCATE(op TOBIT(L, 1, f))); \
+ return 1; \
+ }
+
+#define VARIADIC(name, op) \
+ static int bit_ ## name(lua_State *L) { \
+ lua_Number f; \
+ int n = lua_gettop(L), i; \
+ lua_Integer w = TOBIT(L, 1, f); \
+ for (i = 2; i <= n; i++) \
+ w op TOBIT(L, i, f); \
+ lua_pushinteger(L, BIT_TRUNCATE(w)); \
+ return 1; \
+ }
+
+#define LOGICAL_SHIFT(name, op) \
+ static int bit_ ## name(lua_State *L) { \
+ lua_Number f; \
+ lua_pushinteger(L, BIT_TRUNCATE(BIT_TRUNCATE((lua_UInteger)TOBIT(L, 1, f)) op \
+ (unsigned)luaL_checknumber(L, 2))); \
+ return 1; \
+ }
+
+#define ARITHMETIC_SHIFT(name, op) \
+ static int bit_ ## name(lua_State *L) { \
+ lua_Number f; \
+ lua_pushinteger(L, BIT_TRUNCATE((lua_Integer)TOBIT(L, 1, f) op \
+ (unsigned)luaL_checknumber(L, 2))); \
+ return 1; \
+ }
+
+MONADIC(cast, +)
+MONADIC(bnot, ~)
+VARIADIC(band, &=)
+VARIADIC(bor, |=)
+VARIADIC(bxor, ^=)
+ARITHMETIC_SHIFT(lshift, <<)
+LOGICAL_SHIFT(rshift, >>)
+ARITHMETIC_SHIFT(arshift, >>)
+
+static const struct luaL_reg bitlib[] = {
+ {"cast", bit_cast},
+ {"bnot", bit_bnot},
+ {"band", bit_band},
+ {"bor", bit_bor},
+ {"bxor", bit_bxor},
+ {"lshift", bit_lshift},
+ {"rshift", bit_rshift},
+ {"arshift", bit_arshift},
+ {NULL, NULL}
+};
+
+LUALIB_API int luaopen_bit (lua_State *L) {
+ luaL_register(L, "bit", bitlib);
+ lua_pushnumber(L, BIT_BITS);
+ lua_setfield(L, -2, "bits");
+ return 1;
+}
diff --git a/browser/overlay-ports/abseil/portfile.cmake b/browser/overlay-ports/abseil/portfile.cmake
new file mode 100644
index 0000000000..5bddf86eb8
--- /dev/null
+++ b/browser/overlay-ports/abseil/portfile.cmake
@@ -0,0 +1,55 @@
+if(NOT VCPKG_TARGET_IS_WINDOWS)
+ vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
+endif()
+
+vcpkg_from_github(
+ OUT_SOURCE_PATH SOURCE_PATH
+ REPO abseil/abseil-cpp
+ REF "${VERSION}"
+ SHA512 bd2cca8f007f2eee66f51c95a979371622b850ceb2ce3608d00ba826f7c494a1da0fba3c1427728f2c173fe50d59b701da35c2c9fdad2752a5a49746b1c8ef31
+ HEAD_REF master
+ PATCHES
+ use_pthread.patch
+)
+
+# With ABSL_PROPAGATE_CXX_STD=ON abseil automatically detect if it is being
+# compiled with C++14 or C++17, and modifies the installed `absl/base/options.h`
+# header accordingly. This works even if CMAKE_CXX_STANDARD is not set. Abseil
+# uses the compiler default behavior to update `absl/base/options.h` as needed.
+set(ABSL_USE_CXX17_OPTION "")
+if("cxx17" IN_LIST FEATURES)
+ set(ABSL_USE_CXX17_OPTION "-DCMAKE_CXX_STANDARD=17")
+endif()
+
+set(ABSL_STATIC_RUNTIME_OPTION "")
+if(VCPKG_TARGET_IS_WINDOWS AND VCPKG_CRT_LINKAGE STREQUAL "static")
+ set(ABSL_STATIC_RUNTIME_OPTION "-DABSL_MSVC_STATIC_RUNTIME=ON")
+endif()
+
+vcpkg_cmake_configure(
+ SOURCE_PATH "${SOURCE_PATH}"
+ DISABLE_PARALLEL_CONFIGURE
+ OPTIONS
+ -DABSL_PROPAGATE_CXX_STD=ON
+ ${ABSL_USE_CXX17_OPTION}
+ ${ABSL_STATIC_RUNTIME_OPTION}
+)
+
+vcpkg_cmake_install()
+vcpkg_cmake_config_fixup(PACKAGE_NAME absl CONFIG_PATH lib/cmake/absl)
+vcpkg_fixup_pkgconfig()
+
+vcpkg_copy_pdbs()
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share"
+ "${CURRENT_PACKAGES_DIR}/debug/include"
+ "${CURRENT_PACKAGES_DIR}/include/absl/copts"
+ "${CURRENT_PACKAGES_DIR}/include/absl/strings/testdata"
+ "${CURRENT_PACKAGES_DIR}/include/absl/time/internal/cctz/testdata"
+)
+
+if(VCPKG_TARGET_IS_WINDOWS AND VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic")
+ vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/absl/base/config.h" "defined(ABSL_CONSUME_DLL)" "1")
+ vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/absl/base/internal/thread_identity.h" "defined(ABSL_CONSUME_DLL)" "1")
+endif()
+
+vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
diff --git a/browser/overlay-ports/abseil/use_pthread.patch b/browser/overlay-ports/abseil/use_pthread.patch
new file mode 100644
index 0000000000..12b614cc4f
--- /dev/null
+++ b/browser/overlay-ports/abseil/use_pthread.patch
@@ -0,0 +1,13 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 7c82b3a..48474ec 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -27,6 +27,8 @@ project(absl LANGUAGES CXX VERSION 20240722)
+ set(ABSL_SOVERSION "2407.0.0")
+ include(CTest)
+
++add_compile_options(-pthread)
++
+ # Output directory is correct by default for most build setups. However, when
+ # building Abseil as a DLL, it is important to have the DLL in the same
+ # directory as the executable using it. Thus, we put all executables in a single
diff --git a/browser/overlay-ports/abseil/vcpkg.json b/browser/overlay-ports/abseil/vcpkg.json
new file mode 100644
index 0000000000..b3b191d1ad
--- /dev/null
+++ b/browser/overlay-ports/abseil/vcpkg.json
@@ -0,0 +1,27 @@
+{
+ "name": "abseil",
+ "version": "20240722.0",
+ "description": [
+ "Abseil is an open-source collection of C++ library code designed to augment the C++ standard library. The Abseil library code is collected from Google's own C++ code base, has been extensively tested and used in production, and is the same code we depend on in our daily coding lives.",
+ "In some cases, Abseil provides pieces missing from the C++ standard; in others, Abseil provides alternatives to the standard for special needs we've found through usage in the Google code base. We denote those cases clearly within the library code we provide you.",
+ "Abseil is not meant to be a competitor to the standard library; we've just found that many of these utilities serve a purpose within our code base, and we now want to provide those resources to the C++ community as a whole."
+ ],
+ "homepage": "https://github.com/abseil/abseil-cpp",
+ "license": "Apache-2.0",
+ "dependencies": [
+ {
+ "name": "vcpkg-cmake",
+ "host": true
+ },
+ {
+ "name": "vcpkg-cmake-config",
+ "host": true
+ }
+ ],
+ "features": {
+ "cxx17": {
+ "description": "Enable compiler C++17."
+ }
+ },
+ "builtin-baseline":"c82f74667287d3dc386bce81e44964370c91a289"
+}
diff --git a/browser/overlay-ports/physfs/portfile.cmake b/browser/overlay-ports/physfs/portfile.cmake
new file mode 100644
index 0000000000..980df74cfa
--- /dev/null
+++ b/browser/overlay-ports/physfs/portfile.cmake
@@ -0,0 +1,45 @@
+vcpkg_minimum_required(VERSION 2022-10-12) # for ${VERSION}
+vcpkg_from_github(
+ OUT_SOURCE_PATH SOURCE_PATH
+ REPO icculus/physfs
+ REF "release-${VERSION}"
+ SHA512 e0d84d6ac6bd8f0973149a5add54ed5ed890b5fabb4592ba61b59a3b3e01c05e05f1754f18d7a1c8d72e68777a23cda0c50dc0512cf57a8310a950bf908f54b1
+ PATCHES
+ use_pthread.patch
+)
+
+string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "static" PHYSFS_STATIC)
+string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" PHYSFS_SHARED)
+
+set(generator_param "")
+if(VCPKG_TARGET_IS_UWP)
+ set(generator_param WINDOWS_USE_MSBUILD)
+endif()
+
+vcpkg_cmake_configure(
+ SOURCE_PATH "${SOURCE_PATH}"
+ ${generator_param}
+ OPTIONS
+ -DPHYSFS_BUILD_STATIC=${PHYSFS_STATIC}
+ -DPHYSFS_BUILD_SHARED=${PHYSFS_SHARED}
+ -DPHYSFS_BUILD_TEST=OFF
+ -DPHYSFS_BUILD_DOCS=OFF
+)
+
+vcpkg_cmake_install()
+vcpkg_copy_pdbs()
+
+vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/PhysFS)
+vcpkg_fixup_pkgconfig()
+
+if(PHYSFS_STATIC)
+ vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/physfs.h" "defined(PHYSFS_STATIC)" "1")
+else()
+ vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/physfs.h" "dllexport" "dllimport")
+endif()
+
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
+
+file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
+file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
+file(INSTALL "${SOURCE_PATH}/LICENSE.txt" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
diff --git a/browser/overlay-ports/physfs/usage b/browser/overlay-ports/physfs/usage
new file mode 100644
index 0000000000..39a71fd090
--- /dev/null
+++ b/browser/overlay-ports/physfs/usage
@@ -0,0 +1,10 @@
+physfs provides CMake targets:
+
+ find_package(PhysFS CONFIG REQUIRED)
+ target_link_libraries(main PRIVATE $,PhysFS::PhysFS,PhysFS::PhysFS-static>)
+
+physfs is compatible with built-in CMake targets:
+
+ find_package(PhysFS REQUIRED)
+ target_include_directories(main PRIVATE ${PHYSFS_INCLUDE_DIR})
+ target_link_libraries(main PRIVATE ${PHYSFS_LIBRARY})
diff --git a/browser/overlay-ports/physfs/use_pthread.patch b/browser/overlay-ports/physfs/use_pthread.patch
new file mode 100644
index 0000000000..8bae3092b4
--- /dev/null
+++ b/browser/overlay-ports/physfs/use_pthread.patch
@@ -0,0 +1,13 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index b3291cc..a5d8225 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -25,6 +25,8 @@ set(PHYSFS_CPP_SRCS)
+
+ # I hate that they define "WIN32" ... we're about to move to Win64...I hope!
+
++add_compile_options(-pthread)
++
+ if(APPLE)
+ set(OTHER_LDFLAGS ${OTHER_LDFLAGS} "-framework IOKit -framework Foundation")
+ list(APPEND PHYSFS_M_SRCS src/physfs_platform_apple.m)
diff --git a/browser/overlay-ports/physfs/vcpkg-cmake-wrapper.cmake b/browser/overlay-ports/physfs/vcpkg-cmake-wrapper.cmake
new file mode 100644
index 0000000000..945206048b
--- /dev/null
+++ b/browser/overlay-ports/physfs/vcpkg-cmake-wrapper.cmake
@@ -0,0 +1,6 @@
+find_library(PHYSFS_LIBRARY_RELEASE NAMES physfs physfs-static NAMES_PER_DIR PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib" NO_DEFAULT_PATH)
+find_library(PHYSFS_LIBRARY_DEBUG NAMES physfs physfs-static NAMES_PER_DIR PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/lib" NO_DEFAULT_PATH)
+include(SelectLibraryConfigurations)
+select_library_configurations(PHYSFS)
+unset(PHYSFS_FOUND)
+_find_package(${ARGS})
diff --git a/browser/overlay-ports/physfs/vcpkg.json b/browser/overlay-ports/physfs/vcpkg.json
new file mode 100644
index 0000000000..4147529ac9
--- /dev/null
+++ b/browser/overlay-ports/physfs/vcpkg.json
@@ -0,0 +1,19 @@
+{
+ "name": "physfs",
+ "version-semver": "3.2.0",
+ "port-version": 1,
+ "description": "a library to provide abstract access to various archives",
+ "homepage": "https://icculus.org/physfs/",
+ "license": "Zlib",
+ "dependencies": [
+ {
+ "name": "vcpkg-cmake",
+ "host": true
+ },
+ {
+ "name": "vcpkg-cmake-config",
+ "host": true
+ }
+ ],
+ "builtin-baseline":"c82f74667287d3dc386bce81e44964370c91a289"
+}
diff --git a/browser/overlay-ports/protobuf/fix-arm64-msvc.patch b/browser/overlay-ports/protobuf/fix-arm64-msvc.patch
new file mode 100644
index 0000000000..2a13f1b77f
--- /dev/null
+++ b/browser/overlay-ports/protobuf/fix-arm64-msvc.patch
@@ -0,0 +1,22 @@
+diff --git a/src/google/protobuf/parse_context.h b/src/google/protobuf/parse_context.h
+index df12ee1ab..3eb2e56c7 100644
+--- a/src/google/protobuf/parse_context.h
++++ b/src/google/protobuf/parse_context.h
+@@ -653,7 +653,7 @@ inline const char* VarintParseSlow(const char* p, uint32_t res, uint64_t* out) {
+ return tmp.first;
+ }
+
+-#ifdef __aarch64__
++#if defined(__aarch64__) && !defined(_MSC_VER)
+ // Generally, speaking, the ARM-optimized Varint decode algorithm is to extract
+ // and concatenate all potentially valid data bits, compute the actual length
+ // of the Varint, and mask off the data bits which are not actually part of the
+@@ -883,7 +883,7 @@ static const char* VarintParseSlowArm(const char* p, uint64_t* out,
+
+ template
+ PROTOBUF_NODISCARD const char* VarintParse(const char* p, T* out) {
+-#if defined(__aarch64__) && defined(PROTOBUF_LITTLE_ENDIAN)
++#if defined(__aarch64__) && defined(PROTOBUF_LITTLE_ENDIAN) && !defined(_MSC_VER)
+ // This optimization is not supported in big endian mode
+ uint64_t first8;
+ std::memcpy(&first8, p, sizeof(first8));
diff --git a/browser/overlay-ports/protobuf/fix-default-proto-file-path.patch b/browser/overlay-ports/protobuf/fix-default-proto-file-path.patch
new file mode 100644
index 0000000000..c30abeb9ba
--- /dev/null
+++ b/browser/overlay-ports/protobuf/fix-default-proto-file-path.patch
@@ -0,0 +1,21 @@
+diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
+index f9e9666..d453a4c 100644
+--- a/src/google/protobuf/compiler/command_line_interface.cc
++++ b/src/google/protobuf/compiler/command_line_interface.cc
+@@ -280,12 +280,15 @@ void AddDefaultProtoPaths(
+ paths->emplace_back("", std::move(include_path));
+ return;
+ }
+- // Check if the upper level directory has an "include" subdirectory.
++ // change "'$/bin' is next to 'include'" assumption to "'$/bin/tools' is next to 'include'"
++ for (int i = 0; i < 2; i++)
++ {
+ pos = path.find_last_of("/\\");
+ if (pos == std::string::npos || pos == 0) {
+ return;
+ }
+ path = path.substr(0, pos);
++ }
+ include_path = absl::StrCat(path, "/include");
+ if (IsInstalledProtoPath(include_path)) {
+ paths->emplace_back("", std::move(include_path));
diff --git a/browser/overlay-ports/protobuf/fix-static-build.patch b/browser/overlay-ports/protobuf/fix-static-build.patch
new file mode 100644
index 0000000000..e0ea7fd095
--- /dev/null
+++ b/browser/overlay-ports/protobuf/fix-static-build.patch
@@ -0,0 +1,21 @@
+diff --git a/cmake/install.cmake b/cmake/install.cmake
+index 998c2e31a..233f9e400 100644
+--- a/cmake/install.cmake
++++ b/cmake/install.cmake
+@@ -49,7 +49,7 @@ if (protobuf_BUILD_PROTOC_BINARIES)
+ install(TARGETS protoc EXPORT protobuf-targets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT protoc
+ BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT protoc)
+- if (UNIX AND NOT APPLE)
++ if (UNIX AND NOT APPLE AND NOT protobuf_MSVC_STATIC_RUNTIME)
+ set_property(TARGET protoc
+ PROPERTY INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}")
+ elseif (APPLE)
+@@ -68,7 +68,6 @@ set(protobuf_HEADERS
+ ${cpp_features_proto_proto_srcs}
+ ${descriptor_proto_proto_srcs}
+ ${plugin_proto_proto_srcs}
+- ${java_features_proto_proto_srcs}
+ )
+ foreach(_header ${protobuf_HEADERS})
+ string(FIND ${_header} "${protobuf_SOURCE_DIR}/src" _find_src)
diff --git a/browser/overlay-ports/protobuf/fix-utf8-range.patch b/browser/overlay-ports/protobuf/fix-utf8-range.patch
new file mode 100644
index 0000000000..d2a4600ec7
--- /dev/null
+++ b/browser/overlay-ports/protobuf/fix-utf8-range.patch
@@ -0,0 +1,48 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 4137ce2e9..f1289e08a 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -294,6 +294,7 @@ endif (protobuf_BUILD_TESTS)
+ include(${protobuf_SOURCE_DIR}/cmake/abseil-cpp.cmake)
+
+ if (protobuf_BUILD_PROTOBUF_BINARIES)
++ find_package(utf8_range CONFIG REQUIRED)
+ include(${protobuf_SOURCE_DIR}/cmake/utf8_range.cmake)
+ include(${protobuf_SOURCE_DIR}/cmake/libprotobuf-lite.cmake)
+ if (NOT DEFINED protobuf_LIB_PROTOBUF_LITE)
+diff --git a/cmake/libprotobuf-lite.cmake b/cmake/libprotobuf-lite.cmake
+index f343458cf..f4b1e0faa 100644
+--- a/cmake/libprotobuf-lite.cmake
++++ b/cmake/libprotobuf-lite.cmake
+@@ -42,4 +42,4 @@ set_target_properties(libprotobuf-lite PROPERTIES
+ )
+ add_library(protobuf::libprotobuf-lite ALIAS libprotobuf-lite)
+
+-target_link_libraries(libprotobuf-lite PRIVATE utf8_validity)
++target_link_libraries(libprotobuf-lite PRIVATE utf8_range::utf8_validity)
+diff --git a/cmake/libprotobuf.cmake b/cmake/libprotobuf.cmake
+index 422754a1a..fa9956685 100644
+--- a/cmake/libprotobuf.cmake
++++ b/cmake/libprotobuf.cmake
+@@ -45,4 +45,4 @@ set_target_properties(libprotobuf PROPERTIES
+ )
+ add_library(protobuf::libprotobuf ALIAS libprotobuf)
+
+-target_link_libraries(libprotobuf PRIVATE utf8_validity)
++target_link_libraries(libprotobuf PRIVATE utf8_range::utf8_validity)
+diff --git a/cmake/utf8_range.cmake b/cmake/utf8_range.cmake
+index f411a8c5b..21bf8235b 100644
+--- a/cmake/utf8_range.cmake
++++ b/cmake/utf8_range.cmake
+@@ -1,4 +1,4 @@
+-if (NOT TARGET utf8_range)
++if (0)
+ set(utf8_range_ENABLE_TESTS OFF CACHE BOOL "Disable utf8_range tests")
+
+ if (NOT EXISTS "${protobuf_SOURCE_DIR}/third_party/utf8_range/CMakeLists.txt")
+@@ -12,4 +12,4 @@ if (NOT TARGET utf8_range)
+ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third_party/utf8_range)
+ endif ()
+
+-set(_protobuf_FIND_UTF8_RANGE "if(NOT TARGET utf8_range)\n find_package(utf8_range CONFIG)\nendif()")
++set(_protobuf_FIND_UTF8_RANGE "if(NOT TARGET utf8_range::utf8_range)\n find_package(utf8_range CONFIG)\nendif()")
diff --git a/browser/overlay-ports/protobuf/portfile.cmake b/browser/overlay-ports/protobuf/portfile.cmake
new file mode 100644
index 0000000000..fdc2e7ba94
--- /dev/null
+++ b/browser/overlay-ports/protobuf/portfile.cmake
@@ -0,0 +1,136 @@
+vcpkg_from_github(
+ OUT_SOURCE_PATH SOURCE_PATH
+ REPO protocolbuffers/protobuf
+ REF "v${VERSION}"
+ SHA512 ce81add9d978a6b63d4205715eac5084e81a6753da1f6c6bad6493e60253215901bffc4a60d704a873333f2b9f94fd86cb7eb5b293035f2268c12692bd808bac
+ HEAD_REF master
+ PATCHES
+ use_pthread.patch
+ fix-static-build.patch
+ fix-default-proto-file-path.patch
+ fix-utf8-range.patch
+ fix-arm64-msvc.patch
+)
+
+string(COMPARE EQUAL "${TARGET_TRIPLET}" "${HOST_TRIPLET}" protobuf_BUILD_PROTOC_BINARIES)
+string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" protobuf_BUILD_SHARED_LIBS)
+string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" protobuf_MSVC_STATIC_RUNTIME)
+
+vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
+ FEATURES
+ zlib protobuf_WITH_ZLIB
+)
+
+if(VCPKG_TARGET_IS_UWP)
+ set(protobuf_BUILD_LIBPROTOC OFF)
+else()
+ set(protobuf_BUILD_LIBPROTOC ON)
+endif()
+
+if (VCPKG_DOWNLOAD_MODE)
+ # download PKGCONFIG in download mode which is used in `vcpkg_fixup_pkgconfig()` at the end of this script.
+ # download it here because `vcpkg_cmake_configure()` halts execution in download mode when running configure process.
+ vcpkg_find_acquire_program(PKGCONFIG)
+endif()
+
+# Delete language backends we aren't targeting to reduce false positives in automated dependency
+# detectors like Dependabot.
+file(REMOVE_RECURSE
+ "${SOURCE_PATH}/csharp"
+ "${SOURCE_PATH}/java"
+ "${SOURCE_PATH}/lua"
+ "${SOURCE_PATH}/objectivec"
+ "${SOURCE_PATH}/php"
+ "${SOURCE_PATH}/python"
+ "${SOURCE_PATH}/ruby"
+ "${SOURCE_PATH}/rust"
+)
+
+vcpkg_cmake_configure(
+ SOURCE_PATH "${SOURCE_PATH}"
+ OPTIONS
+ -Dprotobuf_BUILD_SHARED_LIBS=${protobuf_BUILD_SHARED_LIBS}
+ -Dprotobuf_MSVC_STATIC_RUNTIME=${protobuf_MSVC_STATIC_RUNTIME}
+ -Dprotobuf_BUILD_TESTS=OFF
+ -DCMAKE_INSTALL_CMAKEDIR:STRING=share/protobuf
+ -Dprotobuf_BUILD_PROTOC_BINARIES=${protobuf_BUILD_PROTOC_BINARIES}
+ -Dprotobuf_BUILD_LIBPROTOC=${protobuf_BUILD_LIBPROTOC}
+ -Dprotobuf_ABSL_PROVIDER=package
+ ${FEATURE_OPTIONS}
+)
+
+vcpkg_cmake_install()
+
+if(protobuf_BUILD_PROTOC_BINARIES)
+ if(VCPKG_TARGET_IS_WINDOWS)
+ vcpkg_copy_tools(TOOL_NAMES protoc AUTO_CLEAN)
+ else()
+ string(REPLACE "." ";" VERSION_LIST ${VERSION})
+ list(GET VERSION_LIST 1 VERSION_MINOR)
+ list(GET VERSION_LIST 2 VERSION_PATCH)
+ vcpkg_copy_tools(TOOL_NAMES protoc protoc-${VERSION_MINOR}.${VERSION_PATCH}.0 AUTO_CLEAN)
+ endif()
+else()
+ file(COPY "${CURRENT_HOST_INSTALLED_DIR}/tools/${PORT}" DESTINATION "${CURRENT_PACKAGES_DIR}/tools")
+endif()
+
+vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/share/${PORT}/protobuf-config.cmake"
+ "if(protobuf_MODULE_COMPATIBLE)"
+ "if(1)"
+)
+if(NOT protobuf_BUILD_LIBPROTOC)
+ vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/share/${PORT}/protobuf-module.cmake"
+ "_protobuf_find_libraries(Protobuf_PROTOC protoc)"
+ ""
+ )
+endif()
+
+vcpkg_cmake_config_fixup()
+
+if(VCPKG_LIBRARY_LINKAGE STREQUAL "dynamic")
+ vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/google/protobuf/port_def.inc"
+ "\#ifdef PROTOBUF_PORT_"
+ "\#ifndef PROTOBUF_USE_DLLS\n\#define PROTOBUF_USE_DLLS\n\#endif // PROTOBUF_USE_DLLS\n\n\#ifdef PROTOBUF_PORT_"
+ )
+endif()
+
+vcpkg_copy_pdbs()
+
+function(replace_package_string package)
+ set(debug_file "${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/${package}.pc")
+ set(release_file "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/${package}.pc")
+
+ if(EXISTS "${release_file}")
+ vcpkg_replace_string("${release_file}" "absl_abseil_dll" "abseil_dll" IGNORE_UNCHANGED)
+ if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
+ vcpkg_replace_string("${release_file}" "-l${package}" "-llib${package}" IGNORE_UNCHANGED)
+ endif()
+ endif()
+
+ if(EXISTS "${debug_file}")
+ vcpkg_replace_string("${debug_file}" "absl_abseil_dll" "abseil_dll" IGNORE_UNCHANGED)
+ if(VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW)
+ vcpkg_replace_string("${debug_file}" "-l${package}" "-llib${package}d" IGNORE_UNCHANGED)
+ else()
+ vcpkg_replace_string("${debug_file}" "-l${package}" "-l${package}d" IGNORE_UNCHANGED)
+ endif()
+ endif()
+endfunction()
+
+set(packages protobuf protobuf-lite)
+foreach(package IN LISTS packages)
+ replace_package_string("${package}")
+endforeach()
+
+
+vcpkg_fixup_pkgconfig()
+
+if(NOT protobuf_BUILD_PROTOC_BINARIES)
+ configure_file("${CMAKE_CURRENT_LIST_DIR}/protobuf-targets-vcpkg-protoc.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/protobuf-targets-vcpkg-protoc.cmake" COPYONLY)
+endif()
+
+configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-cmake-wrapper.cmake" @ONLY)
+
+file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share" "${CURRENT_PACKAGES_DIR}/debug/include")
+
+vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE")
diff --git a/browser/overlay-ports/protobuf/protobuf-targets-vcpkg-protoc.cmake b/browser/overlay-ports/protobuf/protobuf-targets-vcpkg-protoc.cmake
new file mode 100644
index 0000000000..149e6587ee
--- /dev/null
+++ b/browser/overlay-ports/protobuf/protobuf-targets-vcpkg-protoc.cmake
@@ -0,0 +1,8 @@
+# Create imported target protobuf::protoc
+add_executable(protobuf::protoc IMPORTED)
+
+# Import target "protobuf::protoc" for configuration "Release"
+set_property(TARGET protobuf::protoc APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+set_target_properties(protobuf::protoc PROPERTIES
+ IMPORTED_LOCATION_RELEASE "${Protobuf_PROTOC_EXECUTABLE}"
+)
diff --git a/browser/overlay-ports/protobuf/use_pthread.patch b/browser/overlay-ports/protobuf/use_pthread.patch
new file mode 100644
index 0000000000..7dad46efe4
--- /dev/null
+++ b/browser/overlay-ports/protobuf/use_pthread.patch
@@ -0,0 +1,13 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 7ca1b21..0984f19 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -23,6 +23,8 @@ if(protobuf_DEPRECATED_CMAKE_SUBDIRECTORY_USAGE)
+ get_filename_component(protobuf_SOURCE_DIR ${protobuf_SOURCE_DIR} DIRECTORY)
+ endif()
+
++add_compile_options(-pthread)
++
+ # Options
+ option(protobuf_INSTALL "Install protobuf binaries and files" ON)
+ option(protobuf_BUILD_TESTS "Build tests" ON)
diff --git a/browser/overlay-ports/protobuf/vcpkg-cmake-wrapper.cmake b/browser/overlay-ports/protobuf/vcpkg-cmake-wrapper.cmake
new file mode 100644
index 0000000000..4def9a6ea5
--- /dev/null
+++ b/browser/overlay-ports/protobuf/vcpkg-cmake-wrapper.cmake
@@ -0,0 +1,3 @@
+find_program(Protobuf_PROTOC_EXECUTABLE NAMES protoc PATHS "${CMAKE_CURRENT_LIST_DIR}/../../../@HOST_TRIPLET@/tools/protobuf" NO_DEFAULT_PATH)
+
+_find_package(${ARGS} CONFIG)
diff --git a/browser/overlay-ports/protobuf/vcpkg.json b/browser/overlay-ports/protobuf/vcpkg.json
new file mode 100644
index 0000000000..8502333a3c
--- /dev/null
+++ b/browser/overlay-ports/protobuf/vcpkg.json
@@ -0,0 +1,33 @@
+{
+ "name": "protobuf",
+ "version": "4.25.1",
+ "port-version": 1,
+ "description": "Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.",
+ "homepage": "https://github.com/protocolbuffers/protobuf",
+ "license": "BSD-3-Clause",
+ "dependencies": [
+ "abseil",
+ {
+ "name": "protobuf",
+ "host": true
+ },
+ "utf8-range",
+ {
+ "name": "vcpkg-cmake",
+ "host": true
+ },
+ {
+ "name": "vcpkg-cmake-config",
+ "host": true
+ }
+ ],
+ "features": {
+ "zlib": {
+ "description": "ZLib based features like Gzip streams",
+ "dependencies": [
+ "zlib"
+ ]
+ }
+ },
+ "builtin-baseline":"c82f74667287d3dc386bce81e44964370c91a289"
+}
diff --git a/browser/shell.html b/browser/shell.html
new file mode 100644
index 0000000000..e12cce9a1f
--- /dev/null
+++ b/browser/shell.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+ Loading
+
+
+
+
+
+
+
+ {{{ SCRIPT }}}
+
+
diff --git a/data/images/game/creatureicons/CreatureIcons.png b/data/images/game/creatureicons/CreatureIcons.png
new file mode 100644
index 0000000000..f5b50d4d15
Binary files /dev/null and b/data/images/game/creatureicons/CreatureIcons.png differ
diff --git a/data/images/game/creatureicons/monsterIcons.png b/data/images/game/creatureicons/monsterIcons.png
new file mode 100644
index 0000000000..997e44e0d8
Binary files /dev/null and b/data/images/game/creatureicons/monsterIcons.png differ
diff --git a/data/images/topbuttons/debug.png b/data/images/topbuttons/debug.png
new file mode 100644
index 0000000000..9768e4cf93
Binary files /dev/null and b/data/images/topbuttons/debug.png differ
diff --git a/data/images/ui/containerslot-coloredges.png b/data/images/ui/containerslot-coloredges.png
new file mode 100644
index 0000000000..5a6779b114
Binary files /dev/null and b/data/images/ui/containerslot-coloredges.png differ
diff --git a/data/images/ui/icon-edit.png b/data/images/ui/icon-edit.png
new file mode 100644
index 0000000000..f9b810a70d
Binary files /dev/null and b/data/images/ui/icon-edit.png differ
diff --git a/data/images/ui/rarity_frames.png b/data/images/ui/rarity_frames.png
index cebc6d963b..4c097255b9 100644
Binary files a/data/images/ui/rarity_frames.png and b/data/images/ui/rarity_frames.png differ
diff --git a/data/styles/10-items.otui b/data/styles/10-items.otui
index 719f26110d..81357f2a82 100644
--- a/data/styles/10-items.otui
+++ b/data/styles/10-items.otui
@@ -8,6 +8,22 @@ Item < UIItem
$disabled:
color: #646464
+
+ Label
+ id: duration
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ phantom: true
+ font: "verdana-11px-rounded"
+
+ Label
+ id: charges
+ anchors.top: parent.top
+ anchors.left: parent.left
+ phantom: true
+ text-auto-resize: true
+ font: "verdana-11px-rounded"
+
UIWidget
id: tier
size: 10 9
@@ -191,10 +207,18 @@ MainInventoryItem < UIWidget
color: white
Label
- id: Duration
+ id: duration
anchors.bottom: parent.bottom
anchors.left: parent.left
phantom: true
+ Label
+ id: charges
+ anchors.top: parent.top
+ anchors.left: parent.left
+ phantom: true
+ text-auto-resize: true
+ font: "verdana-11px-rounded"
+
UIWidget
id: tier
size: 10 9
diff --git a/data/styles/10-scrollbars.otui b/data/styles/10-scrollbars.otui
index 88beafb2bb..130c0a9b8b 100644
--- a/data/styles/10-scrollbars.otui
+++ b/data/styles/10-scrollbars.otui
@@ -186,3 +186,23 @@ HorizontalQtScrollBar < UIScrollBar
image-clip: 54 10 12 12
HorizontalScrollBarQtSlider
+
+SmallButton < UIButton
+ size: 106 20
+ font: cipsoftFont
+ text-offset: 0 2
+ image-source: /images/ui/button
+ image-clip: 0 0 22 23
+ image-border: 3
+ padding: 5 10 5 10
+ change-cursor-image: true
+ cursor: pointer
+ color: #ffffff
+ $hover !disabled:
+ image-clip: 0 23 22 23
+ $pressed:
+ image-clip: 0 46 22 23
+ text-offset: 1 2
+ $disabled:
+ color: #ffffff88
+ change-cursor-image: false
diff --git a/init.lua b/init.lua
index b956d3f851..94464dd715 100644
--- a/init.lua
+++ b/init.lua
@@ -4,7 +4,7 @@
-- updater
Services = {
--updater = "http://localhost/api/updater.php", --./updater
- --status = "http://localhost/api/status.php", --./client_entergame | ./client_topmenu
+ --status = "http://localhost/login.php", --./client_entergame | ./client_topmenu
--websites = "http://localhost/?subtopic=accountmanagement", --./client_entergame "Forgot password and/or email"
}
diff --git a/mods/game_bot/default_configs/cavebot_1.3/cavebot/actions.lua b/mods/game_bot/default_configs/cavebot_1.3/cavebot/actions.lua
index 8a35b45a04..f31d6eca1f 100644
--- a/mods/game_bot/default_configs/cavebot_1.3/cavebot/actions.lua
+++ b/mods/game_bot/default_configs/cavebot_1.3/cavebot/actions.lua
@@ -97,7 +97,11 @@ CaveBot.registerAction("function", "red", function(value, retries, prev)
prefix = prefix .. "local " .. extension .. " = CaveBot.Extensions." .. extension .. "\n"
end
local status, result = pcall(function()
- return assert(load(prefix .. value, "cavebot_function"))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ return assert(loadstring(prefix .. value))()
+ else
+ return assert(load(prefix .. value, "cavebot_function"))()
+ end
end)
if not status then
error("Error in cavebot function:\n" .. result)
diff --git a/mods/game_bot/default_configs/cavebot_1.3/tools.lua b/mods/game_bot/default_configs/cavebot_1.3/tools.lua
index 629a9ca36e..c84e5ad987 100644
--- a/mods/game_bot/default_configs/cavebot_1.3/tools.lua
+++ b/mods/game_bot/default_configs/cavebot_1.3/tools.lua
@@ -18,9 +18,13 @@ end)
UI.Separator()
for _, scripts in ipairs({storage.ingame_macros, storage.ingame_hotkeys}) do
- if type(scripts) == "string" and scripts:len() > 3 then
+ if type(scripts) == "string" and scripts:len() > 3 then
local status, result = pcall(function()
- assert(load(scripts, "ingame_editor"))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ assert(loadstring(scripts))()
+ else
+ assert(load(scripts, "ingame_editor"))()
+ end
end)
if not status then
error("Ingame edior error:\n" .. result)
diff --git a/mods/game_bot/default_configs/vBot_4.8/cavebot/actions.lua b/mods/game_bot/default_configs/vBot_4.8/cavebot/actions.lua
index ba4ed7d48a..eca03e9378 100644
--- a/mods/game_bot/default_configs/vBot_4.8/cavebot/actions.lua
+++ b/mods/game_bot/default_configs/vBot_4.8/cavebot/actions.lua
@@ -267,7 +267,11 @@ CaveBot.registerAction("function", "red", function(value, retries, prev)
prefix = prefix .. "local " .. extension .. " = CaveBot.Extensions." .. extension .. "\n"
end
local status, result = pcall(function()
- return assert(load(prefix .. value, "cavebot_function"))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ return assert(loadstring(prefix .. value))()
+ else
+ return assert(load(prefix .. value, "cavebot_function"))()
+ end
end)
if not status then
warn("warn in cavebot function:\n" .. result)
diff --git a/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.lua b/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.lua
index 8a2eff1f66..60796d04c8 100644
--- a/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.lua
+++ b/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.lua
@@ -133,7 +133,12 @@ local toggle = function()
end
local drawGraph = function(graph, value)
- graph:addValue(value)
+ if graph:getGraphsCount() == 0 then
+ graph:createGraph()
+ graph:setLineWidth(1, 1)
+ graph:setLineColor(1, "#FF0000")
+ end
+ graph:addValue(1, value)
end
local toggleAnalyzer = function(window)
diff --git a/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.otui b/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.otui
index 761a9b4c99..261a6a7e3e 100644
--- a/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.otui
+++ b/mods/game_bot/default_configs/vBot_4.8/vBot/analyzer.otui
@@ -226,8 +226,6 @@ AnalyzerLootItem < UIItem
AnalyzerGraph < UIGraph
height: 140
capacity: 400
- line-width: 1
- color: red
margin-top: 5
margin-left: 5
margin-right: 5
diff --git a/mods/game_bot/default_configs/vBot_4.8/vBot/ingame_editor.lua b/mods/game_bot/default_configs/vBot_4.8/vBot/ingame_editor.lua
index 46528fc305..9df4b19945 100644
--- a/mods/game_bot/default_configs/vBot_4.8/vBot/ingame_editor.lua
+++ b/mods/game_bot/default_configs/vBot_4.8/vBot/ingame_editor.lua
@@ -12,7 +12,11 @@ UI.Button("Ingame script editor", function(newText)
for _, scripts in pairs({storage.ingame_hotkeys}) do
if type(scripts) == "string" and scripts:len() > 3 then
local status, result = pcall(function()
- assert(load(scripts, "ingame_editor"))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ return assert(loadstring(scripts))()
+ else
+ assert(load(scripts, "ingame_editor"))()
+ end
end)
if not status then
error("Ingame edior error:\n" .. result)
diff --git a/mods/game_bot/executor.lua b/mods/game_bot/executor.lua
index e9f0c8f7c6..1cce5efeac 100644
--- a/mods/game_bot/executor.lua
+++ b/mods/game_bot/executor.lua
@@ -96,10 +96,23 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, relo
date = os.date,
clock = os.clock
}
- context.load = function(str) return assert(load(str, nil, nil, context)) end
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ context.load = function(str)
+ local func = assert(loadstring(str))
+ setfenv(func, context)
+ return func
+ end
+ context.dofile = function(file)
+ local func = assert(loadstring(g_resources.readFileContents("/bot/" .. config .. "/" .. file)))
+ setfenv(func, context)
+ func()
+ end
+ else
+ context.load = function(str) return assert(load(str, nil, nil, context)) end
+ context.dofile = function(file) assert(load(g_resources.readFileContents("/bot/" .. config .. "/" .. file), file, nil, context))() end
+ end
context.loadstring = context.load
context.assert = assert
- context.dofile = function(file) assert(load(g_resources.readFileContents("/bot/" .. config .. "/" .. file), file, nil, context))() end
context.gcinfo = gcinfo
context.tr = tr
context.json = json
@@ -164,7 +177,13 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, relo
-- run lua script
for i, file in ipairs(luaFiles) do
- assert(load(g_resources.readFileContents(file), file, nil, context))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ local func = assert(loadstring(g_resources.readFileContents(file)))
+ setfenv(func, context)
+ func()
+ else
+ assert(load(g_resources.readFileContents(file), file, nil, context))()
+ end
context.panel = context.mainTab -- reset default tab
end
diff --git a/mods/game_bot/functions/script_loader.lua b/mods/game_bot/functions/script_loader.lua
index 8346634ed0..3619532a3d 100644
--- a/mods/game_bot/functions/script_loader.lua
+++ b/mods/game_bot/functions/script_loader.lua
@@ -10,9 +10,14 @@ context.loadScript = function(path, onLoadCallback)
if not g_resources.fileExists(path) then
return context.error("File " .. path .. " doesn't exist")
end
-
local status, result = pcall(function()
- assert(load(g_resources.readFileContents(path), path, nil, context))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ local func = assert(loadstring(g_resources.readFileContents(path)))
+ setfenv(func, context)
+ func()
+ else
+ assert(load(g_resources.readFileContents(path), path, nil, context))()
+ end
end)
if not status then
return context.error("Error while loading script from: " .. path .. ":\n" .. result)
@@ -42,7 +47,13 @@ context.loadRemoteScript = function(url, onLoadCallback)
end
local status, result = pcall(function()
- assert(load(data, url, nil, context))()
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ local func = assert(loadstring(data))
+ setfenv(func, context)
+ func()
+ else
+ assert(load(data, url, nil, context))()
+ end
end)
if not status then
return context.error("Error while loading script from: " .. url .. ":\n" .. result)
diff --git a/mods/game_bot/panels/waypoints.lua b/mods/game_bot/panels/waypoints.lua
index b66c699619..3f9412ffb4 100644
--- a/mods/game_bot/panels/waypoints.lua
+++ b/mods/game_bot/panels/waypoints.lua
@@ -746,7 +746,13 @@ Panel
elseif command.command == "function" and lastGotoSuccesful then
usedGotoLabel = false
local status, result = pcall(function()
- return assert(load("return " .. command.text, nil, nil, context))()(functions)
+ if _VERSION == "Lua 5.1" and type(jit) ~= "table" then
+ local func = assert(loadstring("return " .. command.text))
+ setfenv(func, context)
+ return func()(functions)
+ else
+ return assert(load("return " .. command.text, nil, nil, context))()(functions)
+ end
end)
if not status then
context.error("Waypoints function execution error:\n" .. result)
diff --git a/modules/client/client.otmod b/modules/client/client.otmod
index e8d8c780e8..2f09b9e170 100644
--- a/modules/client/client.otmod
+++ b/modules/client/client.otmod
@@ -18,4 +18,5 @@ Module
- client_options
- client_entergame
- client_terminal
+ - client_debug_info
- client_serverlist
diff --git a/modules/client_bottommenu/bottommenu.lua b/modules/client_bottommenu/bottommenu.lua
index 17450b7152..6a039afe79 100644
--- a/modules/client_bottommenu/bottommenu.lua
+++ b/modules/client_bottommenu/bottommenu.lua
@@ -45,7 +45,8 @@ function init()
creature_boosted = boostedWindow:recursiveGetChildById('creature')
boss_boosted = boostedWindow:recursiveGetChildById('boss')
- if not Services.status and default_info then
+-- if not Services.status and default_info then
+ if default_info then
local widget = g_ui.createWidget('ShowOffWidget', showOffWindow)
local description = widget:recursiveGetChildById('description')
local image = widget:recursiveGetChildById('image')
@@ -67,6 +68,9 @@ function init()
boss2:setImageSource(randomItem.creature2)
boss2:setVisible(true)
end
+ if g_game.isOnline() then
+ hide()
+ end
end
function terminate()
@@ -494,8 +498,15 @@ end
function Booster_creature(data)
if modules.game_things.isLoaded() then
- -- note: is better image *
- creature_boosted:setOutfit(data.creature)
- boss_boosted:setOutfit(data.boss)
+ local creatureraceid = modules.game_cyclopedia.RACE[data.creatureraceid]
+ local bossraceid = modules.game_cyclopedia.RACE_Bosstiary[data.bossraceid]
+ if creatureraceid then
+ creature_boosted:setOutfit({type=creatureraceid.type})
+ creature_boosted:getCreature():setStaticWalking(1000)
+ end
+ if bossraceid then
+ boss_boosted:setOutfit({type=bossraceid.type})
+ boss_boosted:getCreature():setStaticWalking(1000)
+ end
end
end
diff --git a/modules/client_bottommenu/bottommenu.otui b/modules/client_bottommenu/bottommenu.otui
index 109693c612..e801a89bd4 100644
--- a/modules/client_bottommenu/bottommenu.otui
+++ b/modules/client_bottommenu/bottommenu.otui
@@ -150,12 +150,16 @@ Panel
margin-left: 12
margin-top: 30
size: 64 64
- image-source: /images/ui/panel_flat
Creature
id: creature
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
+ image-source: ""
+ image-border: 0
+ border-width: 0
+ border-color: alpha
+
UIWidget
id: creature2
anchors.horizontalCenter: parent.horizontalCenter
@@ -174,12 +178,16 @@ Panel
margin-right: 12
margin-top: 30
size: 64 64
- image-source: /images/ui/panel_flat
Creature
id: boss
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
+ image-source: ""
+ image-border: 0
+ border-width: 0
+ border-color: alpha
+
UIWidget
id: boss2
anchors.horizontalCenter: parent.horizontalCenter
diff --git a/modules/client_debug_info/debug_info.lua b/modules/client_debug_info/debug_info.lua
new file mode 100644
index 0000000000..57a90e0c47
--- /dev/null
+++ b/modules/client_debug_info/debug_info.lua
@@ -0,0 +1,65 @@
+local debugInfoWindow = nil
+local debugInfoButton = nil
+
+local updateEvent = nil
+
+function init()
+ debugInfoButton = modules.client_topmenu.addTopRightToggleButton('debugInfoButton', tr('Debug Info'),
+ '/images/topbuttons/debug', toggle)
+ debugInfoButton:setOn(false)
+
+ debugInfoWindow = g_ui.displayUI('debug_info')
+ debugInfoWindow:hide()
+
+ Keybind.new("Debug", "Toggle Stats", "Ctrl+Alt+D", "")
+ Keybind.bind("Debug", "Toggle Stats", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
+
+ updateEvent = scheduleEvent(update, 2000)
+end
+
+function terminate()
+ debugInfoWindow:destroy()
+ debugInfoButton:destroy()
+
+ Keybind.delete("Debug", "Toggle Stats")
+
+ removeEvent(updateEvent)
+end
+
+function onClose()
+ debugInfoButton:setOn(false)
+end
+
+function toggle()
+ if debugInfoButton:isOn() then
+ debugInfoWindow:hide()
+ debugInfoButton:setOn(false)
+ else
+ debugInfoWindow:show()
+ debugInfoWindow:raise()
+ debugInfoWindow:focus()
+ debugInfoButton:setOn(true)
+ end
+end
+
+function update()
+ updateEvent = scheduleEvent(update, 20)
+
+ if not debugInfoWindow:isVisible() then
+ return
+ end
+
+ if g_proxy then
+ local text = ""
+ local proxiesDebug = g_proxy.getProxiesDebugInfo()
+ for proxy_name, proxy_debug in pairs(proxiesDebug) do
+ text = text .. proxy_name .. " - " .. proxy_debug .. "\n"
+ end
+ debugInfoWindow.debugPanel.proxies:setText(text)
+ end
+end
diff --git a/modules/client_debug_info/debug_info.otmod b/modules/client_debug_info/debug_info.otmod
new file mode 100644
index 0000000000..f3e6ace075
--- /dev/null
+++ b/modules/client_debug_info/debug_info.otmod
@@ -0,0 +1,9 @@
+Module
+ name: client_debug_info
+ description: Showing and sending debug/stats informations
+ author: otclient@otclient.ovh
+ sandboxed: true
+ scripts: [ debug_info ]
+ dependencies: [ client_topmenu ]
+ @onLoad: init()
+ @onUnload: terminate()
diff --git a/modules/client_debug_info/debug_info.otui b/modules/client_debug_info/debug_info.otui
new file mode 100644
index 0000000000..816849bdf9
--- /dev/null
+++ b/modules/client_debug_info/debug_info.otui
@@ -0,0 +1,65 @@
+DebugText < Label
+ font: terminus-10px
+ text-wrap: false
+ text-auto-resize: true
+ text-align: topleft
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.top: prev.bottom
+
+DebugLabel < Label
+ text-wrap: false
+ text-auto-resize: false
+ text-align: center
+ anchors.right: parent.right
+ anchors.left: parent.left
+ anchors.top: prev.bottom
+
+MainWindow
+ id: debugWindow
+ size: 600 150
+ !text: tr('Debug Info')
+ @onClose: modules.client_debug_info.onMiniWindowClose()
+ &save: false
+ margin: 0 0 0 0
+ padding: 25 3 3 3
+ opacity: 0.9
+ $mobile:
+ size: 600 150
+ @onEnter: modules.client_debug_info.toggle()
+ @onEscape: modules.client_debug_info.toggle()
+
+
+ ScrollablePanel
+ id: debugPanel
+ anchors.fill: parent
+ margin-bottom: 5
+ margin: 5 5 5 5
+ padding-left: 5
+ vertical-scrollbar: debugScroll
+
+ DebugLabel
+ !text: tr('Proxies')
+ anchors.top: parent.top
+
+ DebugText
+ id: proxies
+ text: -
+
+ VerticalScrollBar
+ id: debugScroll
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ step: 48
+ pixels-scroll: true
+
+ ResizeBorder
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+
+ ResizeBorder
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua
index f8f48af3a9..f99e2d5a47 100644
--- a/modules/client_entergame/entergame.lua
+++ b/modules/client_entergame/entergame.lua
@@ -127,7 +127,13 @@ end
-- public functions
function EnterGame.init()
enterGame = g_ui.displayUI('entergame')
- g_keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow)
+ Keybind.new("Misc.", "Change Character", "Ctrl+G", "")
+ Keybind.bind("Misc.", "Change Character", {
+ {
+ type = KEY_DOWN,
+ callback = EnterGame.openWindow,
+ }
+ })
local account = g_settings.get('account')
local password = g_settings.get('password')
@@ -219,12 +225,16 @@ function EnterGame.init()
end
function EnterGame.hidePanels()
- modules.client_bottommenu.hide()
+ if g_modules.getModule("client_bottommenu"):isLoaded() then
+ modules.client_bottommenu.hide()
+ end
modules.client_topmenu.toggle()
end
function EnterGame.showPanels()
- modules.client_bottommenu.show()
+ if g_modules.getModule("client_bottommenu"):isLoaded() then
+ modules.client_bottommenu.show()
+ end
modules.client_topmenu.toggle()
end
@@ -245,15 +255,17 @@ function EnterGame.firstShow()
end
if Services and Services.status then
- EnterGame.postCacheInfo()
- EnterGame.postEventScheduler()
- EnterGame.postShowOff()
- EnterGame.postShowCreatureBoost()
+ if g_modules.getModule("client_bottommenu"):isLoaded() then
+ EnterGame.postCacheInfo()
+ EnterGame.postEventScheduler()
+ -- EnterGame.postShowOff() -- myacc/znote no send login.php
+ EnterGame.postShowCreatureBoost()
+ end
end
end
function EnterGame.terminate()
- g_keyboard.unbindKeyDown('Ctrl+G')
+ Keybind.delete("Misc.", "Change Character")
disconnect(clientBox, {
onOptionChange = EnterGame.onClientVersionChange
@@ -333,26 +345,20 @@ end
function EnterGame.postEventScheduler()
local onRecvInfo = function(message, err)
if err then
- -- onError(nil, 'Bad Request. Game_entergame postEventScheduler1', 400)
- g_logger.warning("[Webscraping] " .. "Bad Request.Game_entergame postEventScheduler1")
+ g_logger.warning("[Webscraping] Bad Request.Game_entergame postEventScheduler1")
return
end
- local _, bodyStart = message:find('{')
- local _, bodyEnd = message:find('.*}')
- if not bodyStart or not bodyEnd then
- -- onError(nil, 'Bad Request. Game_entergame postEventScheduler2', 400)
- g_logger.warning("[Webscraping] " .. "Bad Request.Game_entergame postEventScheduler2")
- return
- end
+ local bodyStart, _ = message:find('{')
+ local _, bodyEnd = message:find('%}%b{}')
- local response = json.decode(message:sub(bodyStart, bodyEnd))
+ local jsonString = message:sub(bodyStart, bodyEnd)
+
+ local response = json.decode(jsonString)
if response.errorMessage then
- -- onError(nil, response.errorMessage, response.errorCode)
g_logger.warning("[Webscraping] " .. "response.errorMessage,response.errorCode")
return
end
-
modules.client_bottommenu.setEventsSchedulerTimestamp(response.lastupdatetimestamp)
modules.client_bottommenu.setEventsSchedulerCalender(response.eventlist)
end
@@ -365,7 +371,6 @@ end
function EnterGame.postShowOff()
local onRecvInfo = function(message, err)
if err then
- -- onError(nil, 'Bad Request. 1 Game_entergame postShowOff', 400)
g_logger.warning("[Webscraping] " .. "Bad Request.Game_entergame postShowOff")
return
end
@@ -373,14 +378,12 @@ function EnterGame.postShowOff()
local _, bodyStart = message:find('{')
local _, bodyEnd = message:find('.*}')
if not bodyStart or not bodyEnd then
- -- onError(nil, 'Bad Request. 2 Game_entergame postShowOff', 400)
g_logger.warning("[Webscraping] " .. "Bad Request.Game_entergame postShowOff")
return
end
local response = json.decode(message:sub(bodyStart, bodyEnd))
if response.errorMessage then
- -- onError(nil, response.errorMessage, response.errorCode)
g_logger.warning("[Webscraping] " .. response.errorMessage, response.errorCode)
return
end
@@ -420,7 +423,7 @@ function EnterGame.postShowCreatureBoost()
end
HTTP.post(Services.status, json.encode({
- type = 'Creatureboost'
+ type = 'boostedcreature'
}), onRecvInfo, false)
end
diff --git a/modules/client_options/audio.otui b/modules/client_options/audio.otui
deleted file mode 100644
index 9bef2bea53..0000000000
--- a/modules/client_options/audio.otui
+++ /dev/null
@@ -1,34 +0,0 @@
-Panel
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: 22
-
- OptionCheckBox
- id: enableAudio
- !text: tr('Enable audio')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 44
-
- OptionCheckBox
- id: enableMusicSound
- !text: tr('Enable music sound')
-
- OptionScaleScroll
- id: musicSoundVolume
- margin-top: 5
- anchors.top: prev.bottom
- anchors.left: parent.left
- anchors.right: parent.right
- &minimumScrollValue: 1
- &maximumScrollValue: 100
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('musicSoundVolume')
- self:setText(tr('Music volume: %d', value))
diff --git a/modules/client_options/console.otui b/modules/client_options/console.otui
deleted file mode 100644
index 4f57be88c4..0000000000
--- a/modules/client_options/console.otui
+++ /dev/null
@@ -1,157 +0,0 @@
-Panel
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: 22
-
- OptionCheckBox
- id: showInfoMessagesInConsole
- !text: tr('Show info messages')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showEventMessagesInConsole
- !text: tr('Show event messages')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showStatusMessagesInConsole
- !text: tr('Show status messages')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showOthersStatusMessagesInConsole
- !text: tr('Show others status messages in console')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showTimestampsInConsole
- !text: tr('Show timestamps')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showLevelsInConsole
- !text: tr('Show levels')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showPrivateMessagesInConsole
- !text: tr('Show private messages')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showPrivateMessagesOnScreen
- !text: tr('Show private messages on screen')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: creatureInformationScale
- anchors.fill: parent
- &minimumScrollValue: 1
- &maximumScrollValue: 9
- &scrollSize: 21
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: staticTextScale
- anchors.fill: parent
- &minimumScrollValue: 1
- &maximumScrollValue: 9
- &scrollSize: 21
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: animatedTextScale
- anchors.fill: parent
- &minimumScrollValue: 1
- &maximumScrollValue: 9
- &scrollSize: 21
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: setEffectAlphaScroll
- anchors.fill: parent
- &minimumScrollValue: 10
- &maximumScrollValue: 100
- &scrollSize: 21
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: setMissileAlphaScroll
- anchors.fill: parent
- &minimumScrollValue: 10
- &maximumScrollValue: 100
- &scrollSize: 21
diff --git a/modules/client_options/control.otui b/modules/client_options/control.otui
deleted file mode 100644
index 11aa6b52b9..0000000000
--- a/modules/client_options/control.otui
+++ /dev/null
@@ -1,127 +0,0 @@
-Panel
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: 22
-
- OptionCheckBox
- id: classicControl
- !text: tr('Classic control')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: autoChaseOverride
- !text: tr('Allow auto chase override')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: moveStack
- !text: tr('Move stacks directly')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: smartWalk
- !text: tr('Enable smart walking')
- !tooltip: tr('Will detect when to use diagonal step based on the\nkeys you are pressing')
-
- SmallReversedQtPanel
- id: hotkeyDelay_label
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: hotkeyDelay
- !text: tr('Hotkey delay: 30ms')
- anchors.fill: parent
- &minimumScrollValue: 30
- &maximumScrollValue: 250
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('hotkeyDelay')
- self:setText(tr('Hotkey delay: %dms', value))
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: walkFirstStepDelay
- !text: tr('Walk Delay after first step: 50ms')
- anchors.fill: parent
- &minimumScrollValue: 50
- &maximumScrollValue: 500
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('walkFirstStepDelay')
- self:setText(tr('Walk Delay after first step: %dms', value))
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: walkTurnDelay
- !text: tr('Walk delay after turn: 50ms')
- anchors.fill: parent
- &minimumScrollValue: 50
- &maximumScrollValue: 500
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('walkTurnDelay')
- self:setText(tr('Walk delay after turn: %dms', value))
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: turnDelay
- !text: tr('Turn delay: 30ms')
- anchors.fill: parent
- &minimumScrollValue: 30
- &maximumScrollValue: 250
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('turnDelay')
- self:setText(tr('Turn delay: %dms', value))
-
- QtButton
- id: hotkeysButton
- !text: tr('Hotkeys Manager')
- @onClick: modules.game_hotkeys.show()
- anchors.top: prev.bottom
- anchors.left: parent.left
- margin-top: 12
-
- size: 120 20
diff --git a/modules/client_options/data_options.lua b/modules/client_options/data_options.lua
index 2dc4ad37bc..5b56bd01ce 100644
--- a/modules/client_options/data_options.lua
+++ b/modules/client_options/data_options.lua
@@ -398,5 +398,56 @@ return {
},
profile = {
value = 1,
- }
+ },
+ showExpiryInInvetory = {
+ value = true,
+ event = nil,
+ action = function(value, options, controller, panels, extraWidgets)
+ if options.showExpiryInContainers.event ~= nil then
+ removeEvent(options.showExpiryInInvetory.event)
+ end
+ options.showExpiryInInvetory.event = scheduleEvent(function()
+ modules.game_inventory.reloadInventory()
+ options.showExpiryInInvetory.event = nil
+ end, 100)
+ end
+ },
+ showExpiryInContainers = {
+ value = true,
+ event = nil,
+ action = function(value, options, controller, panels, extraWidgets)
+ if options.showExpiryInContainers.event ~= nil then
+ removeEvent(options.showExpiryInContainers.event)
+ end
+ options.showExpiryInContainers.event = scheduleEvent(function()
+ modules.game_containers.reloadContainers()
+ options.showExpiryInContainers.event = nil
+ end, 100)
+ end
+ },
+ showExpiryOnUnusedItems = true,
+ framesRarity = {
+ value = 'frames',
+ event = nil,
+ action = function(value, options, controller, panels, extraWidgets)
+ local newValue = value
+ if newValue == 'None' then
+ newValue = nil
+ end
+ panels.interface:recursiveGetChildById('frames'):setCurrentOptionByData(newValue, true)
+ if options.framesRarity.event ~= nil then
+ removeEvent(options.framesRarity.event)
+ end
+ options.framesRarity.event = scheduleEvent(function()
+ modules.game_containers.reloadContainers()
+ options.framesRarity.event = nil
+ end, 100)
+ end
+ },
+ autoSwitchPreset = false,
+ listKeybindsPanel = {
+ action = function(value, options, controller, panels, extraWidgets)
+ listKeybindsComboBox(value)
+ end
+ },
}
diff --git a/modules/client_options/general.otui b/modules/client_options/general.otui
deleted file mode 100644
index 16057d90d4..0000000000
--- a/modules/client_options/general.otui
+++ /dev/null
@@ -1,178 +0,0 @@
-Panel
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: 22
-
- OptionCheckBoxMarked
- id: showPing
- !text: tr('Show connection ping')
- !tooltip: tr('Display connection speed to the server (milliseconds)')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showFps
- !text: tr('Show frame rate')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showLeftPanel
- !text: tr('Show left panel')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showRightExtraPanel
- !text: tr('Show an extra right panel')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showActionbar
- !text: tr('Show action bar')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: showSpellGroupCooldowns
- !text: tr('Show spell group cooldowns')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: openMaximized
- !text: tr('Open containers maximized')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: displayNames
- !text: tr('Display creature names')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: displayHealth
- !text: tr('Display creature health bars')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: displayMana
- !text: tr('Display player mana bar')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: displayText
- !text: tr('Display text messages')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: enableHighlightMouseTarget
- !text: tr('Highlight mouse target')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: asyncTxtLoading
- !text: tr('Async texture loading')
- !tooltip: tr('This option makes textures load asynchronously and uses less RAM.')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: drawEffectOnTop
- !text: tr('Draw Effect On Top')
- !tooltip: tr('Draw effect after drawing the entire floor.')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 32
-
- Label
- !text: 'Crosshair:'
- anchors.left: parent.left
- margin-left: 18
- color: #c0c0c0ff
- anchors.verticalCenter: parent.verticalCenter
-
- QtComboBox
- id: crosshair
- width: 120
- margin-left: 10
- anchors.verticalCenter: prev.verticalCenter
- anchors.left: prev.right
- mouse-scroll: false
\ No newline at end of file
diff --git a/modules/client_options/graphics.otui b/modules/client_options/graphics.otui
deleted file mode 100644
index 747a83a669..0000000000
--- a/modules/client_options/graphics.otui
+++ /dev/null
@@ -1,222 +0,0 @@
-Panel
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: 22
-
- OptionCheckBoxMarked
- id: vsync
- !text: tr('Enable vertical synchronization')
- !tooltip: tr('Limits your fps based on monitor refresh rate')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: optimizeFps
- !text: tr('Optimize FPS')
- !tooltip: tr('Try to optimize when the frame rate is below 60. VISUAL PROBLEMS MAY OCCUR')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: forceEffectOptimization
- !text: tr('Force Effect Optimization')
- !tooltip: tr('Will avoid drawing effects on certain occasions.')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: enableLights
- !text: tr('Enable lights')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: drawEffectOnTop
- !text: tr('Draw Effect On Top')
- !tooltip: tr('Draw effect after drawing the entire floor.')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: limitVisibleDimension
- !text: tr('Limit Visible Dimension')
- !tooltip: tr('The limit is based on your maximum range.')
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: floatingEffect
- !text: 'Draw Floating Effects'
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBoxMarked
- id: fullscreen
- !text: 'Fullscreen'
- !tooltip: 'Ctrl+Shift+F'
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionCheckBox
- id: dontStretchShrink
- !text: 'Don\'t stretch/shrink Game Window'
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 32
-
- Label
- !text: 'Floor View Mode:'
- anchors.left: parent.left
- margin-left: 18
- color: #c0c0c0ff
- anchors.verticalCenter: parent.verticalCenter
-
- QtComboBox
- id: floorViewMode
- width: 180
- margin-left: 10
- anchors.verticalCenter: prev.verticalCenter
- anchors.left: prev.right
- mouse-scroll: false
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 32
-
- Label
- !text: 'Antialiasing Mode:'
- anchors.left: parent.left
- margin-left: 18
- color: #c0c0c0ff
- anchors.verticalCenter: parent.verticalCenter
-
- QtComboBox
- id: antialiasingMode
- width: 180
- margin-left: 10
- anchors.verticalCenter: prev.verticalCenter
- anchors.left: prev.right
- mouse-scroll: false
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: ambientLight
- anchors.fill: parent
- &minimumScrollValue: 0
- &maximumScrollValue: 100
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('ambientLight')
- self:setText(string.format('Ambient light: %s%%', value))
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: shadowFloorIntensity
- anchors.fill: parent
- &minimumScrollValue: 0
- &maximumScrollValue: 100
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('shadowFloorIntensity')
- self:setText(string.format('Floor Shadowing Intensity: %s%%', value))
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: floorFading
- anchors.fill: parent
- &minimumScrollValue: 0
- &maximumScrollValue: 1000
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('floorFading')
- self:setText(tr('Floor Fading: %s ms', value))
-
- SmallReversedQtPanel
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: prev.bottom
- margin-top: 7
- height: 22
-
- OptionScaleScroll
- id: backgroundFrameRate
- !text: tr('Game framerate limit: %s', 'max')
- anchors.fill: parent
- &minimumScrollValue: 10
- &maximumScrollValue: 201
- &scrollSize: 21
- @onSetup: |
- local value = modules.client_options.getOption('backgroundFrameRate')
- local text = value
- if value <= 0 or value >= 201 then
- text = 'max'
- end
-
- self:setText(tr('Game framerate limit: %s', text))
diff --git a/modules/client_options/keybins.lua b/modules/client_options/keybins.lua
new file mode 100644
index 0000000000..fa8abf2103
--- /dev/null
+++ b/modules/client_options/keybins.lua
@@ -0,0 +1,756 @@
+local actionNameLimit = 39
+local changedOptions = {}
+local changedKeybinds = {}
+local changedHotkeys = {}
+local presetWindow = nil
+local actionSearchEvent
+local keyEditWindow = nil
+local chatModeGroup
+
+-- controls and keybinds
+function addNewPreset()
+ presetWindow:setText(tr('Add hotkey preset'))
+
+ presetWindow.info:setText(tr('Enter a name for the new preset:'))
+
+ presetWindow.field:clearText()
+ presetWindow.field:show()
+ presetWindow.field:focus()
+
+ presetWindow:setWidth(360)
+
+ presetWindow.action = 'add'
+
+ presetWindow:show()
+ presetWindow:raise()
+ presetWindow:focus()
+
+ controller.ui:hide()
+end
+
+function copyPreset()
+ presetWindow:setText(tr('Copy hotkey preset'))
+
+ presetWindow.info:setText(tr('Enter a name for the new preset:'))
+
+ presetWindow.field:clearText()
+ presetWindow.field:show()
+ presetWindow.field:focus()
+
+ presetWindow.action = 'copy'
+
+ presetWindow:setWidth(360)
+ presetWindow:show()
+ presetWindow:raise()
+ presetWindow:focus()
+
+ controller.ui:hide()
+end
+
+function renamePreset()
+ presetWindow:setText(tr('Rename hotkey preset'))
+
+ presetWindow.info:setText(tr('Enter a name for the preset:'))
+
+ presetWindow.field:setText(panels.keybindsPanel.presets.list:getCurrentOption().text)
+ presetWindow.field:setCursorPos(1000)
+ presetWindow.field:show()
+ presetWindow.field:focus()
+
+ presetWindow.action = 'rename'
+
+ presetWindow:setWidth(360)
+ presetWindow:show()
+ presetWindow:raise()
+ presetWindow:focus()
+
+ controller.ui:hide()
+end
+
+function removePreset()
+ presetWindow:setText(tr('Warning'))
+
+ presetWindow.info:setText(tr('Do you really want to delete the hotkey preset %s?',
+ panels.keybindsPanel.presets.list:getCurrentOption().text))
+ presetWindow.field:hide()
+ presetWindow.action = 'remove'
+
+ presetWindow:setWidth(presetWindow.info:getTextSize().width + presetWindow:getPaddingLeft() +
+ presetWindow:getPaddingRight())
+ presetWindow:show()
+ presetWindow:raise()
+ presetWindow:focus()
+
+ controller.ui:hide()
+end
+
+function okPresetWindow()
+ local presetName = presetWindow.field:getText():trim()
+ local selectedPreset = panels.keybindsPanel.presets.list:getCurrentOption().text
+
+ presetWindow:hide()
+ show()
+
+ if presetWindow.action == 'add' then
+ Keybind.newPreset(presetName)
+ panels.keybindsPanel.presets.list:addOption(presetName)
+ panels.keybindsPanel.presets.list:setCurrentOption(presetName)
+ elseif presetWindow.action == 'copy' then
+ if not Keybind.copyPreset(selectedPreset, presetName) then
+ return
+ end
+
+ panels.keybindsPanel.presets.list:addOption(presetName)
+ panels.keybindsPanel.presets.list:setCurrentOption(presetName)
+ elseif presetWindow.action == 'rename' then
+ if selectedPreset ~= presetName then
+ panels.keybindsPanel.presets.list:updateCurrentOption(presetName)
+ if changedOptions['currentPreset'] then
+ changedOptions['currentPreset'].value = presetName
+ end
+ Keybind.renamePreset(selectedPreset, presetName)
+ end
+ elseif presetWindow.action == 'remove' then
+ if Keybind.removePreset(selectedPreset) then
+ panels.keybindsPanel.presets.list:removeOption(selectedPreset)
+ end
+ end
+end
+
+function cancelPresetWindow()
+ presetWindow:hide()
+ show()
+end
+
+function editKeybindKeyDown(widget, keyCode, keyboardModifiers)
+ keyEditWindow.keyCombo:setText(determineKeyComboDesc(keyCode,
+ keyEditWindow.alone:isVisible() and KeyboardNoModifier or keyboardModifiers))
+
+ local category = nil
+ local action = nil
+
+ if keyEditWindow.keybind then
+ category = keyEditWindow.keybind.category
+ action = keyEditWindow.keybind.action
+ end
+
+ local keyCombo = keyEditWindow.keyCombo:getText()
+ local keyUsed = Keybind.isKeyComboUsed(keyCombo, category, action, getChatMode())
+ if not keyUsed then
+ for _, change in ipairs(changedHotkeys) do
+ if change.primary == keyCombo or change.secondary == keyCombo then
+ keyUsed = true
+ break
+ end
+ end
+ end
+
+ keyEditWindow.buttons.ok:setEnabled(not keyUsed)
+ keyEditWindow.used:setVisible(keyUsed)
+end
+
+function editKeybind(keybind)
+ keyEditWindow.buttons.cancel.onClick = function()
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ end
+
+ keyEditWindow.info:setText(tr(
+ 'Click \'Ok\' to assign the keybind. Click \'Clear\' to remove the keybind from \'%s: %s\'.', keybind.category,
+ keybind.action))
+ keyEditWindow.alone:setVisible(keybind.alone)
+
+ connect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+
+ keyEditWindow:show()
+ keyEditWindow:raise()
+ keyEditWindow:focus()
+ keyEditWindow:grabKeyboard()
+ hide()
+end
+
+function editKeybindPrimary(button)
+ local column = button:getParent()
+ local row = column:getParent()
+ local index = row.category .. '_' .. row.action
+ local keybind = Keybind.getAction(row.category, row.action)
+ local preset = panels.keybindsPanel.presets.list:getCurrentOption().text
+
+ keyEditWindow.keybind = {
+ category = row.category,
+ action = row.action
+ }
+
+ keyEditWindow:setText(tr('Edit Primary Key for \'%s\'', string.format('%s: %s', keybind.category, keybind.action)))
+ keyEditWindow.keyCombo:setText(Keybind.getKeybindKeys(row.category, row.action, getChatMode(), preset).primary)
+
+ editKeybind(keybind)
+
+ keyEditWindow.buttons.ok.onClick = function()
+ local keyCombo = keyEditWindow.keyCombo:getText()
+
+ column:setText(keyEditWindow.keyCombo:getText())
+
+ if not changedKeybinds[preset] then
+ changedKeybinds[preset] = {}
+ end
+ if not changedKeybinds[preset][index] then
+ changedKeybinds[preset][index] = {}
+ end
+ changedKeybinds[preset][index].primary = {
+ category = row.category,
+ action = row.action,
+ keyCombo = keyCombo
+ }
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ applyChangedOptions()
+ end
+
+ keyEditWindow.buttons.clear.onClick = function()
+ if not changedKeybinds[preset] then
+ changedKeybinds[preset] = {}
+ end
+ if not changedKeybinds[preset][index] then
+ changedKeybinds[preset][index] = {}
+ end
+ changedKeybinds[preset][index].primary = {
+ category = row.category,
+ action = row.action,
+ keyCombo = ''
+ }
+
+ column:setText('')
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ applyChangedOptions()
+ end
+end
+
+function editKeybindSecondary(button)
+ local column = button:getParent()
+ local row = column:getParent()
+ local index = row.category .. '_' .. row.action
+ local keybind = Keybind.getAction(row.category, row.action)
+ local preset = panels.keybindsPanel.presets.list:getCurrentOption().text
+
+ keyEditWindow.keybind = {
+ category = row.category,
+ action = row.action
+ }
+
+ keyEditWindow:setText(tr('Edit Secondary Key for \'%s\'', string.format('%s: %s', keybind.category, keybind.action)))
+ keyEditWindow.keyCombo:setText(Keybind.getKeybindKeys(row.category, row.action, getChatMode(), preset).secondary)
+
+ editKeybind(keybind)
+
+ keyEditWindow.buttons.ok.onClick = function()
+ local keyCombo = keyEditWindow.keyCombo:getText()
+
+ column:setText(keyEditWindow.keyCombo:getText())
+
+ if not changedKeybinds[preset] then
+ changedKeybinds[preset] = {}
+ end
+ if not changedKeybinds[preset][index] then
+ changedKeybinds[preset][index] = {}
+ end
+ changedKeybinds[preset][index].secondary = {
+ category = row.category,
+ action = row.action,
+ keyCombo = keyCombo
+ }
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ applyChangedOptions()
+ end
+
+ keyEditWindow.buttons.clear.onClick = function()
+ if not changedKeybinds[preset] then
+ changedKeybinds[preset] = {}
+ end
+ if not changedKeybinds[preset][index] then
+ changedKeybinds[preset][index] = {}
+ end
+ changedKeybinds[preset][index].secondary = {
+ category = row.category,
+ action = row.action,
+ keyCombo = ''
+ }
+
+ column:setText('')
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ applyChangedOptions()
+ end
+end
+
+function resetActions()
+ changedOptions['resetKeybinds'] = {
+ value = panels.keybindsPanel.presets.list:getCurrentOption().text
+ }
+ updateKeybinds()
+ applyChangedOptions()
+end
+
+function updateKeybinds()
+ panels.keybindsPanel.tablePanel.keybinds:clearData()
+
+ local sortedKeybinds = {}
+
+ for index, _ in pairs(Keybind.defaultKeybinds) do
+ table.insert(sortedKeybinds, index)
+ end
+
+ table.sort(sortedKeybinds, function(a, b)
+ local keybindA = Keybind.defaultKeybinds[a]
+ local keybindB = Keybind.defaultKeybinds[b]
+
+ if keybindA.category ~= keybindB.category then
+ return keybindA.category < keybindB.category
+ end
+ return keybindA.action < keybindB.action
+ end)
+
+
+ local comboBox = panels.keybindsPanel.presets.list:getCurrentOption()
+ if not comboBox then
+ return
+ end
+ for _, index in ipairs(sortedKeybinds) do
+ local keybind = Keybind.defaultKeybinds[index]
+ local keys = Keybind.getKeybindKeys(keybind.category, keybind.action, getChatMode(), comboBox.text,
+ changedOptions['resetKeybinds'])
+ addKeybind(keybind.category, keybind.action, keys.primary, keys.secondary)
+ end
+end
+
+function updateHotkeys()
+ panels.keybindsPanel.tablePanel.keybinds:clearData()
+
+ local chatMode = getChatMode()
+ local preset = panels.keybindsPanel.presets.list:getCurrentOption().text
+ if Keybind.hotkeys[chatMode][preset] then
+ for _, hotkey in ipairs(Keybind.hotkeys[chatMode][preset]) do
+ addHotkey(hotkey.hotkeyId, hotkey.action, hotkey.data, hotkey.primary, hotkey.secondary)
+ end
+ end
+end
+
+function preAddHotkey(action, data)
+ local preset = panels.keybindsPanel.presets.list:getCurrentOption().text
+ local chatMode = getChatMode()
+ local hotkeyId = #changedHotkeys + 1
+
+ if Keybind.hotkeys[chatMode] and Keybind.hotkeys[chatMode][preset] then
+ hotkeyId = hotkeyId + #Keybind.hotkeys[chatMode][preset]
+ end
+
+ table.insert(changedHotkeys, {
+ hotkeyId = hotkeyId,
+ action = action,
+ data = data,
+ new = true
+ })
+
+ addHotkey(hotkeyId, action, data)
+end
+
+function addKeybind(category, action, primary, secondary)
+ local rawText = string.format('%s: %s', category, action)
+ local text = string.format('[color=#ffffff]%s:[/color] %s', category, action)
+ local tooltip = nil
+
+ if rawText:len() > actionNameLimit then
+ tooltip = rawText
+ -- 15 and 8 are length of color codes
+ text = text:sub(1, actionNameLimit + 15 + 8) .. '...'
+ end
+
+ local row = panels.keybindsPanel.tablePanel.keybinds:addRow({ {
+ coloredText = {
+ text = text,
+ color = '#c0c0c0'
+ },
+ width = 286
+ }, {
+ style = 'VerticalSeparator'
+ }, {
+ style = 'EditableKeybindsTableColumn',
+ text = primary,
+ width = 100
+ }, {
+ style = 'VerticalSeparator'
+ }, {
+ style = 'EditableKeybindsTableColumn',
+ text = secondary,
+ width = 90
+ } })
+
+ row.category = category
+ row.action = action
+
+ if tooltip then
+ row:setTooltip(tooltip)
+ end
+
+ row:getChildByIndex(3).edit.onClick = editKeybindPrimary
+ row:getChildByIndex(5).edit.onClick = editKeybindSecondary
+end
+
+function clearHotkey(row)
+ table.insert(changedHotkeys, {
+ hotkeyId = row.hotkeyId,
+ remove = true
+ })
+ panels.keybindsPanel.tablePanel.keybinds:removeRow(row)
+end
+
+function editHotkeyKey(text)
+ keyEditWindow.buttons.cancel.onClick = function()
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ end
+
+ keyEditWindow.info:setText(tr(
+ 'Click \'Ok\' to assign the keybind. Click \'Clear\' to remove the keybind from \'%s\'.', text))
+ keyEditWindow.alone:setVisible(false)
+
+ connect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+
+ keyEditWindow:show()
+ keyEditWindow:raise()
+ keyEditWindow:focus()
+ keyEditWindow:grabKeyboard()
+ hide()
+end
+
+function editHotkeyPrimary(button)
+ local column = button:getParent()
+ local row = column:getParent()
+ local text = row:getChildByIndex(1):getText()
+ local hotkeyId = row.hotkeyId
+ local preset = panels.keybindsPanel.presets.list:getCurrentOption().text
+
+ keyEditWindow:setText(tr('Edit Primary Key for \'%s\'', text))
+ keyEditWindow.keyCombo:setText(Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).primary)
+
+ editHotkeyKey(text)
+
+ keyEditWindow.buttons.ok.onClick = function()
+ local keyCombo = keyEditWindow.keyCombo:getText()
+
+ column:setText(keyEditWindow.keyCombo:getText())
+
+ local changed = table.findbyfield(changedHotkeys, 'hotkeyId', hotkeyId)
+ if changed then
+ changed.primary = keyCombo
+ if not changed.secondary then
+ changed.secondary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).secondary
+ end
+ changed.editKey = true
+ else
+ table.insert(changedHotkeys, {
+ hotkeyId = hotkeyId,
+ primary = keyCombo,
+ secondary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).secondary,
+ editKey = true
+ })
+ end
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ end
+
+ keyEditWindow.buttons.clear.onClick = function()
+ column:setText('')
+
+ local changed = table.findbyfield(changedHotkeys, 'hotkeyId', hotkeyId)
+ if changed then
+ changed.primary = nil
+ if not changed.secondary then
+ changed.secondary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).secondary
+ end
+ changed.editKey = true
+ else
+ table.insert(changedHotkeys, {
+ hotkeyId = hotkeyId,
+ secondary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).secondary,
+ editKey = true
+ })
+ end
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ end
+end
+
+function editHotkeySecondary(button)
+ local column = button:getParent()
+ local row = column:getParent()
+ local text = row:getChildByIndex(1):getText()
+ local hotkeyId = row.hotkeyId
+ local preset = panels.keybindsPanel.presets.list:getCurrentOption().text
+
+ keyEditWindow:setText(tr('Edit Secondary Key for \'%s\'', text))
+ keyEditWindow.keyCombo:setText(Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).secondary)
+
+ editHotkeyKey(text)
+
+ keyEditWindow.buttons.ok.onClick = function()
+ local keyCombo = keyEditWindow.keyCombo:getText()
+
+ column:setText(keyEditWindow.keyCombo:getText())
+
+ if changedHotkeys[hotkeyId] then
+ if not changedHotkeys[hotkeyId].primary then
+ changedHotkeys[hotkeyId].primary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).primary
+ end
+ changedHotkeys[hotkeyId].secondary = keyCombo
+ changedHotkeys[hotkeyId].editKey = true
+ else
+ table.insert(changedHotkeys, {
+ hotkeyId = hotkeyId,
+ primary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).primary,
+ secondary = keyCombo,
+ editKey = true
+ })
+ end
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ end
+
+ keyEditWindow.buttons.clear.onClick = function()
+ column:setText('')
+
+ if changedHotkeys[hotkeyId] then
+ if not changedHotkeys[hotkeyId].primary then
+ changedHotkeys[hotkeyId].primary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).primary
+ end
+ changedHotkeys[hotkeyId].secondary = nil
+ changedHotkeys[hotkeyId].editKey = true
+ else
+ table.insert(changedHotkeys, {
+ hotkeyId = hotkeyId,
+ primary = Keybind.getHotkeyKeys(hotkeyId, preset, getChatMode()).primary,
+ editKey = true
+ })
+ end
+
+ disconnect(keyEditWindow, {
+ onKeyDown = editKeybindKeyDown
+ })
+ keyEditWindow:hide()
+ keyEditWindow:ungrabKeyboard()
+ show()
+ end
+end
+
+function searchActions(field, text, oldText)
+ if actionSearchEvent then
+ removeEvent(actionSearchEvent)
+ end
+
+ actionSearchEvent = scheduleEvent(performeSearchActions, 200)
+end
+
+function performeSearchActions()
+ local searchText = panels.keybindsPanel.search.field:getText():trim():lower()
+
+ local rows = panels.keybindsPanel.tablePanel.keybinds.dataSpace:getChildren()
+ if searchText:len() > 0 then
+ for _, row in ipairs(rows) do
+ row:hide()
+ end
+
+ for _, row in ipairs(rows) do
+ local actionText = row:getChildByIndex(1):getText():lower()
+ local primaryText = row:getChildByIndex(3):getText():lower()
+ local secondaryText = row:getChildByIndex(5):getText():lower()
+ if actionText:find(searchText) or primaryText:find(searchText) or secondaryText:find(searchText) then
+ row:show()
+ end
+ end
+ else
+ for _, row in ipairs(rows) do
+ row:show()
+ end
+ end
+
+ removeEvent(actionSearchEvent)
+ actionSearchEvent = nil
+end
+
+function chatModeChange()
+ changedHotkeys = {}
+ changedKeybinds = {}
+
+ panels.keybindsPanel.search.field:clearText()
+
+ updateKeybinds()
+end
+
+function getChatMode()
+ if chatModeGroup:getSelectedWidget() == panels.keybindsPanel.panel.chatMode.on then
+ return CHAT_MODE.ON
+ end
+
+ return CHAT_MODE.OFF
+end
+
+function applyChangedOptions()
+ local needKeybindsUpdate = false
+ local needHotkeysUpdate = false
+
+ for key, option in pairs(changedOptions) do
+ if key == 'resetKeybinds' then
+ Keybind.resetKeybindsToDefault(option.value, option.chatMode)
+ needKeybindsUpdate = true
+ end
+ end
+ changedOptions = {}
+
+ for preset, keybinds in pairs(changedKeybinds) do
+ for index, keybind in pairs(keybinds) do
+ if keybind.primary then
+ if Keybind.setPrimaryActionKey(keybind.primary.category, keybind.primary.action, preset,
+ keybind.primary.keyCombo, getChatMode()) then
+ needKeybindsUpdate = true
+ end
+ elseif keybind.secondary then
+ if Keybind.setSecondaryActionKey(keybind.secondary.category, keybind.secondary.action, preset,
+ keybind.secondary.keyCombo, getChatMode()) then
+ needKeybindsUpdate = true
+ end
+ end
+ end
+ end
+ changedKeybinds = {}
+
+ if needKeybindsUpdate then
+ updateKeybinds()
+ end
+ g_settings.save()
+end
+
+function presetOption(widget, key, value, force)
+ if not controller.ui:isVisible() then
+ return
+ end
+
+ changedOptions[key] = { widget = widget, value = value, force = force }
+ if key == "currentPreset" then
+ Keybind.selectPreset(value)
+ panels.keybindsPanel.presets.list:setCurrentOption(value, true)
+ end
+end
+
+function init_binds()
+ chatModeGroup = UIRadioGroup.create()
+ chatModeGroup:addWidget(panels.keybindsPanel.panel.chatMode.on)
+ chatModeGroup:addWidget(panels.keybindsPanel.panel.chatMode.off)
+ chatModeGroup.onSelectionChange = chatModeChange
+ chatModeGroup:selectWidget(panels.keybindsPanel.panel.chatMode.on)
+
+ keyEditWindow = g_ui.displayUI("styles/controls/key_edit")
+ keyEditWindow:hide()
+ presetWindow = g_ui.displayUI("styles/controls/preset")
+ presetWindow:hide()
+ panels.keybindsPanel.presets.add.onClick = addNewPreset
+ panels.keybindsPanel.presets.copy.onClick = copyPreset
+ panels.keybindsPanel.presets.rename.onClick = renamePreset
+ panels.keybindsPanel.presets.remove.onClick = removePreset
+ panels.keybindsPanel.buttons.newAction:disable()
+ panels.keybindsPanel.buttons.newAction.onClick = newHotkeyAction
+ panels.keybindsPanel.buttons.reset.onClick = resetActions
+ panels.keybindsPanel.search.field.onTextChange = searchActions
+ panels.keybindsPanel.search.clear.onClick = function() panels.keybindsPanel.search.field:clearText() end
+ presetWindow.onEnter = okPresetWindow
+ presetWindow.onEscape = cancelPresetWindow
+ presetWindow.buttons.ok.onClick = okPresetWindow
+ presetWindow.buttons.cancel.onClick = cancelPresetWindow
+end
+
+function terminate_binds()
+ if presetWindow then
+ presetWindow:destroy()
+ presetWindow = nil
+ end
+
+ if chatModeGroup then
+ chatModeGroup:destroy()
+ chatModeGroup = nil
+ end
+
+ if keyEditWindow then
+ if keyEditWindow:isVisible() then
+ keyEditWindow:ungrabKeyboard()
+ disconnect(keyEditWindow, { onKeyDown = editKeybindKeyDown })
+ end
+ keyEditWindow:destroy()
+ keyEditWindow = nil
+ end
+
+ actionSearchEvent = nil
+end
+
+function listKeybindsComboBox(value)
+ local widget = panels.keybindsPanel.presets.list
+ presetOption(widget, 'currentPreset', value, false)
+ changedKeybinds = {}
+ changedHotkeys = {}
+ applyChangedOptions()
+ updateKeybinds()
+end
+
+function debug()
+ local currentOptionText = Keybind.currentPreset
+ local chatMode = Keybind.chatMode
+ local chatModeText = (chatMode == 1) and "Chat mode ON" or (chatMode == 2) and "Chat mode OFF" or "Unknown chat mode"
+ print(string.format("The current configuration is: %s, and the mode is: %s", currentOptionText, chatModeText))
+end
diff --git a/modules/client_options/options.lua b/modules/client_options/options.lua
index 53190cb64f..d89f726e7f 100644
--- a/modules/client_options/options.lua
+++ b/modules/client_options/options.lua
@@ -1,6 +1,5 @@
local options = dofile("data_options")
-
-local panels = {
+panels = {
generalPanel = nil,
graphicsPanel = nil,
soundPanel = nil,
@@ -9,24 +8,19 @@ local panels = {
interfaceHUD = nil,
interface = nil,
misc = nil,
- miscHelp = nil
+ miscHelp = nil,
+ keybindsPanel = nil
}
-- LuaFormatter off
local buttons = {{
text = "Controls",
icon = "/images/icons/icon_controls",
- open = "generalPanel"
- --[[ subCategories = {{
+ open = "generalPanel",
+ subCategories = {{
text = "General Hotkeys",
- open = "generalPanel"
- }, {
- text = "Action Bar Hotkeys",
- open = "Action_Bar_Hotkeys"
- }, {
- text = "Custom Hotkeys",
- open = "Custom_Hotkeys"
- }} ]]
+ open = "keybindsPanel"
+ }}
}, {
text = "Interface",
icon = "/images/icons/icon_interface",
@@ -105,6 +99,9 @@ local function setupComboBox()
local crosshairCombo = panels.interface:recursiveGetChildById('crosshair')
local antialiasingModeCombobox = panels.graphicsPanel:recursiveGetChildById('antialiasingMode')
local floorViewModeCombobox = panels.graphicsEffectsPanel:recursiveGetChildById('floorViewMode')
+ local framesRarityCombobox = panels.interface:recursiveGetChildById('frames')
+ local vocationPresetsCombobox = panels.keybindsPanel:recursiveGetChildById('list')
+ local listKeybindsPanel = panels.keybindsPanel:recursiveGetChildById('list')
for k, v in pairs({ { 'Disabled', 'disabled' }, { 'Default', 'default' }, { 'Full', 'full' } }) do
crosshairCombo:addOption(v[1], v[2])
@@ -132,6 +129,14 @@ local function setupComboBox()
setOption('floorViewMode', comboBox:getCurrentOption().data)
end
+ for k, v in pairs({ { 'None', 'none' }, { 'Frames', 'frames' }, { 'Corners', 'corners' } }) do
+ framesRarityCombobox:addOption(v[1], v[2])
+ end
+
+ framesRarityCombobox.onOptionChange = function(comboBox, option)
+ setOption('framesRarity', comboBox:getCurrentOption().data)
+ end
+
if not g_game.isEnabledBotProtection() then
local profileCombobox = panels.misc:recursiveGetChildById('profile')
@@ -144,6 +149,13 @@ local function setupComboBox()
end
end
+ for _, preset in ipairs(Keybind.presets) do
+ listKeybindsPanel:addOption(preset)
+ end
+ listKeybindsPanel.onOptionChange = function(comboBox, option)
+ setOption('listKeybindsPanel', option)
+ end
+ panels.keybindsPanel.presets.list:setCurrentOption(Keybind.currentPreset)
end
local function setup()
@@ -168,8 +180,6 @@ end
controller = Controller:new()
controller:setUI('options')
-controller:bindKeyDown('Ctrl+Shift+F', function() toggleOption('fullscreen') end)
-controller:bindKeyDown('Ctrl+N', toggleDisplays)
function controller:onInit()
for k, obj in pairs(options) do
@@ -190,6 +200,7 @@ function controller:onInit()
'/images/topbuttons/logout', toggle)
panels.generalPanel = g_ui.loadUI('styles/controls/general',controller.ui.optionsTabContent)
+ panels.keybindsPanel = g_ui.loadUI('styles/controls/keybinds',controller.ui.optionsTabContent)
panels.graphicsPanel = g_ui.loadUI('styles/graphics/graphics',controller.ui.optionsTabContent)
panels.graphicsEffectsPanel = g_ui.loadUI('styles/graphics/effects',controller.ui.optionsTabContent)
@@ -207,6 +218,39 @@ function controller:onInit()
configureCharacterCategories()
addEvent(setup)
+ init_binds()
+
+ Keybind.new("UI", "Toggle Fullscreen", "Ctrl+Shift+F", "")
+ Keybind.bind("UI", "Toggle Fullscreen", {
+ {
+ type = KEY_DOWN,
+ callback = function() toggleOption('fullscreen') end,
+ }
+ })
+ Keybind.new("UI", "Show/hide FPS / lag indicator", "", "")
+ Keybind.bind("UI", "Show/hide FPS / lag indicator", {{
+ type = KEY_DOWN,
+ callback = function()
+ toggleOption('showPing')
+ toggleOption('showFps')
+ end
+ }})
+
+ Keybind.new("UI", "Show/hide Creature Names and Bars", "Ctrl+N", "")
+ Keybind.bind("UI", "Show/hide Creature Names and Bars", {
+ {
+ type = KEY_DOWN,
+ callback = toggleDisplays,
+ }
+ })
+
+ Keybind.new("Sound", "Mute/unmute", "", "")
+ Keybind.bind("Sound", "Mute/unmute", {
+ {
+ type = KEY_DOWN,
+ callback = function() toggleOption('enableAudio') end,
+ }
+ })
end
function controller:onTerminate()
@@ -215,6 +259,21 @@ function controller:onTerminate()
panels = {}
extraWidgets = {}
buttons = {}
+ Keybind.delete("UI", "Toggle Full Screen")
+ Keybind.delete("UI", "Show/hide Creature Names and Bars")
+ Keybind.delete("Sound", "Mute/unmute")
+
+ terminate_binds()
+end
+
+function controller:onGameStart()
+ if g_settings.getBoolean("autoSwitchPreset") then
+ local name = g_game.getCharacterName()
+ if Keybind.selectPreset(name) then
+ panels.keybindsPanel.presets.list:setCurrentOption(name, true)
+ updateKeybinds()
+ end
+ end
end
function setOption(key, value, force)
@@ -290,6 +349,7 @@ function toggle()
end
end
show()
+ updateKeybinds()
end
function addTab(name, panel, icon)
diff --git a/modules/client_options/options.otmod b/modules/client_options/options.otmod
index 7671184cab..2b44193a17 100644
--- a/modules/client_options/options.otmod
+++ b/modules/client_options/options.otmod
@@ -4,6 +4,6 @@ Module
author: edubart, BeniS
website: https://github.com/edubart/otclient
sandboxed: true
- scripts: [ options ]
+ scripts: [ options, keybins ]
@onLoad: controller:init()
@onUnload: controller:terminate()
diff --git a/modules/client_options/styles/controls/key_edit.otui b/modules/client_options/styles/controls/key_edit.otui
new file mode 100644
index 0000000000..aeac99932a
--- /dev/null
+++ b/modules/client_options/styles/controls/key_edit.otui
@@ -0,0 +1,66 @@
+MainWindow
+ size: 400 225
+ layout:
+ type: verticalBox
+ spacing: 10
+ fit-children: true
+
+ Label
+ font: verdana-11px-monochrome
+ text-align: center
+ color: #c0c0c0
+ text: Mode: "Chat On"
+
+ FlatPanel
+ id: keyCombo
+ height: 33
+ font: verdana-11px-monochrome
+ text-align: center
+ color: #c0c0c0
+
+ Label
+ id: info
+ font: verdana-11px-monochrome
+ color: #c0c0c0
+ text-wrap: true
+ text-auto-resize: true
+
+ Label
+ id: alone
+ font: verdana-11px-monochrome
+ color: #c0c0c0
+ !text: tr("This action must be bound to a single key.")
+ visible: false
+
+ Label
+ id: used
+ font: verdana-11px-monochrome
+ text-align: center
+ color: #f75f5f
+ !text: tr("This hotkey is already in use.")
+ visible: false
+
+ HorizontalSeparator
+
+ Panel
+ id: buttons
+ height: 20
+ layout:
+ type: horizontalBox
+ align-right: true
+ spacing: -5
+
+ SmallButton
+ id: ok
+ width: 40
+ !text: tr("Ok")
+
+ SmallButton
+ id: clear
+ width: 45
+ !text: tr("Clear")
+ SmallButton
+ id: cancel
+ width: 45
+ !text: tr("Cancel")
+
diff --git a/modules/client_options/styles/controls/keybinds.otui b/modules/client_options/styles/controls/keybinds.otui
new file mode 100644
index 0000000000..98ca8999c4
--- /dev/null
+++ b/modules/client_options/styles/controls/keybinds.otui
@@ -0,0 +1,285 @@
+RadioBox < CheckBox
+ image-source: /images/ui/outfits/checkbox_round
+
+OptionContainer < FlatPanel
+ height: 22
+ padding: 3
+KeybindsTableRow < UITableRow
+ focusable: true
+ height: 20
+ background-color: alpha
+ even-background-color: alpha
+ odd-background-color: #484848
+ text-align: left
+ @onFocusChange: for _, child in ipairs(self:getChildren()) do child:setChecked(self:isFocused()) end
+ layout: horizontalBox
+
+ $focus:
+ background-color: #585858
+
+KeybindsTableColumn < Label
+ color: #c0c0c0
+ text-align: left
+ focusable: false
+ text-offset: 2 0
+ font: verdana-11px-monochrome
+
+EditableKeybindsTableColumn < Label
+ color: #c0c0c0
+ text-align: left
+ focusable: false
+ text-offset: 2 0
+ font: verdana-11px-monochrome
+ @onCheckChange: self.edit:setVisible(self:isChecked())
+
+ Button
+ id: edit
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ size: 13 13
+ margin-right: 2
+ icon: /images/ui/icon-edit
+ visible: false
+
+EditableHotkeysTableColumn < Label
+ color: #c0c0c0
+ text-align: left
+ focusable: false
+ text-offset: 18 0
+ font: verdana-11px-monochrome
+ @onCheckChange: self.edit:setVisible(self:isChecked())
+
+ UIItem
+ id: item
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: parent.left
+ size: 16 16
+ virtual: true
+ phantom: true
+
+ Button
+ id: edit
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ size: 13 13
+ margin-right: 2
+ icon: /images/options/icon-edit
+ visible: false
+
+KeybindsTableHeaderRow < TableHeaderRow
+ padding: 0
+ height: 18
+
+KeybindsTableHeaderColumn < UITableHeaderColumn
+ font: verdana-11px-monochrome
+ text-offset: 2 0
+ text-align: left
+ height: 14
+ color: #c0c0c0
+ background: #363636
+
+UIWidget
+ anchors.fill: parent
+ visible: false
+ Panel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ id: presets
+ height: 20
+ layout:
+ type: horizontalBox
+ spacing: 5
+
+ QtComboBox
+ id: list
+ width: 320
+
+ SmallButton
+ id: add
+ width: 30
+ !text: tr("Add")
+ @onClick: addNewPreset()
+
+ SmallButton
+ id: copy
+ width: 35
+ !text: tr("Copy")
+ @onClick: copyPreset()
+
+ SmallButton
+ id: rename
+ width: 45
+ !text: tr("Rename")
+ @onClick : renamePreset()
+
+ SmallButton
+ id: remove
+ width: 45
+ !text: tr("Remove")
+ @onClick : removePreset()
+
+ SmallReversedQtPanel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: prev.bottom
+ height: 22
+ margin-top: 7
+
+ OptionCheckBoxMarked
+ id: autoSwitchPreset
+ !text: tr('Auto-Switch Hotkey Preset')
+ !tooltip: tr('If you have named your hotkey presets the same like your\ncharacters and have checked this option, the preset with the same\nname as the corresponding character will be activated upon login\nautomatically.')
+
+ SmallReversedQtPanel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: prev.bottom
+ height: 22
+ margin-top: 7
+ id: panel
+ OptionContainer
+ anchors.left: parent.left
+ anchors.right: parent.right
+ image-source: ""
+ !tooltip: tr('There are two chat modes for each hotkey preset: Chat On and Chat\n Off.You can find a small button that tells you which chat mode is\n currently active on the right side of the entry line of the chat console.Press this button to switch between the two chat modes.\nOf course, you can also set a hotkey to toggle between Chat On and Off.\n- Chat On\nIn this mode, you will be able to write text in the entry line of the chat console.\nHowever, if you have assigned an action to a letter key, the action will be triggered.\n- Chat On*\nThis is the temporary Chat On mode.\nIn the default hotkey preset, the Return key switches between Chat Off and this mode.\nIf the chat mode is off and you press this hotkey, it temporarily activates the chat mode so that you can write one message in the console.\nAs soon as you press Return to send the message, your chat mode will be set to Chat Off again so that you can continue walking with WASD, for example.\n- Chat Off\nIn this mode, you will not be able to write any text in the entry line of the console, but you can use your predefined hotkeys to walk or cast spells, for example.\nYou can assign functions to all keys of your keyboard, e.g. walk with WASD.')
+
+ id: chatMode
+ $!first:
+ margin-left:40
+ layout:
+ type: horizontalBox
+ spacing: 127
+
+ RadioBox
+ id: on
+ text-horizontal-auto-resize: true
+ !text: tr("Chat Mode On")
+ margin-bottom: -5
+ text-offset: 20 -3
+ RadioBox
+ id: off
+ text-horizontal-auto-resize: true
+ !text: tr("Chat Mode Off")
+ margin-bottom: -5
+ text-offset: 20 -3
+ UIWidget
+ id: toolTipWidget
+ image-source: /images/icons/show_gui_help_grey
+ size: 12 12
+ anchors.right: parent.right
+ margin-right: 3
+ !tooltip: tr('There are two chat modes for each hotkey preset: Chat On and Chat \nOff. You can find a small button that tells you which chat mode is \ncurrently active on the right side of the entry line of the chat \nconsole. Press this button to switch between the two chat modes. Of \ncourse, you can also set a hotkey to toggle between Chat On and Off.\n\tChat On\n\t\tIn this mode, you will be able to write text in the entry line of\n the chat console. However, if you have assigned an action to \na letter key, the action will be \t\ttriggered.\n\n\n\tChat On\n\t\tThis is the temporary Chat On mode. In the default hotkey \npreset, the Return key switches between Chat Off and this\n mode. If the chat mode is off and you press \t\tthis hotkey, it \ntemporarily activates the chat mode so that you can write \none message in the console. As soon as you press Return to \nsend the message, your chat \t\tmode will be set to \nChat Off again so that you can continue walking with WASD, for\n example.\n\n\tChat Off\n\t\tIn this mode, you will not be able to write any text in the entry line of the\n console, but you can use your predefined\n hotkeys to walk or cast spells, for \t\texample. You can assign\n functions to all keys of your keyboard, e.g. walk with WASD')
+
+ Panel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: prev.bottom
+ id: search
+ height: 20
+ margin-top: 5
+ layout:
+ type: horizontalBox
+ spacing: 6
+
+ Label
+ text-offset: 0 3
+ font: verdana-11px-monochrome
+ !text: tr('Type to search for a hotkey:')
+
+ TextEdit
+ id: field
+ width: 300
+ @onTextChange: searchActions()
+
+ UIButton
+ id: clear
+ image-source: /images/ui/button-clear-18x18-up
+ image-clip: 0 0 20 20
+ width: 20
+
+ $pressed:
+ image-source: /images/ui/button-clear-18x18-down
+ @onClick : self:getParent().field:clearText()
+
+ Panel
+ id: tablePanel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: prev.bottom
+ height: 305
+ margin-top: 5
+ image-source: /images/game/actionbar/1pixel-down-frame
+ image-border: 1
+ background: #404040
+
+ Table
+ id: keybinds
+ anchors.fill: parent
+ margin: 1
+ table-data: keybindsData
+ row-style: KeybindsTableRow
+ column-style: KeybindsTableColumn
+ header-row-style: KeybindsTableHeaderRow
+ header-column-style: KeybindsTableHeaderColumn
+
+ KeybindsTableHeaderRow
+ id: header
+
+ KeybindsTableHeaderColumn
+ !text: tr("Action")
+ width: 286
+
+ VerticalSeparator
+
+ KeybindsTableHeaderColumn
+ !text: tr("Primary Key")
+ width: 100
+
+ VerticalSeparator
+
+ KeybindsTableHeaderColumn
+ !text: tr("Secondary Key")
+ width: 100
+
+ HorizontalSeparator
+
+ TableData
+ id: keybindsData
+ anchors.fill: keybinds
+ margin-top: 20
+ padding-bottom: 1
+ vertical-scrollbar: scrollBar
+
+ VerticalQtScrollBar
+ id: scrollBar
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ step: 16
+ pixels-scroll: true
+
+ Panel
+ id: buttons
+ height: 20
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: prev.bottom
+
+ SmallButton
+ id: newAction
+ anchors.top: parent.top
+ anchors.left: parent.left
+ width: 75
+ !text: tr('New Action')
+ visible: false
+ @onClick : newHotkeyAction()
+
+ SmallButton
+ id: reset
+ anchors.top: parent.top
+ anchors.right: parent.right
+ width: 45
+ margin-top: 8
+ !text: tr('Reset')
+ @onClick : resetActions()
diff --git a/modules/client_options/styles/controls/preset.otui b/modules/client_options/styles/controls/preset.otui
new file mode 100644
index 0000000000..9b3d4ab223
--- /dev/null
+++ b/modules/client_options/styles/controls/preset.otui
@@ -0,0 +1,43 @@
+MainWindow
+ width: 360
+ text: Add hotkey preset
+ layout:
+ type: verticalBox
+ fit-children: true
+
+ Label
+ id: info
+ font: verdana-11px-monochrome
+ color: #c0c0c0
+ text: Enter a name for the new preset:
+
+ TextEdit
+ id: field
+ margin-top: 3
+ height: 16
+ padding: 1 4
+ font: verdana-11px-monochrome
+ color: #c0c0c0
+
+ HorizontalSeparator
+ margin-top: 10
+
+ Panel
+ id: buttons
+ margin-top: 10
+ height: 20
+ layout:
+ type: horizontalBox
+ align-right: true
+ spacing: -8
+
+ SmallButton
+ id: ok
+ width: 40
+ !text: tr("Ok")
+
+ SmallButton
+ id: cancel
+ width: 45
+ !text: tr("Cancel")
+
diff --git a/modules/client_options/styles/interface/interface.otui b/modules/client_options/styles/interface/interface.otui
index bddc74b729..58529dc3bc 100644
--- a/modules/client_options/styles/interface/interface.otui
+++ b/modules/client_options/styles/interface/interface.otui
@@ -11,7 +11,6 @@ UIWidget
id: enableHighlightMouseTarget
!text: tr('Highlight mouse target')
-
SmallReversedQtPanel
anchors.left: parent.left
anchors.right: parent.right
@@ -77,4 +76,59 @@ UIWidget
margin-left: 10
anchors.verticalCenter: prev.verticalCenter
anchors.left: prev.right
- mouse-scroll: false
\ No newline at end of file
+ mouse-scroll: false
+
+ SmallReversedQtPanel
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: prev.bottom
+ margin-top: 7
+ height: 86
+
+ Label
+ !text: 'Colourise Loot Value: '
+ anchors.left: parent.left
+ anchors.top: parent.top
+ margin-left: 10
+ margin-top: -25
+ color: #c0c0c0ff
+ anchors.verticalCenter: parent.verticalCenter
+
+ QtComboBox
+ id: frames
+ width: 120
+ margin-left: 10
+ anchors.verticalCenter: prev.verticalCenter
+ anchors.left: prev.right
+ mouse-scroll: false
+
+
+ UIWidget
+ id: toolTipWidget
+ image-source: /images/icons/show_gui_help_grey
+ size: 12 12
+ anchors.right: parent.right
+ margin-right: 3
+ !tooltip: tr('If you select Frames or Corners, your loot will be marked with \ndifferent colours in loot messages, containers and the Bestiary.\n\nThe colour depends on the loot value, which can be determined in\n the Items Cyclopedia, either using the NPC buy value, the average\n Market price or your own preferred value.')
+
+ OptionCheckBoxMarked
+ id: showExpiryInInvetory
+ anchors.top: frames.bottom
+ margin-top: 5
+ !text: tr('Show Expiry In Invetory')
+ !tooltip: tr('Check this box to see how much time or how many charges are left \non your equipped items')
+
+ OptionCheckBoxMarked
+ id: showExpiryInContainers
+ anchors.top: prev.bottom
+ margin-top: 5
+ !text: tr('Show Expiry In Containers')
+ !tooltip: tr('Check this box to see how mmuch time or how many charges the \nitems in your open containers have left')
+
+ OptionCheckBoxMarked
+ id: showExpiryOnUnusedItems
+ anchors.top: prev.bottom
+ margin-top: 5
+ !text: tr('Show Expiry On Unused Items')
+ !tooltip: tr('Check this box to see how much time or how many charges are left\n on items that have not been used yet')
+ enabled: false
diff --git a/modules/client_options/styles/misc/help.otui b/modules/client_options/styles/misc/help.otui
index 784cefa5b3..a23a309d08 100644
--- a/modules/client_options/styles/misc/help.otui
+++ b/modules/client_options/styles/misc/help.otui
@@ -30,7 +30,7 @@ UIWidget
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
- height: 33
+ height: 55
margin-top: 5
QtButton
@@ -40,5 +40,20 @@ UIWidget
margin-right: 10
anchors.left: parent.left
anchors.top: parent.top
- @onClick: g_platform.openUrl("https://github.com/mehah/otclient/wiki")
-
+ @onClick: |
+ g_settings.clear()
+ controller:scheduleEvent(function()
+ g_app.restart()
+ end, 1000)
+ QtButton
+ !text: tr('Change language')
+ size: 130 20
+ margin-top: 5
+ anchors.top: prev.bottom
+ anchors.left: parent.left
+ @onClick: |
+ if g_game.isOnline() then
+ g_logger.warning("To prevent errors, change the language while offline." )
+ else
+ modules.client_locales.createWindow()
+ end
diff --git a/modules/client_terminal/terminal.lua b/modules/client_terminal/terminal.lua
index 97cb13ecc4..2bc613e1ff 100644
--- a/modules/client_terminal/terminal.lua
+++ b/modules/client_terminal/terminal.lua
@@ -150,7 +150,12 @@ function init()
terminalButton = modules.client_topmenu.addTopRightToggleButton('terminalButton', tr('Terminal') .. ' (Ctrl + T)',
'/images/topbuttons/terminal', toggle)
- g_keyboard.bindKeyDown('Ctrl+T', toggle)
+ Keybind.new("Misc.", "Toggle Terminal", "Ctrl+T", "")
+ Keybind.bind("Misc.", "Toggle Terminal", {{
+ type = KEY_DOWN,
+ callback = toggle
+ }})
+
commandHistory = g_settings.getList('terminal-history')
@@ -214,7 +219,7 @@ function terminate()
}
g_settings.setNode('terminal-window', settings)
- g_keyboard.unbindKeyDown('Ctrl+T')
+ Keybind.delete("Misc.", "Toggle Terminal")
g_logger.setOnLog(nil)
terminalWindow:destroy()
terminalButton:destroy()
diff --git a/modules/client_topmenu/topmenu.lua b/modules/client_topmenu/topmenu.lua
index f21fb6a6fe..40dab3f738 100644
--- a/modules/client_topmenu/topmenu.lua
+++ b/modules/client_topmenu/topmenu.lua
@@ -92,7 +92,13 @@ function init()
topLeftYoutubeLink = topMenu:recursiveGetChildById('youtubeIcon')
topLeftDiscordLink = topMenu:recursiveGetChildById('discordIcon')
- g_keyboard.bindKeyDown('Ctrl+Shift+T', toggle)
+ Keybind.new("UI", "Toggle Top Menu", "Ctrl+Shift+T", "")
+ Keybind.bind("UI", "Toggle Top Menu", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
if Services.websites then
managerAccountsButton = modules.client_topmenu.addTopRightRegularButton('hotkeysButton', tr('Manage Account'),
nil, openManagerAccounts)
@@ -113,7 +119,7 @@ function terminate()
})
topMenu:destroy()
- if PingWidget then
+ if PingWidget and not PingWidget:isDestroyed() then
PingWidget:destroy()
PingWidget = nil
end
@@ -121,7 +127,7 @@ function terminate()
managerAccountsButton:destroy()
managerAccountsButton = nil
end
-
+ Keybind.delete("UI", "Toggle Top Menu")
end
function hide()
@@ -138,8 +144,23 @@ function online()
showGameButtons()
addEvent(function()
- if modules.client_options.getOption('showPing') and
- (g_game.getFeature(GameClientPing) or g_game.getFeature(GameExtendedClientPing)) then
+ local showPing = modules.client_options.getOption('showPing')
+ local pingFeatureAvailable = g_game.getFeature(GameClientPing) or g_game.getFeature(GameExtendedClientPing)
+
+ if not PingWidget then
+ PingWidget = g_ui.loadUI("pingFps", modules.game_interface.getMapPanel())
+ MainPingPanel = g_ui.createWidget("testPingPanel", PingWidget:getChildByIndex(1))
+ MainPingPanel:setId("ping")
+
+ pingImg = MainPingPanel:getChildByIndex(1)
+ pingPanel = MainPingPanel:getChildByIndex(2)
+
+ mainFpsPanel = g_ui.createWidget("testPingPanel", PingWidget:getChildByIndex(2))
+ mainFpsPanel:setId("fps")
+ fpsPanel2 = mainFpsPanel:getChildByIndex(2)
+ end
+
+ if showPing and pingFeatureAvailable then
pingLabel:show()
if pingPanel then
pingPanel:show()
@@ -152,32 +173,13 @@ function online()
pingImg:hide()
end
end
- end)
- if PingWidget then
- return
- end
- PingWidget = g_ui.loadUI("pingFps", modules.game_interface.getMapPanel())
-
- MainPingPanel = g_ui.createWidget("testPingPanel", PingWidget:getChildByIndex(1))
- MainPingPanel.setId(MainPingPanel, "ping")
- pingImg = MainPingPanel.getChildByIndex(MainPingPanel, 1)
- pingPanel = MainPingPanel.getChildByIndex(MainPingPanel, 2)
- if modules.client_options.getOption('showPing') then
- pingImg:setVisible(true)
- pingPanel:setVisible(true)
- else
- pingImg:setVisible(false)
- pingPanel:setVisible(true)
- end
- mainFpsPanel = g_ui.createWidget("testPingPanel", PingWidget:getChildByIndex(2))
- mainFpsPanel.setId(mainFpsPanel, "fps")
- fpsPanel2 = mainFpsPanel.getChildByIndex(mainFpsPanel, 2)
- if modules.client_options.getOption('showFps') then
- fpsPanel2:setVisible(true)
- else
- fpsPanel2:setVisible(false)
- end
+ pingImg:setVisible(showPing)
+ pingPanel:setVisible(showPing)
+
+ local showFps = modules.client_options.getOption('showFps')
+ fpsPanel2:setVisible(showFps)
+ end)
end
function offline()
diff --git a/modules/corelib/bitwise.lua b/modules/corelib/bitwise.lua
index c9901c4e69..09d1471190 100644
--- a/modules/corelib/bitwise.lua
+++ b/modules/corelib/bitwise.lua
@@ -14,4 +14,4 @@ end
function Bit.clearbit(x, p)
return Bit.hasBit(x, p) and x - p or x
-end
+end
\ No newline at end of file
diff --git a/modules/corelib/corelib.otmod b/modules/corelib/corelib.otmod
index 80c3359b77..7c665fb4f5 100644
--- a/modules/corelib/corelib.otmod
+++ b/modules/corelib/corelib.otmod
@@ -17,6 +17,7 @@ Module
dofile 'globals'
dofile 'config'
dofile 'settings'
+ dofile 'keybind'
dofile 'keyboard'
dofile 'mouse'
dofile 'net'
@@ -28,4 +29,7 @@ Module
dofile 'outputmessage'
dofile 'json'
- dofile 'http'
\ No newline at end of file
+ dofile 'http'
+ Keybind.init()
+
+ @onUnload: Keybind.terminate()
diff --git a/modules/corelib/keybind.lua b/modules/corelib/keybind.lua
new file mode 100644
index 0000000000..a61cfdf553
--- /dev/null
+++ b/modules/corelib/keybind.lua
@@ -0,0 +1,1036 @@
+
+CHAT_MODE = {
+ ON = 1,
+ OFF = 2
+}
+
+Keybind = {
+ presets = {},
+ presetToIndex = {},
+ currentPreset = nil,
+ configs = {
+ keybinds = {},
+ hotkeys = {}
+ },
+ defaultKeys = {
+ [CHAT_MODE.ON] = {},
+ [CHAT_MODE.OFF] = {}
+ },
+ defaultKeybinds = {},
+ hotkeys = {
+ [CHAT_MODE.ON] = {},
+ [CHAT_MODE.OFF] = {}
+ },
+ chatMode = CHAT_MODE.ON,
+
+ reservedKeys = {
+ ["Up"] = true,
+ ["Down"] = true,
+ ["Left"] = true,
+ ["Right"] = true
+ }
+}
+
+KEY_UP = 1
+KEY_DOWN = 2
+KEY_PRESS = 3
+
+HOTKEY_ACTION = {
+ USE_YOURSELF = 1,
+ USE_CROSSHAIR = 2,
+ USE_TARGET = 3,
+ EQUIP = 4,
+ USE = 5,
+ TEXT = 6,
+ TEXT_AUTO = 7,
+ SPELL = 8
+}
+
+function Keybind.init()
+ connect(g_game, { onGameStart = Keybind.online, onGameEnd = Keybind.offline })
+
+ Keybind.presets = g_settings.getList("controls-presets")
+
+ if #Keybind.presets == 0 then
+ Keybind.presets = { "Druid", "Knight", "Paladin", "Sorcerer" }
+ Keybind.currentPreset = "Druid"
+ else
+ Keybind.currentPreset = g_settings.getValue("controls-preset-current")
+ end
+
+ for index, preset in ipairs(Keybind.presets) do
+ Keybind.presetToIndex[preset] = index
+ end
+
+ if not g_resources.directoryExists("/controls") then
+ g_resources.makeDir("/controls")
+ end
+
+ if not g_resources.directoryExists("/controls/keybinds") then
+ g_resources.makeDir("/controls/keybinds")
+ end
+
+ if not g_resources.directoryExists("/controls/hotkeys") then
+ g_resources.makeDir("/controls/hotkeys")
+ end
+
+ for _, preset in ipairs(Keybind.presets) do
+ Keybind.configs.keybinds[preset] = g_configs.create("/controls/keybinds/" .. preset .. ".otml")
+ Keybind.configs.hotkeys[preset] = g_configs.create("/controls/hotkeys/" .. preset .. ".otml")
+ end
+
+ for preset, config in pairs(Keybind.configs.hotkeys) do
+ for chatMode = CHAT_MODE.ON, CHAT_MODE.OFF do
+ Keybind.hotkeys[chatMode][preset] = {}
+ local hotkeyId = 1
+ local hotkeys = config:getNode(chatMode)
+
+ if hotkeys then
+ local hotkey = hotkeys[tostring(hotkeyId)]
+ while hotkey do
+ if hotkey.data.parameter then
+ hotkey.data.parameter = "\"" .. hotkey.data.parameter .. "\"" -- forcing quotes cause OTML is not saving them, just wow
+ end
+
+ table.insert(Keybind.hotkeys[chatMode][preset], hotkey)
+ hotkeyId = hotkeyId + 1
+
+ hotkey = hotkeys[tostring(hotkeyId)]
+ end
+ end
+ end
+ end
+end
+
+function Keybind.terminate()
+ disconnect(g_game, { onGameStart = Keybind.online, onGameEnd = Keybind.offline })
+
+ for _, preset in ipairs(Keybind.presets) do
+ Keybind.configs.keybinds[preset]:save()
+ Keybind.configs.hotkeys[preset]:save()
+ end
+
+ g_settings.setList("controls-presets", Keybind.presets)
+ g_settings.setValue("controls-preset-current", Keybind.currentPreset)
+ g_settings.save()
+end
+
+function Keybind.online()
+ for _, hotkey in ipairs(Keybind.hotkeys[Keybind.chatMode][Keybind.currentPreset]) do
+ Keybind.bindHotkey(hotkey.hotkeyId, Keybind.chatMode)
+ end
+end
+
+function Keybind.offline()
+ for _, hotkey in ipairs(Keybind.hotkeys[Keybind.chatMode][Keybind.currentPreset]) do
+ Keybind.unbindHotkey(hotkey.hotkeyId, Keybind.chatMode)
+ end
+end
+
+function Keybind.new(category, action, primary, secondary, alone)
+ local index = category .. '_' .. action
+ if Keybind.defaultKeybinds[index] then
+ pwarning(string.format("Keybind for [%s: %s] is already in use", category, action))
+ return
+ end
+
+ local keys = {}
+ if type(primary) == "string" then
+ keys[CHAT_MODE.ON] = { primary = primary }
+ keys[CHAT_MODE.OFF] = { primary = primary }
+ else
+ keys[CHAT_MODE.ON] = { primary = primary[CHAT_MODE.ON] }
+ keys[CHAT_MODE.OFF] = { primary = primary[CHAT_MODE.OFF] }
+ end
+
+ if type(secondary) == "string" then
+ keys[CHAT_MODE.ON].secondary = secondary
+ keys[CHAT_MODE.OFF].secondary = secondary
+ else
+ keys[CHAT_MODE.ON].secondary = secondary[CHAT_MODE.ON]
+ keys[CHAT_MODE.OFF].secondary = secondary[CHAT_MODE.OFF]
+ end
+
+ keys[CHAT_MODE.ON].primary = retranslateKeyComboDesc(keys[CHAT_MODE.ON].primary)
+
+ if keys[CHAT_MODE.ON].secondary then
+ keys[CHAT_MODE.ON].secondary = retranslateKeyComboDesc(keys[CHAT_MODE.ON].secondary)
+ end
+
+ keys[CHAT_MODE.OFF].primary = retranslateKeyComboDesc(keys[CHAT_MODE.OFF].primary)
+
+ if keys[CHAT_MODE.OFF].secondary then
+ keys[CHAT_MODE.OFF].secondary = retranslateKeyComboDesc(keys[CHAT_MODE.OFF].secondary)
+ end
+
+ if Keybind.defaultKeys[CHAT_MODE.ON][keys[CHAT_MODE.ON].primary] then
+ local primaryIndex = Keybind.defaultKeys[CHAT_MODE.ON][keys[CHAT_MODE.ON].primary]
+ local primaryKeybind = Keybind.defaultKeybinds[primaryIndex]
+ perror(string.format("Default primary key (Chat Mode On) assigned to [%s: %s] is already in use by [%s: %s]",
+ category, action, primaryKeybind.category, primaryKeybind.action))
+ return
+ end
+
+ if Keybind.defaultKeys[CHAT_MODE.OFF][keys[CHAT_MODE.OFF].primary] then
+ local primaryIndex = Keybind.defaultKeys[CHAT_MODE.OFF][keys[CHAT_MODE.OFF].primary]
+ local primaryKeybind = Keybind.defaultKeybinds[primaryIndex]
+ perror(string.format("Default primary key (Chat Mode Off) assigned to [%s: %s] is already in use by [%s: %s]",
+ category, action, primaryKeybind.category, primaryKeybind.action))
+ return
+ end
+
+ if keys[CHAT_MODE.ON].secondary and Keybind.defaultKeys[CHAT_MODE.ON][keys[CHAT_MODE.ON].secondary] then
+ local secondaryIndex = Keybind.defaultKeys[CHAT_MODE.ON][keys[CHAT_MODE.ON].secondary]
+ local secondaryKeybind = Keybind.defaultKeybinds[secondaryIndex]
+ perror(string.format("Default secondary key (Chat Mode On) assigned to [%s: %s] is already in use by [%s: %s]",
+ category, action, secondaryKeybind.category, secondaryKeybind.action))
+ return
+ end
+
+ if keys[CHAT_MODE.OFF].secondary and Keybind.defaultKeys[CHAT_MODE.OFF][keys[CHAT_MODE.OFF].secondary] then
+ local secondaryIndex = Keybind.defaultKeys[CHAT_MODE.OFF][keys[CHAT_MODE.OFF].secondary]
+ local secondaryKeybind = Keybind.defaultKeybinds[secondaryIndex]
+ perror(string.format("Default secondary key (Chat Mode Off) assigned to [%s: %s] is already in use by [%s: %s]",
+ category, action, secondaryKeybind.category, secondaryKeybind.action))
+ return
+ end
+
+ if keys[CHAT_MODE.ON].primary then
+ Keybind.defaultKeys[CHAT_MODE.ON][keys[CHAT_MODE.ON].primary] = index
+ end
+
+ if keys[CHAT_MODE.OFF].primary then
+ Keybind.defaultKeys[CHAT_MODE.OFF][keys[CHAT_MODE.OFF].primary] = index
+ end
+
+ Keybind.defaultKeybinds[index] = {
+ category = category,
+ action = action,
+ keys = keys,
+ alone = alone
+ }
+
+ if keys[CHAT_MODE.ON].secondary then
+ Keybind.defaultKeys[CHAT_MODE.ON][keys[CHAT_MODE.ON].secondary] = index
+ end
+ if keys[CHAT_MODE.OFF].secondary then
+ Keybind.defaultKeys[CHAT_MODE.OFF][keys[CHAT_MODE.OFF].secondary] = index
+ end
+end
+
+function Keybind.delete(category, action)
+ local index = category .. '_' .. action
+ local keybind = Keybind.defaultKeybinds[index]
+
+ if not keybind then
+ return
+ end
+
+ Keybind.unbind(category, action)
+
+ local keysOn = keybind.keys[CHAT_MODE.ON]
+ local keysOff = keybind.keys[CHAT_MODE.OFF]
+
+ local primaryOn = keysOn.primary and tostring(keysOn.primary) or nil
+ local primaryOff = keysOff.primary and tostring(keysOff.primary) or nil
+ local secondaryOn = keysOn.secondary and tostring(keysOn.secondary) or nil
+ local secondaryOff = keysOff.secondary and tostring(keysOff.secondary) or nil
+
+ if primaryOn and primaryOn:len() > 0 then
+ Keybind.defaultKeys[CHAT_MODE.ON][primaryOn] = nil
+ end
+ if secondaryOn and secondaryOn:len() > 0 then
+ Keybind.defaultKeys[CHAT_MODE.ON][secondaryOn] = nil
+ end
+
+ if primaryOff and primaryOff:len() > 0 then
+ Keybind.defaultKeys[CHAT_MODE.OFF][primaryOff] = nil
+ end
+ if secondaryOff and secondaryOff:len() > 0 then
+ Keybind.defaultKeys[CHAT_MODE.OFF][secondaryOff] = nil
+ end
+
+ Keybind.defaultKeybinds[index] = nil
+end
+
+function Keybind.bind(category, action, callbacks, widget)
+ local index = category .. '_' .. action
+ local keybind = Keybind.defaultKeybinds[index]
+
+ if not keybind then
+ return
+ end
+
+ keybind.callbacks = callbacks
+ keybind.widget = widget
+
+ local keys = Keybind.getKeybindKeys(category, action)
+
+ for _, callback in ipairs(keybind.callbacks) do
+ if callback.type == KEY_UP then
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ g_keyboard.bindKeyUp(keys.primary, callback.callback, keybind.widget, callback.alone)
+ end
+ end
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ g_keyboard.bindKeyUp(keys.secondary, callback.callback, keybind.widget, callback.alone)
+ end
+ end
+ elseif callback.type == KEY_DOWN then
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ g_keyboard.bindKeyDown(keys.primary, callback.callback, keybind.widget, callback.alone)
+ end
+ end
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ g_keyboard.bindKeyDown(keys.secondary, callback.callback, keybind.widget, callback.alone)
+ end
+ end
+ elseif callback.type == KEY_PRESS then
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ g_keyboard.bindKeyPress(keys.primary, callback.callback, keybind.widget)
+ end
+ end
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ g_keyboard.bindKeyPress(keys.secondary, callback.callback, keybind.widget)
+ end
+ end
+ end
+ end
+end
+
+function Keybind.unbind(category, action)
+ local index = category .. '_' .. action
+ local keybind = Keybind.defaultKeybinds[index]
+
+ if not keybind or not keybind.callbacks then
+ return
+ end
+
+ local keys = Keybind.getKeybindKeys(category, action)
+
+ for _, callback in ipairs(keybind.callbacks) do
+ if callback.type == KEY_UP then
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ g_keyboard.unbindKeyUp(keys.primary, callback.callback, keybind.widget)
+ end
+ end
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ g_keyboard.unbindKeyUp(keys.secondary, callback.callback, keybind.widget)
+ end
+ end
+ elseif callback.type == KEY_DOWN then
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ g_keyboard.unbindKeyDown(keys.primary, callback.callback, keybind.widget)
+ end
+ end
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ g_keyboard.unbindKeyDown(keys.secondary, callback.callback, keybind.widget)
+ end
+ end
+ elseif callback.type == KEY_PRESS then
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ g_keyboard.unbindKeyPress(keys.primary, callback.callback, keybind.widget)
+ end
+ end
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ g_keyboard.unbindKeyPress(keys.secondary, callback.callback, keybind.widget)
+ end
+ end
+ end
+ end
+end
+
+function Keybind.newPreset(presetName)
+ if Keybind.presetToIndex[presetName] then
+ return
+ end
+
+ table.insert(Keybind.presets, presetName)
+ Keybind.presetToIndex[presetName] = #Keybind.presets
+
+ Keybind.configs.keybinds[presetName] = g_configs.create("/controls/keybinds/" .. presetName .. ".otml")
+ Keybind.configs.hotkeys[presetName] = g_configs.create("/controls/hotkeys/" .. presetName .. ".otml")
+
+ Keybind.hotkeys[CHAT_MODE.ON][presetName] = {}
+ Keybind.hotkeys[CHAT_MODE.OFF][presetName] = {}
+
+ g_settings.setList("controls-presets", Keybind.presets)
+ g_settings.save()
+end
+
+function Keybind.copyPreset(fromPreset, toPreset)
+ if Keybind.presetToIndex[toPreset] then
+ return false
+ end
+
+ table.insert(Keybind.presets, toPreset)
+ Keybind.presetToIndex[toPreset] = #Keybind.presets
+
+ Keybind.configs.keybinds[fromPreset]:save()
+ Keybind.configs.hotkeys[fromPreset]:save()
+
+ local keybindsConfigPath = Keybind.configs.keybinds[fromPreset]:getFileName()
+ local keybindsConfigContent = g_resources.readFileContents(keybindsConfigPath)
+ g_resources.writeFileContents("/controls/keybinds/" .. toPreset .. ".otml", keybindsConfigContent)
+ Keybind.configs.keybinds[toPreset] = g_configs.create("/controls/keybinds/" .. toPreset .. ".otml")
+
+ local hotkeysConfigPath = Keybind.configs.hotkeys[fromPreset]:getFileName()
+ local hotkeysConfigContent = g_resources.readFileContents(hotkeysConfigPath)
+ g_resources.writeFileContents("/controls/hotkeys/" .. toPreset .. ".otml", hotkeysConfigContent)
+ Keybind.configs.hotkeys[toPreset] = g_configs.create("/controls/hotkeys/" .. toPreset .. ".otml")
+
+ for chatMode = CHAT_MODE.ON, CHAT_MODE.OFF do
+ Keybind.hotkeys[chatMode][toPreset] = {}
+
+ local hotkeyId = 1
+ local hotkeys = Keybind.configs.hotkeys[toPreset]:getNode(chatMode)
+
+ if hotkeys then
+ local hotkey = hotkeys[tostring(hotkeyId)]
+ while hotkey do
+ if hotkey.data.parameter then
+ hotkey.data.parameter = "\"" .. hotkey.data.parameter .. "\"" -- forcing quotes cause OTML is not saving them, just wow
+ end
+
+ table.insert(Keybind.hotkeys[chatMode][toPreset], hotkey)
+ hotkeyId = hotkeyId + 1
+
+ hotkey = hotkeys[tostring(hotkeyId)]
+ end
+ end
+ end
+
+ g_settings.setList("controls-presets", Keybind.presets)
+ g_settings.save()
+
+ return true
+end
+
+function Keybind.renamePreset(oldPresetName, newPresetName)
+ if Keybind.currentPreset == oldPresetName then
+ Keybind.currentPreset = newPresetName
+ end
+
+ local index = Keybind.presetToIndex[oldPresetName]
+ Keybind.presetToIndex[oldPresetName] = nil
+ Keybind.presetToIndex[newPresetName] = index
+ Keybind.presets[index] = newPresetName
+
+ local keybindsConfigPath = Keybind.configs.keybinds[oldPresetName]:getFileName()
+ Keybind.configs.keybinds[oldPresetName]:save()
+ Keybind.configs.keybinds[oldPresetName] = nil
+
+ local keybindsConfigContent = g_resources.readFileContents(keybindsConfigPath)
+ g_resources.deleteFile(keybindsConfigPath)
+ g_resources.writeFileContents("/controls/keybinds/" .. newPresetName .. ".otml", keybindsConfigContent)
+ Keybind.configs.keybinds[newPresetName] = g_configs.create("/controls/keybinds/" .. newPresetName .. ".otml")
+
+ local hotkeysConfigPath = Keybind.configs.hotkeys[oldPresetName]:getFileName()
+ Keybind.configs.hotkeys[oldPresetName]:save()
+ Keybind.configs.hotkeys[oldPresetName] = nil
+
+ local hotkeysConfigContent = g_resources.readFileContents(hotkeysConfigPath)
+ g_resources.deleteFile(hotkeysConfigPath)
+ g_resources.writeFileContents("/controls/hotkeys/" .. newPresetName .. ".otml", hotkeysConfigContent)
+ Keybind.configs.hotkeys[newPresetName] = g_configs.create("/controls/hotkeys/" .. newPresetName .. ".otml")
+
+ Keybind.hotkeys[CHAT_MODE.ON][newPresetName] = Keybind.hotkeys[CHAT_MODE.ON][oldPresetName]
+ Keybind.hotkeys[CHAT_MODE.OFF][newPresetName] = Keybind.hotkeys[CHAT_MODE.OFF][oldPresetName]
+
+ g_settings.setList("controls-presets", Keybind.presets)
+ g_settings.save()
+end
+
+function Keybind.removePreset(presetName)
+ if #Keybind.presets == 1 then
+ return false
+ end
+
+ table.remove(Keybind.presets, Keybind.presetToIndex[presetName])
+ Keybind.presetToIndex[presetName] = nil
+
+ Keybind.configs.keybinds[presetName] = nil
+ g_configs.unload("/controls/keybinds/" .. presetName .. ".otml")
+ g_resources.deleteFile("/controls/keybinds/" .. presetName .. ".otml")
+
+ Keybind.configs.hotkeys[presetName] = nil
+ g_configs.unload("/controls/hotkeys/" .. presetName .. ".otml")
+ g_resources.deleteFile("/controls/hotkeys/" .. presetName .. ".otml")
+
+ if Keybind.currentPreset == presetName then
+ Keybind.currentPreset = Keybind.presets[1]
+ end
+
+ g_settings.setList("controls-presets", Keybind.presets)
+ g_settings.save()
+
+ return true
+end
+
+function Keybind.selectPreset(presetName)
+ if Keybind.currentPreset == presetName then
+ return false
+ end
+
+ if not Keybind.presetToIndex[presetName] then
+ return false
+ end
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ if keybind.callbacks then
+ Keybind.unbind(keybind.category, keybind.action)
+ end
+ end
+
+ for _, hotkey in ipairs(Keybind.hotkeys[Keybind.chatMode][Keybind.currentPreset]) do
+ Keybind.unbindHotkey(hotkey.hotkeyId, Keybind.chatMode)
+ end
+
+ Keybind.currentPreset = presetName
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ if keybind.callbacks then
+ Keybind.bind(keybind.category, keybind.action, keybind.callbacks, keybind.widget)
+ end
+ end
+
+ for _, hotkey in ipairs(Keybind.hotkeys[Keybind.chatMode][Keybind.currentPreset]) do
+ Keybind.bindHotkey(hotkey.hotkeyId, Keybind.chatMode)
+ end
+
+ return true
+end
+
+function Keybind.getAction(category, action)
+ local index = category .. '_' .. action
+ return Keybind.defaultKeybinds[index]
+end
+
+function Keybind.setPrimaryActionKey(category, action, preset, keyCombo, chatMode)
+ local index = category .. '_' .. action
+ local keybind = Keybind.defaultKeybinds[index]
+
+ local keys = Keybind.configs.keybinds[preset]:getNode(index)
+ if not keys then
+ keys = table.recursivecopy(keybind.keys)
+ else
+ chatMode = tostring(chatMode)
+ end
+
+ if keybind.callbacks then
+ Keybind.unbind(category, action)
+ end
+
+ if not keys[chatMode] then
+ keys[chatMode] = { primary = keyCombo, secondary = keybind.keys[tonumber(chatMode)].secondary }
+ end
+
+ keys[chatMode].primary = keyCombo
+
+ local ret = false
+ if keys[chatMode].secondary == keyCombo then
+ keys[chatMode].secondary = nil
+ ret = true
+ end
+
+ Keybind.configs.keybinds[preset]:setNode(index, keys)
+
+ if keybind.callbacks then
+ Keybind.bind(category, action, keybind.callbacks, keybind.widget)
+ end
+
+ return ret
+end
+
+function Keybind.setSecondaryActionKey(category, action, preset, keyCombo, chatMode)
+ local index = category .. '_' .. action
+ local keybind = Keybind.defaultKeybinds[index]
+
+ local keys = Keybind.configs.keybinds[preset]:getNode(index)
+ if not keys then
+ keys = table.recursivecopy(keybind.keys)
+ else
+ chatMode = tostring(chatMode)
+ end
+
+ if keybind.callbacks then
+ Keybind.unbind(category, action)
+ end
+
+ if not keys[chatMode] then
+ keys[chatMode] = { primary = keybind.keys[tonumber(chatMode)].primary, secondary = keyCombo }
+ end
+
+ keys[chatMode].secondary = keyCombo
+
+ local ret = false
+ if keys[chatMode].primary == keyCombo then
+ keys[chatMode].primary = nil
+ ret = true
+ end
+
+ Keybind.configs.keybinds[preset]:setNode(index, keys)
+
+ if keybind.callbacks then
+ Keybind.bind(category, action, keybind.callbacks, keybind.widget)
+ end
+
+ return ret
+end
+
+function Keybind.resetKeybindsToDefault(presetName, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ if keybind.callbacks then
+ Keybind.unbind(keybind.category, keybind.action)
+ end
+ end
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ local index = keybind.category .. '_' .. keybind.action
+ Keybind.configs.keybinds[presetName]:setNode(index, keybind.keys)
+ end
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ if keybind.callbacks then
+ Keybind.bind(keybind.category, keybind.action, keybind.callbacks, keybind.widget)
+ end
+ end
+end
+
+function Keybind.getKeybindKeys(category, action, chatMode, preset, forceDefault)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ local index = category .. '_' .. action
+ local keybind = Keybind.defaultKeybinds[index]
+ local keys = Keybind.configs.keybinds[preset or Keybind.currentPreset]:getNode(index)
+
+ if not keys or forceDefault then
+ keys = {
+ primary = keybind.keys[chatMode].primary,
+ secondary = keybind.keys[chatMode].secondary
+ }
+ else
+ keys = keys[chatMode] or keys[tostring(chatMode)]
+ end
+
+ if not keys then
+ keys = {
+ primary = "",
+ secondary = ""
+ }
+ end
+
+ return keys
+end
+
+function Keybind.isKeyComboUsed(keyCombo, category, action, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ if Keybind.reservedKeys[keyCombo] then
+ return true
+ end
+
+ if category and action then
+ local targetKeys = Keybind.getKeybindKeys(category, action, chatMode, Keybind.currentPreset)
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ local keys = Keybind.getKeybindKeys(keybind.category, keybind.action, chatMode, Keybind.currentPreset)
+ if (keys.primary == keyCombo and targetKeys.primary ~= keyCombo) or (keys.secondary == keyCombo and targetKeys.secondary ~= keyCombo) then
+ return true
+ end
+ end
+ else
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ local keys = Keybind.getKeybindKeys(keybind.category, keybind.action, chatMode, Keybind.currentPreset)
+ if keys.primary == keyCombo or keys.secondary == keyCombo then
+ return true
+ end
+ end
+
+ if Keybind.hotkeys[chatMode][Keybind.currentPreset] then
+ for _, hotkey in ipairs(Keybind.hotkeys[chatMode][Keybind.currentPreset]) do
+ if hotkey.primary == keyCombo or hotkey.secondary == keyCombo then
+ return true
+ end
+ end
+ end
+ end
+
+ return false
+end
+
+function Keybind.newHotkey(action, data, primary, secondary, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ local hotkey = {
+ action = action,
+ data = data,
+ primary = primary or "",
+ secondary = secondary or ""
+ }
+
+ if not Keybind.hotkeys[chatMode][Keybind.currentPreset] then
+ Keybind.hotkeys[chatMode][Keybind.currentPreset] = {}
+ end
+
+ table.insert(Keybind.hotkeys[chatMode][Keybind.currentPreset], hotkey)
+
+ local hotkeyId = #Keybind.hotkeys[chatMode][Keybind.currentPreset]
+ hotkey.hotkeyId = hotkeyId
+ Keybind.configs.hotkeys[Keybind.currentPreset]:setNode(chatMode, Keybind.hotkeys[chatMode][Keybind.currentPreset])
+ Keybind.configs.hotkeys[Keybind.currentPreset]:save()
+
+ Keybind.bindHotkey(hotkeyId, chatMode)
+end
+
+function Keybind.removeHotkey(hotkeyId, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ if not Keybind.hotkeys[chatMode][Keybind.currentPreset] then
+ return
+ end
+
+ Keybind.unbindHotkey(hotkeyId, chatMode)
+
+ table.remove(Keybind.hotkeys[chatMode][Keybind.currentPreset], hotkeyId)
+
+ Keybind.configs.hotkeys[Keybind.currentPreset]:clear()
+
+ for id, hotkey in ipairs(Keybind.hotkeys[chatMode][Keybind.currentPreset]) do
+ hotkey.hotkeyId = id
+ Keybind.configs.hotkeys[Keybind.currentPreset]:setNode(id, hotkey)
+ end
+
+ Keybind.configs.hotkeys[Keybind.currentPreset]:save()
+end
+
+function Keybind.editHotkey(hotkeyId, action, data, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ Keybind.unbindHotkey(hotkeyId, chatMode)
+
+ local hotkey = Keybind.hotkeys[chatMode][Keybind.currentPreset][hotkeyId]
+ hotkey.action = action
+ hotkey.data = data
+ Keybind.configs.hotkeys[Keybind.currentPreset]:setNode(chatMode, Keybind.hotkeys[chatMode][Keybind.currentPreset])
+ Keybind.configs.hotkeys[Keybind.currentPreset]:save()
+
+ Keybind.bindHotkey(hotkeyId, chatMode)
+end
+
+function Keybind.editHotkeyKeys(hotkeyId, primary, secondary, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ Keybind.unbindHotkey(hotkeyId, chatMode)
+
+ local hotkey = Keybind.hotkeys[chatMode][Keybind.currentPreset][hotkeyId]
+ hotkey.primary = primary or ""
+ hotkey.secondary = secondary or ""
+ Keybind.configs.hotkeys[Keybind.currentPreset]:setNode(chatMode, Keybind.hotkeys[chatMode][Keybind.currentPreset])
+ Keybind.configs.hotkeys[Keybind.currentPreset]:save()
+
+ Keybind.bindHotkey(hotkeyId, chatMode)
+end
+
+function Keybind.removeAllHotkeys(chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ for _, hotkey in ipairs(Keybind.hotkeys[chatMode][Keybind.currentPreset]) do
+ Keybind.unbindHotkey(hotkey.hotkeyId)
+ end
+
+ Keybind.hotkeys[chatMode][Keybind.currentPreset] = {}
+
+ Keybind.configs.hotkeys[Keybind.currentPreset]:remove(chatMode)
+ Keybind.configs.hotkeys[Keybind.currentPreset]:save()
+end
+
+function Keybind.getHotkeyKeys(hotkeyId, preset, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+ if not preset then
+ preset = Keybind.currentPreset
+ end
+
+ local keys = { primary = "", secondary = "" }
+ if not Keybind.hotkeys[chatMode][preset] then
+ return keys
+ end
+
+ local hotkey = Keybind.hotkeys[chatMode][preset][hotkeyId]
+ if not hotkey then
+ return keys
+ end
+
+ local config = Keybind.configs.hotkeys[preset]:getNode(chatMode)
+ if not config then
+ return keys
+ end
+
+ return config[tostring(hotkeyId)] or keys
+end
+
+function Keybind.hotkeyCallback(hotkeyId, chatMode)
+ if not chatMode then
+ chatMode = Keybind.chatMode
+ end
+
+ local hotkey = Keybind.hotkeys[chatMode][Keybind.currentPreset][hotkeyId]
+
+ if not hotkey then
+ return
+ end
+
+ local action = hotkey.action
+ local data = hotkey.data
+
+ if action == HOTKEY_ACTION.USE_YOURSELF then
+ if g_game.getClientVersion() < 780 then
+ local item = g_game.findPlayerItem(data.itemId, data.subType or -1)
+
+ if item then
+ g_game.useWith(item, g_game.getLocalPlayer())
+ end
+ else
+ g_game.useInventoryItemWith(data.itemId, g_game.getLocalPlayer(), data.subType or -1)
+ end
+ elseif action == HOTKEY_ACTION.USE_CROSSHAIR then
+ local item = Item.create(data.itemId)
+
+ if g_game.getClientVersion() < 780 then
+ item = g_game.findPlayerItem(data.itemId, data.subType or -1)
+ end
+
+ if item then
+ modules.game_interface.startUseWith(item, data.subType or -1)
+ end
+ elseif action == HOTKEY_ACTION.USE_TARGET then
+ local attackingCreature = g_game.getAttackingCreature()
+ if not attackingCreature then
+ local item = Item.create(data.itemId)
+
+ if g_game.getClientVersion() < 780 then
+ item = g_game.findPlayerItem(data.itemId, data.subType or -1)
+ end
+
+ if item then
+ modules.game_interface.startUseWith(item, data.subType or -1)
+ end
+
+ return
+ end
+
+ if attackingCreature:getTile() then
+ if g_game.getClientVersion() < 780 then
+ local item = g_game.findPlayerItem(data.itemId, data.subType or -1)
+ if item then
+ g_game.useWith(item, attackingCreature, data.subType or -1)
+ end
+ else
+ g_game.useInventoryItemWith(data.itemId, attackingCreature, data.subType or -1)
+ end
+ end
+ elseif action == HOTKEY_ACTION.EQUIP then
+ if g_game.getClientVersion() >= 910 then
+ local item = Item.create(data.itemId)
+
+ g_game.equipItem(item)
+ end
+ elseif action == HOTKEY_ACTION.USE then
+ if g_game.getClientVersion() < 780 then
+ local item = g_game.findPlayerItem(data.itemId, data.subType or -1)
+
+ if item then
+ g_game.use(item)
+ end
+ else
+ g_game.useInventoryItem(data.itemId)
+ end
+ elseif action == HOTKEY_ACTION.TEXT then
+ if modules.game_interface.isChatVisible() then
+ modules.game_console.setTextEditText(hotkey.data.text)
+ end
+ elseif action == HOTKEY_ACTION.TEXT_AUTO then
+ if modules.game_interface.isChatVisible() then
+ modules.game_console.sendMessage(hotkey.data.text)
+ else
+ g_game.talk(hotkey.data.text)
+ end
+ elseif action == HOTKEY_ACTION.SPELL then
+ local text = data.words
+ if data.parameter then
+ text = text .. " " .. data.parameter
+ end
+
+ if modules.game_interface.isChatVisible() then
+ modules.game_console.sendMessage(text)
+ else
+ g_game.talk(text)
+ end
+ end
+end
+
+function Keybind.bindHotkey(hotkeyId, chatMode)
+ if not chatMode or chatMode ~= Keybind.chatMode then
+ return
+ end
+
+ if not modules.game_interface then
+ return
+ end
+
+ local hotkey = Keybind.hotkeys[chatMode][Keybind.currentPreset][hotkeyId]
+
+ if not hotkey then
+ return
+ end
+
+ local keys = Keybind.getHotkeyKeys(hotkeyId, Keybind.currentPreset, chatMode)
+ local gameRootPanel = modules.game_interface.getRootPanel()
+ local action = hotkey.action
+
+ hotkey.callback = function() Keybind.hotkeyCallback(hotkeyId, chatMode) end
+
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ if action == HOTKEY_ACTION.EQUIP or action == HOTKEY_ACTION.USE or action == HOTKEY_ACTION.TEXT or action == HOTKEY_ACTION.TEXT_AUTO then
+ g_keyboard.bindKeyDown(keys.primary, hotkey.callback, gameRootPanel)
+ else
+ g_keyboard.bindKeyPress(keys.primary, hotkey.callback, gameRootPanel)
+ end
+ end
+ end
+
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ if action == HOTKEY_ACTION.EQUIP or action == HOTKEY_ACTION.USE or action == HOTKEY_ACTION.TEXT or action == HOTKEY_ACTION.TEXT_AUTO then
+ g_keyboard.bindKeyDown(keys.secondary, hotkey.callback, gameRootPanel)
+ else
+ g_keyboard.bindKeyPress(keys.secondary, hotkey.callback, gameRootPanel)
+ end
+ end
+ end
+end
+
+function Keybind.unbindHotkey(hotkeyId, chatMode)
+ if not chatMode or chatMode ~= Keybind.chatMode then
+ return
+ end
+
+ if not modules.game_interface then
+ return
+ end
+
+ local hotkey = Keybind.hotkeys[chatMode][Keybind.currentPreset][hotkeyId]
+
+ if not hotkey then
+ return
+ end
+
+ local keys = Keybind.getHotkeyKeys(hotkeyId, Keybind.currentPreset, chatMode)
+ local gameRootPanel = modules.game_interface.getRootPanel()
+ local action = hotkey.action
+
+ if keys.primary then
+ keys.primary = tostring(keys.primary)
+ if keys.primary:len() > 0 then
+ if action == HOTKEY_ACTION.EQUIP or action == HOTKEY_ACTION.USE or action == HOTKEY_ACTION.TEXT or action == HOTKEY_ACTION.TEXT_AUTO then
+ g_keyboard.unbindKeyDown(keys.primary, hotkey.callback, gameRootPanel)
+ else
+ g_keyboard.unbindKeyPress(keys.primary, hotkey.callback, gameRootPanel)
+ end
+ end
+ end
+
+ if keys.secondary then
+ keys.secondary = tostring(keys.secondary)
+ if keys.secondary:len() > 0 then
+ if action == HOTKEY_ACTION.EQUIP or action == HOTKEY_ACTION.USE or action == HOTKEY_ACTION.TEXT or action == HOTKEY_ACTION.TEXT_AUTO then
+ g_keyboard.unbindKeyDown(keys.secondary, hotkey.callback, gameRootPanel)
+ else
+ g_keyboard.unbindKeyPress(keys.secondary, hotkey.callback, gameRootPanel)
+ end
+ end
+ end
+end
+
+function Keybind.setChatMode(chatMode)
+ if Keybind.chatMode == chatMode then
+ return
+ end
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ if keybind.callbacks then
+ Keybind.unbind(keybind.category, keybind.action)
+ end
+ end
+
+ for _, hotkey in ipairs(Keybind.hotkeys[Keybind.chatMode][Keybind.currentPreset]) do
+ Keybind.unbindHotkey(hotkey.hotkeyId, Keybind.chatMode)
+ end
+
+ if modules.game_walking then
+ modules.game_walking.unbindTurnKeys()
+ end
+
+ Keybind.chatMode = chatMode
+
+ for _, keybind in pairs(Keybind.defaultKeybinds) do
+ if keybind.callbacks then
+ Keybind.bind(keybind.category, keybind.action, keybind.callbacks, keybind.widget)
+ end
+ end
+
+ for _, hotkey in ipairs(Keybind.hotkeys[chatMode][Keybind.currentPreset]) do
+ Keybind.bindHotkey(hotkey.hotkeyId, chatMode)
+ end
+
+ if modules.game_walking then
+ modules.game_walking.bindTurnKeys()
+ end
+end
diff --git a/modules/corelib/ui/uicombobox.lua b/modules/corelib/ui/uicombobox.lua
index feb856727c..d41dc122c7 100644
--- a/modules/corelib/ui/uicombobox.lua
+++ b/modules/corelib/ui/uicombobox.lua
@@ -199,3 +199,12 @@ function UIComboBox:HTML_onReadNodes(nodes)
return false
end
+
+function UIComboBox:getCurrentIndex()
+ return self.currentIndex
+end
+
+function UIComboBox:updateCurrentOption(newText)
+ self.options[self.currentIndex].text = newText
+ self:setText(newText)
+end
diff --git a/modules/corelib/ui/uipopupmenu.lua b/modules/corelib/ui/uipopupmenu.lua
index dd6e44f896..d02ae72bea 100644
--- a/modules/corelib/ui/uipopupmenu.lua
+++ b/modules/corelib/ui/uipopupmenu.lua
@@ -62,11 +62,11 @@ function UIPopupMenu:onGeometryChange(newRect, oldRect)
self:bindRectToParent()
end
-function UIPopupMenu:addOption(optionName, optionCallback, shortcut)
+function UIPopupMenu:addOption(optionName, optionCallback, shortcut, disabled)
local optionWidget = g_ui.createWidget(self:getStyleName() .. 'Button', self)
optionWidget.onClick = function(widget)
self:destroy()
- optionCallback()
+ optionCallback(self:getPosition())
end
optionWidget:setText(optionName)
local width = optionWidget:getTextSize().width + optionWidget:getMarginLeft() + optionWidget:getMarginRight() + 15
@@ -77,7 +77,7 @@ function UIPopupMenu:addOption(optionName, optionCallback, shortcut)
width = width + shortcutLabel:getTextSize().width + shortcutLabel:getMarginLeft() +
shortcutLabel:getMarginRight()
end
-
+ optionWidget:setEnabled(not disabled)
self:setWidth(math.max(190, math.max(self:getWidth(), width)))
end
diff --git a/modules/corelib/ui/uitabbar.lua b/modules/corelib/ui/uitabbar.lua
index 5a9722321b..5daf117a9c 100644
--- a/modules/corelib/ui/uitabbar.lua
+++ b/modules/corelib/ui/uitabbar.lua
@@ -48,7 +48,9 @@ function UITabBar:addTab(text, panel, icon)
tab.onClick = onTabClick
tab.onMouseRelease = onTabMouseRelease
tab.onDestroy = function()
- tab.tabPanel:destroy()
+ if not tab.tabPanel:isDestroyed() then
+ tab.tabPanel:destroy()
+ end
end
table.insert(self.tabs, tab)
diff --git a/modules/corelib/ui/uitable.lua b/modules/corelib/ui/uitable.lua
index a19e101f59..e1bae68fa5 100644
--- a/modules/corelib/ui/uitable.lua
+++ b/modules/corelib/ui/uitable.lua
@@ -223,7 +223,10 @@ function UITable:addRow(data, height)
self.columns[rowId] = {}
for colId, column in pairs(data) do
- local col = g_ui.createWidget(self.columBaseStyle, row)
+ local col = g_ui.createWidget(column.style or self.columBaseStyle, row)
+ if column.id then
+ col:setId(column.id)
+ end
if column.width then
col:setWidth(column.width)
else
@@ -235,11 +238,29 @@ function UITable:addRow(data, height)
if column.text then
col:setText(column.text)
end
+ if column.color then
+ col:setColor(column.color)
+ end
+ if column.coloredText then
+ col:parseColoredText(column.coloredText.text, column.coloredText.color)
+ end
if column.sortvalue then
col.sortvalue = column.sortvalue
else
col.sortvalue = column.text or 0
end
+ if column.marginTop then
+ col:setMarginTop(column.marginTop)
+ end
+ if column.marginBottom then
+ col:setMarginBottom(column.marginBottom)
+ end
+ if column.comboBox then
+ for _, comboValue in ipairs(column.comboBox) do
+ col:addOption(comboValue[1], comboValue[2])
+ end
+ end
+
table.insert(self.columns[rowId], col)
end
diff --git a/modules/corelib/ui/uiwidget.lua b/modules/corelib/ui/uiwidget.lua
index 85a26dbf53..9e6da7fbe2 100644
--- a/modules/corelib/ui/uiwidget.lua
+++ b/modules/corelib/ui/uiwidget.lua
@@ -18,3 +18,28 @@ function UIWidget:setMargin(...)
self:setMarginLeft(params[4])
end
end
+
+function UIWidget:parseColoredText(text, default_color)
+ local result = ""
+ local i = 1
+ while i <= #text do
+ local start, stop = text:find("%[color=.-%]", i)
+ if start then
+ result = result .. text:sub(i, start - 1)
+ local closing_tag_start, closing_tag_stop = text:find("%[/color%]", stop + 1)
+ if closing_tag_start then
+ local content = text:sub(stop + 1, closing_tag_start - 1)
+ local color_start, color_stop = text:find("#%x+", start)
+ local color = text:sub(color_start, color_stop) or default_color
+ result = result .. "{" .. content .. ", " .. color .. "}"
+ i = closing_tag_stop + 1
+ else
+ break
+ end
+ else
+ result = result .. text:sub(i)
+ break
+ end
+ end
+ self:setColoredText(result)
+end
diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua
index dda3268f5a..7f9e438c28 100644
--- a/modules/game_battle/battle.lua
+++ b/modules/game_battle/battle.lua
@@ -69,7 +69,13 @@ function init() -- Initiating the module (load)
battleWindow = g_ui.loadUI('battle')
-- Binding Ctrl + B shortcut
- g_keyboard.bindKeyDown('Ctrl+B', toggle)
+ Keybind.new("Windows", "Show/hide battle list", "Ctrl+B", "")
+ Keybind.bind("Windows", "Show/hide battle list", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
-- Disabling scrollbar auto hiding
local scrollbar = battleWindow:getChildById('miniwindowScrollBar')
@@ -1087,7 +1093,7 @@ function terminate() -- Terminating the Module (unload)
filterPanel = nil
toggleFilterButton = nil
- g_keyboard.unbindKeyDown('Ctrl+B')
+ Keybind.delete("Windows", "Show/hide battle list")
disconnect(g_game, {
onAttackingCreatureChange = onAttack,
diff --git a/modules/game_bugreport/bugreport.lua b/modules/game_bugreport/bugreport.lua
index 56b622f296..0cbc0a838d 100644
--- a/modules/game_bugreport/bugreport.lua
+++ b/modules/game_bugreport/bugreport.lua
@@ -12,11 +12,17 @@ function init()
bugTextEdit = bugReportWindow:getChildById('bugTextEdit')
- g_keyboard.bindKeyDown(HOTKEY, show)
+ Keybind.new("Dialogs", "Open Bugreport", HOTKEY, "")
+ Keybind.bind("Dialogs", "Open Bugreport", {
+ {
+ type = KEY_DOWN,
+ callback = show,
+ }
+ }, modules.game_interface.getRootPanel())
end
function terminate()
- g_keyboard.unbindKeyDown(HOTKEY)
+ Keybind.delete("Dialogs", "Open Bugreport")
bugReportWindow:destroy()
end
diff --git a/modules/game_console/console.lua b/modules/game_console/console.lua
index 3d92211f0e..3b07f6e6a6 100644
--- a/modules/game_console/console.lua
+++ b/modules/game_console/console.lua
@@ -222,14 +222,8 @@ function init()
g_keyboard.bindKeyPress('Shift+Down', function()
navigateMessageHistory(-1)
end, consolePanel)
- g_keyboard.bindKeyPress('Tab', function()
- consoleTabBar:selectNextTab()
- end, consolePanel)
- g_keyboard.bindKeyPress('Shift+Tab', function()
- consoleTabBar:selectPrevTab()
- end, consolePanel)
+
g_keyboard.bindKeyDown('Enter', switchChatOnCall, consolePanel)
- g_keyboard.bindKeyDown('Enter', sendCurrentMessage, consolePanel)
g_keyboard.bindKeyDown('Escape', disableChatOnCall, consolePanel)
g_keyboard.bindKeyPress('Ctrl+A', function()
consoleTextEdit:clearText()
@@ -241,9 +235,52 @@ function init()
consoleTabBar.onTabChange = onTabChange
-- tibia like hotkeys
- g_keyboard.bindKeyDown('Ctrl+O', g_game.requestChannels)
- g_keyboard.bindKeyDown('Ctrl+E', removeCurrentTab)
- g_keyboard.bindKeyDown('Ctrl+H', openHelp)
+ local gameRootPanel = modules.game_interface.getRootPanel()
+ Keybind.new("Chat Channel", "Next Channel", "Tab", "")
+ Keybind.bind("Chat Channel", "Next Channel", {
+ {
+ type = KEY_PRESS,
+ callback = function() consoleTabBar:selectNextTab() end,
+ }
+ }, consolePanel)
+
+ Keybind.new("Chat Channel", "Previous Channel", "Shift+Tab", "")
+ Keybind.bind("Chat Channel", "Previous Channel", {
+ {
+ type = KEY_PRESS,
+ callback = function() consoleTabBar:selectPrevTab() end,
+ }
+ }, consolePanel)
+ Keybind.new("Chat", "Send current chat line", { [CHAT_MODE.ON] = "Enter", [CHAT_MODE.OFF] = "" }, "")
+ Keybind.bind("Chat", "Send current chat line", {
+ {
+ type = KEY_DOWN,
+ callback = sendCurrentMessage,
+ }
+ }, consolePanel)
+ Keybind.new("Chat Channel", "Open Channel List", "Ctrl+O", "")
+ Keybind.bind("Chat Channel", "Open Channel List", {
+ {
+ type = KEY_DOWN,
+ callback = g_game.requestChannels,
+ }
+ }, gameRootPanel)
+ Keybind.new("Chat Channel", "Close Current Channel", "Ctrl+E", "")
+
+ Keybind.bind("Chat Channel", "Close Current Channel", {
+ {
+ type = KEY_DOWN,
+ callback = removeCurrentTab,
+ }
+ }, gameRootPanel)
+
+ Keybind.new("Chat Channel", "Open Help Channel", "Ctrl+H", "")
+ Keybind.bind("Chat Channel", "Open Help Channel", {
+ {
+ type = KEY_DOWN,
+ callback = openHelp,
+ }
+ }, consolePanel)
-- toggle WASD
consoleToggleChat = consolePanel:getChildById('toggleChat')
@@ -340,9 +377,11 @@ function switchChat(enabled)
if enabled then
unbindMovingKeys()
consoleToggleChat:setTooltip(tr('Disable chat mode, allow to walk using WASD'))
+ Keybind.setChatMode(CHAT_MODE.ON)
else
bindMovingKeys()
consoleToggleChat:setTooltip(tr('Enable chat mode'))
+ Keybind.setChatMode(CHAT_MODE.OFF)
end
end
@@ -401,10 +440,12 @@ function terminate()
clear()
end
- g_keyboard.unbindKeyDown('Ctrl+O')
- g_keyboard.unbindKeyDown('Ctrl+E')
- g_keyboard.unbindKeyDown('Ctrl+H')
-
+ Keybind.delete("Chat Channel", "Close Current Channel")--
+ Keybind.delete("Chat Channel", "Next Channel")--
+ Keybind.delete("Chat Channel", "Previous Channel")--
+ Keybind.delete("Chat Channel", "Open Channel List")--
+ Keybind.delete("Chat Channel", "Open Help Channel")--
+ Keybind.delete("Chat", "Send current chat line")
saveCommunicationSettings()
if channelsWindow then
@@ -1994,7 +2035,14 @@ function online()
tab.npcChat = true
end
if g_game.getClientVersion() < 862 then
- g_keyboard.bindKeyDown('Ctrl+R', openPlayerReportRuleViolationWindow)
+ Keybind.new("Dialogs", "Open Rule Violation", "Ctrl+R", "")
+ local gameRootPanel = modules.game_interface.getRootPanel()
+ Keybind.bind("Dialogs", "Open Rule Violation", {
+ {
+ type = KEY_DOWN,
+ callback = openPlayerReportRuleViolationWindow,
+ }
+ }, gameRootPanel)
end
-- open last channels
local lastChannelsOpen = g_settings.getNode('lastChannelsOpen')
@@ -2019,7 +2067,7 @@ end
function offline()
if g_game.getClientVersion() < 862 then
- g_keyboard.unbindKeyDown('Ctrl+R')
+ Keybind.delete("Dialogs", "Open Rule Violation")
end
clear()
end
diff --git a/modules/game_containers/containers.lua b/modules/game_containers/containers.lua
index d12357110d..a8f787e1ac 100644
--- a/modules/game_containers/containers.lua
+++ b/modules/game_containers/containers.lua
@@ -53,6 +53,10 @@ function refreshContainerItems(container)
itemWidget:setItem(container:getItem(slot))
ItemsDatabase.setRarityItem(itemWidget, container:getItem(slot))
ItemsDatabase.setTier(itemWidget, container:getItem(slot))
+ if modules.client_options.getOption('showExpiryInContainers') then
+ ItemsDatabase.setCharges(itemWidget, container:getItem(slot))
+ ItemsDatabase.setDuration(itemWidget, container:getItem(slot))
+ end
end
if container:hasPages() then
@@ -140,6 +144,10 @@ function onContainerOpen(container, previousContainer)
itemWidget:setItem(container:getItem(slot))
ItemsDatabase.setRarityItem(itemWidget, container:getItem(slot))
ItemsDatabase.setTier(itemWidget, container:getItem(slot))
+ if modules.client_options.getOption('showExpiryInContainers') then
+ ItemsDatabase.setCharges(itemWidget, container:getItem(slot))
+ ItemsDatabase.setDuration(itemWidget, container:getItem(slot))
+ end
itemWidget:setMargin(0)
itemWidget.position = container:getSlotPosition(slot)
@@ -191,4 +199,8 @@ function onContainerUpdateItem(container, slot, item, oldItem)
end
local itemWidget = container.itemsPanel:getChildById('item' .. slot)
itemWidget:setItem(item)
+ if modules.client_options.getOption('showExpiryInContainers') then
+ ItemsDatabase.setCharges(itemWidget, container:getItem(slot))
+ ItemsDatabase.setDuration(itemWidget, container:getItem(slot))
+ end
end
diff --git a/modules/game_cooldown/cooldown.otui b/modules/game_cooldown/cooldown.otui
index 51fdaf7baa..cc93530041 100644
--- a/modules/game_cooldown/cooldown.otui
+++ b/modules/game_cooldown/cooldown.otui
@@ -29,6 +29,8 @@ Panel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
+ focusable: false
+ phantom: true
Panel
id:contentsPanel2
anchors.fill: parent
diff --git a/modules/game_cyclopedia/game_cyclopedia.lua b/modules/game_cyclopedia/game_cyclopedia.lua
index ad1c906491..535085edb0 100644
--- a/modules/game_cyclopedia/game_cyclopedia.lua
+++ b/modules/game_cyclopedia/game_cyclopedia.lua
@@ -33,7 +33,6 @@ controllerCyclopedia = Controller:new()
controllerCyclopedia:setUI('game_cyclopedia')
function controllerCyclopedia:onInit()
-
end
function controllerCyclopedia:onGameStart()
@@ -168,6 +167,19 @@ function controllerCyclopedia:onGameStart()
trackerMiniWindow:setupOnStart()
loadFilters()
Cyclopedia.BossSlots.UnlockBosses = {}
+ Keybind.new("Windows", "Show/hide Bosstiary Tracker", "", "")
+
+ Keybind.bind("Windows", "Show/hide Bosstiary Tracker", {{
+ type = KEY_DOWN,
+ callback = Cyclopedia.toggleBosstiaryTracker
+ }})
+
+ Keybind.new("Windows", "Show/hide Bestiary Tracker", "", "")
+ Keybind.bind("Windows", "Show/hide Bestiary Tracker", {{
+ type = KEY_DOWN,
+ callback = Cyclopedia.toggleBestiaryTracker
+ }})
+
end
end
@@ -179,6 +191,8 @@ function controllerCyclopedia:onGameEnd()
end
hide()
saveFilters()
+ Keybind.delete("Windows", "Show/hide Bosstiary Tracker")
+ Keybind.delete("Windows", "Show/hide Bestiary Tracker")
end
function controllerCyclopedia:onTerminate()
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Construct.png b/modules/game_cyclopedia/images/bestiary/creatures/construct.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Construct.png
rename to modules/game_cyclopedia/images/bestiary/creatures/construct.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Demon.png b/modules/game_cyclopedia/images/bestiary/creatures/demon.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Demon.png
rename to modules/game_cyclopedia/images/bestiary/creatures/demon.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Dragon.png b/modules/game_cyclopedia/images/bestiary/creatures/dragon.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Dragon.png
rename to modules/game_cyclopedia/images/bestiary/creatures/dragon.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Extra_Dimensional.png b/modules/game_cyclopedia/images/bestiary/creatures/extra_dimensional.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Extra_Dimensional.png
rename to modules/game_cyclopedia/images/bestiary/creatures/extra_dimensional.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Fey.png b/modules/game_cyclopedia/images/bestiary/creatures/fey.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Fey.png
rename to modules/game_cyclopedia/images/bestiary/creatures/fey.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Giant.png b/modules/game_cyclopedia/images/bestiary/creatures/giant.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Giant.png
rename to modules/game_cyclopedia/images/bestiary/creatures/giant.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Human.png b/modules/game_cyclopedia/images/bestiary/creatures/human.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Human.png
rename to modules/game_cyclopedia/images/bestiary/creatures/human.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Humanoid.png b/modules/game_cyclopedia/images/bestiary/creatures/humanoid.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Humanoid.png
rename to modules/game_cyclopedia/images/bestiary/creatures/humanoid.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Lycanthrope.png b/modules/game_cyclopedia/images/bestiary/creatures/lycanthrope.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Lycanthrope.png
rename to modules/game_cyclopedia/images/bestiary/creatures/lycanthrope.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Magical.png b/modules/game_cyclopedia/images/bestiary/creatures/magical.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Magical.png
rename to modules/game_cyclopedia/images/bestiary/creatures/magical.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Mammal.png b/modules/game_cyclopedia/images/bestiary/creatures/mammal.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Mammal.png
rename to modules/game_cyclopedia/images/bestiary/creatures/mammal.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Plant.png b/modules/game_cyclopedia/images/bestiary/creatures/plant.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Plant.png
rename to modules/game_cyclopedia/images/bestiary/creatures/plant.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Reptile.png b/modules/game_cyclopedia/images/bestiary/creatures/reptile.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Reptile.png
rename to modules/game_cyclopedia/images/bestiary/creatures/reptile.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Slime.png b/modules/game_cyclopedia/images/bestiary/creatures/slime.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Slime.png
rename to modules/game_cyclopedia/images/bestiary/creatures/slime.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Undead.png b/modules/game_cyclopedia/images/bestiary/creatures/undead.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Undead.png
rename to modules/game_cyclopedia/images/bestiary/creatures/undead.png
diff --git a/modules/game_cyclopedia/images/bestiary/creatures/Vermin.png b/modules/game_cyclopedia/images/bestiary/creatures/vermin.png
similarity index 100%
rename from modules/game_cyclopedia/images/bestiary/creatures/Vermin.png
rename to modules/game_cyclopedia/images/bestiary/creatures/vermin.png
diff --git a/modules/game_cyclopedia/tab/bestiary/bestiary.lua b/modules/game_cyclopedia/tab/bestiary/bestiary.lua
index 5b34d7ddea..5baa514515 100644
--- a/modules/game_cyclopedia/tab/bestiary/bestiary.lua
+++ b/modules/game_cyclopedia/tab/bestiary/bestiary.lua
@@ -125,10 +125,7 @@ function Cyclopedia.CreateCreatureItems(data)
end
end
- local price, rarity = ItemsDatabase.getSellValueAndColor(itemWidget.id)
- if price > 0 then
- itemWidget:setImageSource("/images/ui/rarity_" .. rarity)
- end
+ ItemsDatabase.setRarityItem(itemWidget, itemWidget:getItem())
itemWidget.onMouseRelease = onAddLootClick
end
diff --git a/modules/game_cyclopedia/tab/boss_slots/boss_slots.lua b/modules/game_cyclopedia/tab/boss_slots/boss_slots.lua
index 10bd8d68c8..27e28e6e71 100644
--- a/modules/game_cyclopedia/tab/boss_slots/boss_slots.lua
+++ b/modules/game_cyclopedia/tab/boss_slots/boss_slots.lua
@@ -1,4 +1,4 @@
-local UI = nil
+local UI = nil
function showBossSlot()
UI = g_ui.loadUI("boss_slots", contentContainer)
diff --git a/modules/game_cyclopedia/tab/bosstiary/bosstiary.lua b/modules/game_cyclopedia/tab/bosstiary/bosstiary.lua
index cd51e198a2..7c89300e1b 100644
--- a/modules/game_cyclopedia/tab/bosstiary/bosstiary.lua
+++ b/modules/game_cyclopedia/tab/bosstiary/bosstiary.lua
@@ -1,4 +1,4 @@
-local UI = nil
+local UI = nil
function showBosstiary()
UI = g_ui.loadUI("bosstiary", contentContainer)
UI:show()
diff --git a/modules/game_cyclopedia/tab/character/character.lua b/modules/game_cyclopedia/tab/character/character.lua
index 9f3625ab55..0643baeb6f 100644
--- a/modules/game_cyclopedia/tab/character/character.lua
+++ b/modules/game_cyclopedia/tab/character/character.lua
@@ -641,7 +641,7 @@ function Cyclopedia.loadCharacterCombatStats(data, mitigation, additionalSkillsA
-- Critical Chance
local skillIndex = skillsIndexes[Skill.CriticalChance]
local skill = additionalSkillsArray[skillIndex][2]
- UI.CombatStats.criticalChance.value:setText(skill .. "%")
+ UI.CombatStats.criticalChance.value:setText(string.format("%.2f%%", skill / 100))
if skill > 0 then
UI.CombatStats.criticalChance.value:setColor("#44AD25")
else
@@ -651,7 +651,7 @@ function Cyclopedia.loadCharacterCombatStats(data, mitigation, additionalSkillsA
-- Critical Damage
skillIndex = skillsIndexes[Skill.CriticalDamage]
skill = additionalSkillsArray[skillIndex][2]
- UI.CombatStats.criticalDamage.value:setText(skill .. "%")
+ UI.CombatStats.criticalDamage.value:setText(string.format("%.2f%%", skill / 100))
if skill > 0 then
UI.CombatStats.criticalDamage.value:setColor("#44AD25")
else
@@ -727,13 +727,7 @@ function Cyclopedia.loadCharacterCombatStats(data, mitigation, additionalSkillsA
value:setText(string.format("%.2f%%", percent / 100))
value:setColor("#C0C0C0")
value:setMarginRight(2)
-
- if percent > 0 then
- value:setColor("#44AD25")
- else
- value:setColor("#C0C0C0")
- end
-
+ value:setColor("#C0C0C0")
firstSpecial = firstSpecial and false
end
end
@@ -852,9 +846,7 @@ function Cyclopedia.loadCharacterGeneralStats(data, skills)
Cyclopedia.setCharacterSkillBase("magiclevel", data.magicLevel, data.baseMagicLevel)
for i = Skill.Fist + 1, Skill.Fishing + 1 do
- local skillLevel = skills[i][1]
- local baseSkill = skills[i][2]
- local skillPercent = skills[i][3]
+ local skillLevel, baseSkill, skillPercent = unpack(skills[i])
Cyclopedia.onSkillChange(player, i - 1, skillLevel, skillPercent)
Cyclopedia.onBaseCharacterSkillChange(player, i - 1, baseSkill)
end
diff --git a/modules/game_cyclopedia/tab/charms/charms.lua b/modules/game_cyclopedia/tab/charms/charms.lua
index 5616136a54..eeaecd2b88 100644
--- a/modules/game_cyclopedia/tab/charms/charms.lua
+++ b/modules/game_cyclopedia/tab/charms/charms.lua
@@ -1,4 +1,4 @@
-local UI = nil
+local UI = nil
function showCharms()
UI = g_ui.loadUI("charms", contentContainer)
diff --git a/modules/game_cyclopedia/tab/house/house.lua b/modules/game_cyclopedia/tab/house/house.lua
index dba48199d4..480e101c2a 100644
--- a/modules/game_cyclopedia/tab/house/house.lua
+++ b/modules/game_cyclopedia/tab/house/house.lua
@@ -1,4 +1,4 @@
-local UI = nil
+local UI = nil
function showHouse()
UI = g_ui.loadUI("house", contentContainer)
diff --git a/modules/game_cyclopedia/tab/items/items.lua b/modules/game_cyclopedia/tab/items/items.lua
index 66903c842a..4d307385a5 100644
--- a/modules/game_cyclopedia/tab/items/items.lua
+++ b/modules/game_cyclopedia/tab/items/items.lua
@@ -219,28 +219,11 @@ function Cyclopedia.internalCreateItem(data)
item:setId(data:getId())
item.Sprite:setItemId(data:getId())
item.Name:setText(marketData.name)
- local function getColorForValue(value)
- if value >= 1000000 then
- return "yellow"
- elseif value >= 100000 then
- return "purple"
- elseif value >= 10000 then
- return "blue"
- elseif value >= 1000 then
- return "green"
- elseif value >= 50 then
- return "grey"
- end
- return "white"
- end
local price = data:getMeanPrice()
- local rarity = getColorForValue(price)
+
item.Value = price
item.Vocation = marketData.restrictVocation
-
- if price > 0 then
- item.Rarity:setImageSource("/images/ui/rarity_" .. rarity)
- end
+ ItemsDatabase.setRarityItem(item.Sprite, item.Sprite:getItem())
function item.onClick(widget)
UI.InfoBase.SellBase.List:destroyChildren()
@@ -267,8 +250,8 @@ function Cyclopedia.internalCreateItem(data)
UI.SelectedItem.Sprite:setItemId(data:getId())
if price > 0 then
- UI.InfoBase.ResultGoldBase.Rarity:setImageSource("/images/ui/rarity_" .. rarity)
- UI.SelectedItem.Rarity:setImageSource("/images/ui/rarity_" .. rarity)
+ ItemsDatabase.setRarityItem(UI.SelectedItem.Rarity, price)
+ ItemsDatabase.setRarityItem(UI.InfoBase.ResultGoldBase.Rarity, price)
else
UI.InfoBase.ResultGoldBase.Rarity:setImageSource("")
UI.SelectedItem.Rarity:setImageSource("")
diff --git a/modules/game_cyclopedia/tab/items/items.otui b/modules/game_cyclopedia/tab/items/items.otui
index 8123e295b9..0ecadcd88a 100644
--- a/modules/game_cyclopedia/tab/items/items.otui
+++ b/modules/game_cyclopedia/tab/items/items.otui
@@ -394,7 +394,7 @@ UIWidget
UIWidget
id: Rarity
anchors.fill: parent
- image-border: 5
+ image-border: 8
margin: 1
UIWidget
id: Icon
diff --git a/modules/game_cyclopedia/tab/map/map.lua b/modules/game_cyclopedia/tab/map/map.lua
index 93344cb589..46814ac14d 100644
--- a/modules/game_cyclopedia/tab/map/map.lua
+++ b/modules/game_cyclopedia/tab/map/map.lua
@@ -1,4 +1,4 @@
-local UI = nil
+local UI = nil
local virtualFloor = 7
function showMap()
diff --git a/modules/game_cyclopedia/utils.lua b/modules/game_cyclopedia/utils.lua
index 7684d10134..440ed01ef8 100644
--- a/modules/game_cyclopedia/utils.lua
+++ b/modules/game_cyclopedia/utils.lua
@@ -1,4 +1,4 @@
-RACE = {
+RACE = {
[-1] = {name = "unknow", type = 2},
[0] = {name = "unknow", type = 2},
[1] = {name = "unknow", type = 2},
@@ -1012,8 +1012,8 @@ ACHIEVEMENTS = {
[80] = { name = "Party Animal", grade = 1, points = 1, secret = true, description = "Oh my god, it's a paaaaaaaaaaaarty! You're always in for fun, friends and booze and love being the center of attention. There's endless reasons to celebrate! Woohoo!" },
[81] = { name = "Fireworks in the Sky", grade = 1, points = 2, secret = true, description = "You love the moment right before your rocket takes off and explodes into beautiful colours - not only on new year's eve!" },
[82] = { name = "Quick as a Turtle", grade = 1, points = 2, secret = true, description = "There... is... simply... no... better... way - than to travel on the back of a turtle. At least you get to enjoy the beautiful surroundings of Laguna." },
- [83] = { name = "Polisher", grade = 2, points = 4, secret = true, description = "If you see a rusty item, you can't resist polishing it. There's always a little flask of rust remover in your inventory – who knows, there might be a golden armor beneath all that dirt!" },
- [84] = { name = "Ship's Kobold", grade = 2, points = 4, secret = true, description = "You’ve probably never gotten seasick in your life — you love spending your free time on the ocean and covered quite a lot of miles with ships. Aren’t you glad you didn’t have to swim all that?" },
+ [83] = { name = "Polisher", grade = 2, points = 4, secret = true, description = "If you see a rusty item, you can't resist polishing it. There's always a little flask of rust remover in your inventory - who knows, there might be a golden armor beneath all that dirt!" },
+ [84] = { name = "Ship's Kobold", grade = 2, points = 4, secret = true, description = "You've probably never gotten seasick in your life — you love spending your free time on the ocean and covered quite a lot of miles with ships. Aren't you glad you didn't have to swim all that?" },
[85] = { name = "Steampunked", grade = 1, points = 2, secret = true, description = "Travelling with the dwarven steamboats through the underground rivers is your preferred way of crossing the lands. No pesky seagulls, and good beer on board!" },
[86] = { name = "Vanity", grade = 1, points = 3, secret = true, description = "Aren't you just perfectly, wonderfully, beautifully gorgeous? You can't pass a mirror without admiring your looks. Or maybe doing a quick check whether something's stuck in your teeth, perhaps?" },
[87] = { name = "Superstitious", grade = 1, points = 2, secret = true, description = "Fortune tellers and horoscopes guide you through your life. And you probably wouldn't dare going on a big game hunt without your trusty voodoo skull giving you his approval for the day." },
@@ -1339,7 +1339,7 @@ ACHIEVEMENTS = {
-- [407] = Unknown/non-existent
[408] = { name = "Rift Warrior", grade = 1, points = 3, description = "You went through hell. Seven times. You defeated the demons. Countless times. You put an end to Ferumbras claims to ascendancy. Once and for all." },
-- [409] = Unknown/non-existent
- [410] = { name = "Hat Hunter", grade = 2, points = 5, description = "You sucessfully fought against all odds to protect your world from an ascending god! – You weren't there for the hat only after all?" },
+ [410] = { name = "Hat Hunter", grade = 2, points = 5, description = "You sucessfully fought against all odds to protect your world from an ascending god! - You weren't there for the hat only after all?" },
[411] = { name = "Ogre Chef", grade = 1, points = 1, description = "You didn't manage to become an ogre chief. But at least you are, beyond doubt, a worthy ogre chef." },
[412] = { name = "The Call of the Wild", grade = 1, points = 2, description = "You opposed man-eating ogres and clumsy clomps. You grappled with hungry chieftains, desperate goblins and angry spirits. So you truly overcame the wild vastness of Krailos." },
[413] = { name = "Ender of the End", grade = 2, points = 5, description = "You have entered the heart of destruction and valiantly defeated the world devourer. By your actions you have postponed the end of the world — at least for a while." },
diff --git a/modules/game_healthcircle/game_healthcircle.lua b/modules/game_healthcircle/game_healthcircle.lua
index e0b4b2bacf..1a57dbb6d9 100644
--- a/modules/game_healthcircle/game_healthcircle.lua
+++ b/modules/game_healthcircle/game_healthcircle.lua
@@ -114,6 +114,7 @@ function terminate()
disconnect(g_game, {
onGameStart = setPlayerValues
})
+ statsBarMenuLoaded = false
end
-------------------------------------------------
diff --git a/modules/game_hotkeys/hotkeys_manager.lua b/modules/game_hotkeys/hotkeys_manager.lua
index 941758480c..3bef1c9ef6 100644
--- a/modules/game_hotkeys/hotkeys_manager.lua
+++ b/modules/game_hotkeys/hotkeys_manager.lua
@@ -64,7 +64,13 @@ local hotkeysWindowButton = nil
-- public functions
function init()
- g_keyboard.bindKeyDown('Ctrl+K', toggle)
+ Keybind.new("Windows", "Show/hide Hotkeys", "Ctrl+K", "")
+ Keybind.bind("Windows", "Show/hide Hotkeys", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
hotkeysWindow = g_ui.displayUI('hotkeys_manager')
hotkeysWindow:setVisible(false)
hotkeysWindowButton = modules.client_topmenu.addRightGameToggleButton('hotkeysWindowButton', tr('Hotkeys'), '/images/options/hotkeys', toggle)
@@ -128,7 +134,7 @@ function terminate()
onGameEnd = offline
})
- g_keyboard.unbindKeyDown('Ctrl+K')
+ Keybind.delete("Windows", "Show/hide Hotkeys")
unload()
diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua
index 1f87592706..09f30763b1 100644
--- a/modules/game_interface/gameinterface.lua
+++ b/modules/game_interface/gameinterface.lua
@@ -154,25 +154,41 @@ function bindKeys()
bindTurnKey('Ctrl+Numpad2', South)
bindTurnKey('Ctrl+Numpad4', West)
- g_keyboard.bindKeyPress('Escape', function()
- g_game.cancelAttackAndFollow()
- end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+=', function()
gameMapPanel:zoomIn()
end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+-', function()
gameMapPanel:zoomOut()
end, gameRootPanel)
- g_keyboard.bindKeyDown('Ctrl+Q', function()
- tryLogout(false)
- end, gameRootPanel)
- g_keyboard.bindKeyDown('Ctrl+L', function()
- tryLogout(false)
- end, gameRootPanel)
- g_keyboard.bindKeyDown('Alt+W', function()
- g_map.cleanTexts()
- modules.game_textmessage.clearMessages()
- end, gameRootPanel)
+
+ Keybind.new("Movement", "Stop All Actions", "Esc", "", true)
+ Keybind.bind("Movement", "Stop All Actions", {
+ {
+ type = KEY_PRESS,
+ callback = function()
+ g_game.cancelAttackAndFollow()
+ end,
+ }
+ }, gameRootPanel)
+
+ Keybind.new("Misc", "Logout", "Ctrl+L", "Ctrl+Q")
+ Keybind.bind("Misc", "Logout", {
+ {
+ type = KEY_PRESS,
+ callback = function() tryLogout(false) end,
+ }
+ }, gameRootPanel)
+
+ Keybind.new("UI", "Clear All Texts", "Ctrl+W", "")
+ Keybind.bind("UI", "Clear All Texts", {
+ {
+ type = KEY_DOWN,
+ callback = function()
+ g_map.cleanTexts()
+ modules.game_textmessage.clearMessages()
+ end,
+ }
+ }, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+.', nextViewMode, gameRootPanel)
end
@@ -251,6 +267,9 @@ function terminate()
logoutButton:destroy()
gameRootPanel:destroy()
+ Keybind.delete("Movement", "Stop All Actions")
+ Keybind.delete("Misc", "Logout")
+ Keybind.delete("UI", "Clear All Texts")
end
function onGameStart()
diff --git a/modules/game_interface/widgets/statsbar.lua b/modules/game_interface/widgets/statsbar.lua
index b66ed64a86..32e1b43f97 100644
--- a/modules/game_interface/widgets/statsbar.lua
+++ b/modules/game_interface/widgets/statsbar.lua
@@ -309,7 +309,6 @@ function StatsBar.reloadCurrentStatsBarQuickInfo_state(localPlayer, now, old)
if now == old then
return
end
-
local bitsChanged = bit.bxor(now, old)
for i = 1, 32 do
local pow = math.pow(2, i - 1)
diff --git a/modules/game_inventory/inventory.lua b/modules/game_inventory/inventory.lua
index 9855c20697..b3a4dc02f2 100644
--- a/modules/game_inventory/inventory.lua
+++ b/modules/game_inventory/inventory.lua
@@ -43,6 +43,21 @@ local function updateSlotsDuration()
end
-- @
+ if not modules.client_options.getOption('showExpiryInInvetory') then
+ stopEvent()
+ local ui = getInventoryUi()
+ for slot, itemDurationReg in pairs(itemSlotsWithDuration) do
+ local getSlotInfo = getSlotPanelBySlot[slot]
+ if getSlotInfo then
+ local slotPanel = getSlotInfo(ui)
+ if slotPanel and slotPanel.item then
+ slotPanel.item.duration:setText("")
+ end
+ end
+ end
+ return
+ end
+
local currTime = g_clock.seconds()
local ui = getInventoryUi()
local hasItemsWithDuration = false
@@ -56,7 +71,7 @@ local function updateSlotsDuration()
if getSlotInfo then
local slotPanel = getSlotInfo(ui)
if slotPanel and slotPanel.item then
- slotPanel.item.Duration:setText(formatDuration(durationTimeLeft))
+ slotPanel.item.duration:setText(formatDuration(durationTimeLeft))
end
end
end
@@ -110,20 +125,29 @@ local function inventoryEvent(player, slot, item, oldItem)
toggler:setEnabled(not item)
slotPanel.item:setWidth(34)
slotPanel.item:setHeight(34)
+ slotPanel.item.duration:setText("")
+ slotPanel.item.charges:setText("")
if g_game.getFeature(GameThingClock) then
if item and item:getDurationTime() > 0 then
- itemSlotsWithDuration[slot] = {
- item = item,
- timeEnd = g_clock.seconds() + item:getDurationTime()
- }
- updateSlotsDuration()
+ if not itemSlotsWithDuration[slot] or itemSlotsWithDuration[slot].item ~= item then
+ itemSlotsWithDuration[slot] = {
+ item = item,
+ timeEnd = g_clock.seconds() + item:getDurationTime()
+ }
+ end
+ if modules.client_options.getOption('showExpiryInInvetory') then
+ if not updateSlotsDurationEvent then
+ updateSlotsDuration()
+ end
+ end
else
itemSlotsWithDuration[slot] = nil
- if slotPanel and slotPanel.item then
- slotPanel.item.Duration:setText("")
- end
end
end
+
+ if modules.client_options.getOption('showExpiryInInvetory') then
+ ItemsDatabase.setCharges(slotPanel.item, item)
+ end
ItemsDatabase.setTier(slotPanel.item, item)
end
@@ -403,3 +427,20 @@ end
function getSlot5()
return inventoryController.ui.onPanel.shield
end
+
+function reloadInventory()
+ if modules.client_options.getOption('showExpiryInInvetory') then
+ updateSlotsDuration()
+ end
+
+ for slot, getSlotInfo in pairs(getSlotPanelBySlot) do
+ local ui = getInventoryUi()
+ local slotPanel, toggler = getSlotInfo(ui)
+ if slotPanel then
+ local player = g_game.getLocalPlayer()
+ if player then
+ inventoryEvent(player, slot, player:getInventoryItem(slot))
+ end
+ end
+ end
+end
diff --git a/modules/game_outfit/outfit.lua b/modules/game_outfit/outfit.lua
index ece28278b8..b13eac7425 100644
--- a/modules/game_outfit/outfit.lua
+++ b/modules/game_outfit/outfit.lua
@@ -512,6 +512,8 @@ function destroy()
showTitleCheck = nil
colorBoxes = {}
currentColorBox = nil
+ previewCreature:destroy()
+ previewCreature = nil
if appearanceGroup then
appearanceGroup:destroy()
appearanceGroup = nil
diff --git a/modules/game_playerdeath/playerdeath.lua b/modules/game_playerdeath/playerdeath.lua
index abb55968de..7a7ee08950 100644
--- a/modules/game_playerdeath/playerdeath.lua
+++ b/modules/game_playerdeath/playerdeath.lua
@@ -17,7 +17,7 @@ local deathTexts = {
}
deathController = Controller:new()
-deathController:setUI('deathWindow')
+deathController:setUI('deathwindow')
function deathController:onInit()
deathController:registerEvents(g_game, {
onDeath = display,
diff --git a/modules/game_playermount/playermount.lua b/modules/game_playermount/playermount.lua
index d29596ff37..477c040851 100644
--- a/modules/game_playermount/playermount.lua
+++ b/modules/game_playermount/playermount.lua
@@ -18,13 +18,19 @@ end
function online()
if g_game.getFeature(GamePlayerMounts) then
- g_keyboard.bindKeyDown('Ctrl+R', toggleMount)
+ Keybind.new("Movement", "Mount/dismount", "Ctrl+R", "")
+ Keybind.bind("Movement", "Mount/dismount", {
+ {
+ type = KEY_DOWN,
+ callback = toggleMount,
+ }
+ })
end
end
function offline()
if g_game.getFeature(GamePlayerMounts) then
- g_keyboard.unbindKeyDown('Ctrl+R')
+ Keybind.delete("Movement", "Mount/dismount")
end
end
diff --git a/modules/game_questlog/questlog.lua b/modules/game_questlog/questlog.lua
index 7a8012f5c5..a6dccf23ea 100644
--- a/modules/game_questlog/questlog.lua
+++ b/modules/game_questlog/questlog.lua
@@ -15,6 +15,16 @@ function init()
onQuestLine = onGameQuestLine,
onGameEnd = destroyWindows
})
+
+ Keybind.new("Windows", "Show/hide quest Log", "", "")
+ Keybind.bind("Windows", "Show/hide quest Log", {
+ {
+ type = KEY_DOWN,
+ callback = function()
+ g_game.requestQuestLog()
+ end,
+ }
+ })
end
function terminate()
@@ -26,15 +36,19 @@ function terminate()
destroyWindows()
questLogButton:destroy()
+ questLogButton = nil
+ Keybind.delete("Windows", "Show/hide quest Log")
end
function destroyWindows()
if questLogWindow then
questLogWindow:destroy()
+ questLogWindow = nil
end
if questLineWindow then
questLineWindow:destroy()
+ questLineWindow = nil
end
end
@@ -69,6 +83,7 @@ function onGameQuestLine(questId, questMissions)
end
if questLineWindow then
questLineWindow:destroy()
+ questLineWindow = nil
end
questLineWindow = g_ui.createWidget('QuestLineWindow', rootWidget)
@@ -85,7 +100,7 @@ function onGameQuestLine(questId, questMissions)
})
for i, questMission in pairs(questMissions) do
- local name, description = unpack(questMission)
+ local name, description, missionId = unpack(questMission)
local missionLabel = g_ui.createWidget('MissionLabel')
missionLabel:setText(name)
diff --git a/modules/game_quickloot/quickloot.lua b/modules/game_quickloot/quickloot.lua
index 25f2ba6c90..9294eae918 100644
--- a/modules/game_quickloot/quickloot.lua
+++ b/modules/game_quickloot/quickloot.lua
@@ -1,4 +1,4 @@
-QuickLoot = {}
+QuickLoot = {}
local function getFilter(id)
local filter = {
@@ -219,9 +219,9 @@ function QuickLoot.Define()
quickLootController.ui.fallbackPanel.checkbox:setChecked(fallback)
-- LuaFormatter off
local slotBags = {
- { color = "#484848", name = "Unassigned", type = 1 },
+ { color = "#484848", name = "Unassigned", type = 31 },
{ color = "#414141", name = "Gold", type = 30 },
- { color = "#484848", name = "Armors", type = 31 },
+ { color = "#484848", name = "Armors", type = 1 },
{ color = "#414141", name = "Amulets", type = 2 },
{ color = "#484848", name = "Boots", type = 3 },
{ color = "#414141", name = "Containers", type = 4 },
@@ -259,8 +259,8 @@ function QuickLoot.Define()
for _, container in pairs(lootContainers) do
if container[1] == id then
- local lootContainerId = container[2]
- local obtainerContainerId = container[3]
+ local lootContainerId = container[3]
+ local obtainerContainerId = container[2]
widget.item:setItemId(lootContainerId)
widget.item2:setItemId(obtainerContainerId)
diff --git a/modules/game_quickloot/quickloot.otui b/modules/game_quickloot/quickloot.otui
index 97747eb8e8..16980637cc 100644
--- a/modules/game_quickloot/quickloot.otui
+++ b/modules/game_quickloot/quickloot.otui
@@ -49,9 +49,9 @@ QuicklootBagLabel < UIWidget
margin-left: 2
SelectBagButton
id: selectBag
- &click: 6
- &Select:0
- &borrar:1
+ &click: 2
+ &Select:4
+ &borrar:5
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
@@ -61,9 +61,9 @@ QuicklootBagLabel < UIWidget
anchors.verticalCenter: parent.verticalCenter
anchors.right: selectBag.left
margin-right: 5
- &click: 6
- &Select:0
- &borrar:1
+ &click: 2
+ &Select:4
+ &borrar:5
@onClick: modules.game_quickloot.QuickLoot.clearItem(self)
Item
id: item
@@ -71,18 +71,18 @@ QuicklootBagLabel < UIWidget
anchors.right: removeBag.left
margin-right: 5
opacity: 1.0
- &click: 6
- &Select:0
- &borrar:1
+ &click: 2
+ &Select:4
+ &borrar:5
@onClick: modules.game_quickloot.QuickLoot.openContainer(self)
$pressed:
opacity: 0.5
SelectBagButton
id: selectBag2
- &click: 2
- &Select:4
- &borrar:5
+ &click: 6
+ &Select:0
+ &borrar:1
anchors.verticalCenter: parent.verticalCenter
anchors.right: item.left
margin-right: 5
@@ -92,18 +92,18 @@ QuicklootBagLabel < UIWidget
anchors.verticalCenter: parent.verticalCenter
anchors.right: selectBag2.left
margin-right: 5
- &click: 2
- &Select:4
- &borrar:5
+ &click: 6
+ &Select:0
+ &borrar:1
@onClick: modules.game_quickloot.QuickLoot.clearItem(self)
Item
id: item2
anchors.verticalCenter: parent.verticalCenter
anchors.right: removeBag2.left
margin-right: 5
- &click: 2
- &Select:4
- &borrar:5
+ &click: 6
+ &Select:0
+ &borrar:1
opacity: 1.0
@onClick: modules.game_quickloot.QuickLoot.openContainer(self)
$pressed:
diff --git a/modules/game_ruleviolation/ruleviolation.lua b/modules/game_ruleviolation/ruleviolation.lua
index 61e4609fca..66e7935dc3 100644
--- a/modules/game_ruleviolation/ruleviolation.lua
+++ b/modules/game_ruleviolation/ruleviolation.lua
@@ -45,7 +45,7 @@ function init()
reasonsTextList = ruleViolationWindow:getChildById('reasonList')
actionsTextList = ruleViolationWindow:getChildById('actionList')
- g_keyboard.bindKeyDown('Ctrl+Y', function()
+ g_keyboard.bindKeyDown('Ctrl+U', function()
show()
end)
@@ -58,7 +58,7 @@ function terminate()
disconnect(g_game, {
onGMActions = loadReasons
})
- g_keyboard.unbindKeyDown('Ctrl+Y')
+ g_keyboard.unbindKeyDown('Ctrl+U')
ruleViolationWindow:destroy()
end
diff --git a/modules/game_shaders/shaders.lua b/modules/game_shaders/shaders.lua
index 1418b29fca..0b75c66a25 100644
--- a/modules/game_shaders/shaders.lua
+++ b/modules/game_shaders/shaders.lua
@@ -124,19 +124,23 @@ function ShaderController:onInit()
for _, opts in pairs(MOUNT_SHADERS) do
registerShader(opts, 'setupMountShader')
end
+ Keybind.new('Windows', 'show/hide Shader Windows', HOTKEY, '')
+ Keybind.bind('Windows', 'show/hide Shader Windows', {
+ {
+ type = KEY_DOWN,
+ callback = function() ShaderController.ui:setVisible(not ShaderController.ui:isVisible()) end,
+ }
+ })
end
function ShaderController:onTerminate()
g_shaders.clear()
+ Keybind.delete('Windows', 'show/hide Shader Windows')
end
function ShaderController:onGameStart()
attachShaders()
- self:bindKeyDown(HOTKEY, function()
- ShaderController.ui:setVisible(not ShaderController.ui:isVisible())
- end)
-
self:loadHtml('shaders.html', modules.game_interface.getMapPanel())
for _, opts in pairs(MAP_SHADERS) do
diff --git a/modules/game_shaders/shaders/fragment/bloom.frag b/modules/game_shaders/shaders/fragment/bloom.frag
index 6571dd558c..acde4de9d6 100644
--- a/modules/game_shaders/shaders/fragment/bloom.frag
+++ b/modules/game_shaders/shaders/fragment/bloom.frag
@@ -5,11 +5,9 @@ varying vec2 v_TexCoord;
void main()
{
vec4 color = texture2D(u_Tex0, v_TexCoord);
- int j;
- int i;
- for (i = -4; i <= 4; i++)
- for (j = -4; j <= 4; j++)
+ for (int i = -4; i <= 4; i++)
+ for (int j = -4; j <= 4; j++)
color += texture2D(u_Tex0, v_TexCoord + vec2(i, j) * 0.003) * 0.008;
gl_FragColor = color;
diff --git a/modules/game_skills/skills.lua b/modules/game_skills/skills.lua
index ea683ca775..7c9294b383 100644
--- a/modules/game_skills/skills.lua
+++ b/modules/game_skills/skills.lua
@@ -31,7 +31,13 @@ function init()
skillsButton:setOn(true)
skillsWindow = g_ui.loadUI('skills')
- g_keyboard.bindKeyDown('Alt+S', toggle)
+ Keybind.new("Windows", "Show/hide skills windows", "Alt+S", "")
+ Keybind.bind("Windows", "Show/hide skills windows", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
skillSettings = g_settings.getNode('skills-hide')
if not skillSettings then
@@ -69,7 +75,7 @@ function terminate()
onGameEnd = offline
})
- g_keyboard.unbindKeyDown('Alt+S')
+ Keybind.delete("Windows", "Show/hide skills windows")
skillsWindow:destroy()
skillsButton:destroy()
@@ -119,8 +125,10 @@ function setSkillValue(id, value)
local skill = skillsWindow:recursiveGetChildById(id)
if skill then
local widget = skill:getChildById('value')
- if id == "skillId7" or id == "skillId9" or id == "skillId11" or id == "skillId13" or id == "skillId14" or id == "skillId15" or id == "skillId16" then
- local value = value / 100
+ if id == "skillId7" or id == "skillId8" or id == "skillId9" or id == "skillId11" or id == "skillId13" or id == "skillId14" or id == "skillId15" or id == "skillId16" then
+ if g_game.getFeature(GameEnterGameShowAppearance) then
+ value = value / 100
+ end
widget:setText(value .. "%")
else
widget:setText(value)
diff --git a/modules/game_skills/skills.otui b/modules/game_skills/skills.otui
index ae13deb5da..eb66b77821 100644
--- a/modules/game_skills/skills.otui
+++ b/modules/game_skills/skills.otui
@@ -217,6 +217,10 @@ MiniWindow
image-source: /images/icons/icon_fishing
image-size: 9 9
SkillPercentPanel
+
+ HorizontalSeparator
+ margin-top: 5
+ margin-bottom: 5
SmallSkillButton
id: skillId7
diff --git a/modules/game_spelllist/spelllist.lua b/modules/game_spelllist/spelllist.lua
index 7d9f682a8f..787ed76548 100644
--- a/modules/game_spelllist/spelllist.lua
+++ b/modules/game_spelllist/spelllist.lua
@@ -176,6 +176,13 @@ function init()
if g_game.isOnline() then
online()
end
+ Keybind.new("Windows", "Show/hide spell list", "Alt+L", "")
+ Keybind.bind("Windows", "Show/hide spell list", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
end
function terminate()
@@ -199,6 +206,7 @@ function terminate()
vocationRadioGroup:destroy()
groupRadioGroup:destroy()
premiumRadioGroup:destroy()
+ Keybind.delete("Windows", "Show/hide spell list")
end
function initializeSpelllist()
diff --git a/modules/game_tasks/tasks.lua b/modules/game_tasks/tasks.lua
index 91679d3a18..bedf3401ab 100644
--- a/modules/game_tasks/tasks.lua
+++ b/modules/game_tasks/tasks.lua
@@ -12,7 +12,14 @@ function init()
window = g_ui.displayUI('tasks')
window:setVisible(false)
- g_keyboard.bindKeyDown('Ctrl+A', toggleWindow)
+ Keybind.new('Windows', 'show/hide Tasks Windows', 'Ctrl+A', '')
+ Keybind.bind('Windows', 'show/hide Tasks Windows', {
+ {
+ type = KEY_DOWN,
+ callback = toggleWindow,
+ }
+ })
+
g_keyboard.bindKeyDown('Escape', hideWindowzz)
taskButton = modules.client_topmenu.addLeftGameButton('taskButton', tr('Tasks'), '/modules/game_tasks/images/taskIcon', toggleWindow)
ProtocolGame.registerExtendedJSONOpcode(215, parseOpcode)
@@ -25,6 +32,7 @@ function terminate()
ProtocolGame.unregisterExtendedJSONOpcode(215, parseOpcode)
taskButton:destroy()
destroy()
+ Keybind.delete('Windows', 'show/hide Tasks Windows')
end
function onGameStart()
diff --git a/modules/game_textmessage/textmessage.lua b/modules/game_textmessage/textmessage.lua
index 50279e654f..4e4df247a4 100644
--- a/modules/game_textmessage/textmessage.lua
+++ b/modules/game_textmessage/textmessage.lua
@@ -159,9 +159,10 @@ function displayMessage(mode, text)
if msgtype == MessageSettings.loot then
local coloredText = ItemsDatabase.setColorLootMessage(text)
label:setColoredText(coloredText)
- local console = modules.game_console
- local consoleBuffer = console.consoleTabBar:getTabPanel(console.getTab("Server Log")):getChildById('consoleBuffer')
- consoleBuffer:getLastChild():setColoredText(coloredText)
+ local getTabServerLog = modules.game_console.consoleTabBar:getTabPanel(modules.game_console.getTab("Server Log"))
+ if getTabServerLog then
+ getTabServerLog:getChildById('consoleBuffer'):getLastChild():setColoredText(coloredText)
+ end
else
label:setText(text)
label:setColor(msgtype.color)
diff --git a/modules/game_viplist/addgroup.otui b/modules/game_viplist/addgroup.otui
index 0cefe46c50..eeeb159771 100644
--- a/modules/game_viplist/addgroup.otui
+++ b/modules/game_viplist/addgroup.otui
@@ -4,7 +4,7 @@ MainWindow
text-offset: 0 1
@onEnter: modules.game_viplist.addGroup()
@onEscape: |
- self:getParent():destroy()
+ self:destroy()
modules.game_viplist.addGroupWindow = nil
Label
diff --git a/modules/game_viplist/viplist.lua b/modules/game_viplist/viplist.lua
index 1c590d66b9..381ebe8b46 100644
--- a/modules/game_viplist/viplist.lua
+++ b/modules/game_viplist/viplist.lua
@@ -20,8 +20,13 @@ local globalSettings = {
controllerVip = Controller:new()
function controllerVip:onInit()
- g_keyboard.bindKeyDown('Ctrl+P', toggle)
-
+ Keybind.new("Windows", "Show/hide VIP list", "Ctrl+P", "")
+ Keybind.bind("Windows", "Show/hide VIP list", {
+ {
+ type = KEY_DOWN,
+ callback = toggle,
+ }
+ })
vipButton = modules.game_mainpanel.addToggleButton('vipListButton', tr('VIP List') .. ' (Ctrl+P)',
'/images/options/button_vip', toggle, false, 3)
vipWindow = g_ui.loadUI('viplist')
@@ -50,7 +55,7 @@ function controllerVip:onInit()
end
function controllerVip:onTerminate()
- g_keyboard.unbindKeyDown('Ctrl+P')
+ Keybind.delete("Windows", "Show/hide VIP list")
local ArrayWidgets = {addVipWindow, editVipWindow, vipWindow, vipButton, addGroupWindow}
for _, widget in ipairs(ArrayWidgets) do
if widget ~= nil or widget then
diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua
index 5323f505c1..5acb080ba6 100644
--- a/modules/gamelib/const.lua
+++ b/modules/gamelib/const.lua
@@ -74,6 +74,17 @@ SouthEast = Directions.SouthEast
SouthWest = Directions.SouthWest
NorthWest = Directions.NorthWest
+DirectionString = {
+ [North] = "North",
+ [East] = "East",
+ [South] = "South",
+ [West] = "West",
+ [NorthEast] = "North East",
+ [SouthEast] = "South East",
+ [SouthWest] = "South West",
+ [NorthWest] = "North West"
+ }
+
FightOffensive = 1
FightBalanced = 2
FightDefensive = 3
diff --git a/modules/gamelib/items.lua b/modules/gamelib/items.lua
index 47cc12f1cd..cea469c55b 100644
--- a/modules/gamelib/items.lua
+++ b/modules/gamelib/items.lua
@@ -24,6 +24,21 @@ local function getColorForValue(value)
end
end
+local function clipfunction(value)
+ if value >= 1000000 then
+ return "128 0 32 32"
+ elseif value >= 100000 then
+ return "96 0 32 32"
+ elseif value >= 10000 then
+ return "64 0 32 32"
+ elseif value >= 1000 then
+ return "32 0 32 32"
+ elseif value >= 50 then
+ return "0 0 32 32"
+ end
+ return ""
+end
+
function ItemsDatabase.setRarityItem(widget, item, style)
if not g_game.getFeature(GameColorizedLootValue) then
return
@@ -33,14 +48,26 @@ function ItemsDatabase.setRarityItem(widget, item, style)
return
end
+ local frameOption = modules.client_options.getOption('framesRarity')
+ if frameOption == "none" then
+ return
+ end
if item then
local price = type(item) == "number" and item or (item and item:getMeanPrice()) or 0
local itemRarity = getColorForValue(price)
- local imagePath = '/images/ui/item'
- if itemRarity and itemRarity ~= "white" then -- necessary in setColorLootMessage
- imagePath = '/images/ui/rarity_' .. itemRarity
+ if itemRarity then
+ local clip = clipfunction(price)
+ if clip ~= "" then
+ local imagePath = '/images/ui/item'
+ if frameOption == "frames" then
+ imagePath = "/images/ui/rarity_frames"
+ elseif frameOption == "corners" then
+ imagePath = "/images/ui/containerslot-coloredges"
+ end
+ widget:setImageClip(clip)
+ widget:setImageSource(imagePath)
+ end
end
- widget:setImageSource(imagePath)
end
if style then
@@ -84,3 +111,37 @@ function ItemsDatabase.setTier(widget, item)
widget.tier:setVisible(false)
end
end
+
+function ItemsDatabase.setCharges(widget, item, style)
+ if not g_game.getFeature(GameThingCounter) or not widget then
+ return
+ end
+
+ if item and item:getCharges() > 0 then
+ widget.charges:setText(item:getCharges())
+ else
+ widget.charges:setText("")
+ end
+
+ if style then
+ widget:setStyle(style)
+ end
+end
+
+
+function ItemsDatabase.setDuration(widget, item, style)
+ if not g_game.getFeature(GameThingClock) or not widget then
+ return
+ end
+
+ if item and item:getDurationTime() > 0 then
+ local durationTimeLeft = item:getDurationTime()
+ widget.duration:setText(string.format("%dm%02d", durationTimeLeft / 60, durationTimeLeft % 60))
+ else
+ widget.duration:setText("")
+ end
+
+ if style then
+ widget:setStyle(style)
+ end
+end
diff --git a/modules/modulelib/controller.lua b/modules/modulelib/controller.lua
index cfedbd5a17..634bd633ab 100644
--- a/modules/modulelib/controller.lua
+++ b/modules/modulelib/controller.lua
@@ -59,6 +59,7 @@ Controller = {
ui = nil,
name = nil,
attrs = nil,
+ extendedOpcodes = nil,
opcodes = nil,
events = nil,
htmlRoot = nil,
@@ -75,7 +76,8 @@ function Controller:new()
scheduledEvents = {},
keyboardEvents = {},
attrs = {},
- opcodes = {}
+ extendedOpcodes = {},
+ opcodes = {},
}
setmetatable(obj, self)
self.__index = self
@@ -194,10 +196,6 @@ function Controller:setUI(name, parent)
end
function Controller:terminate()
- if self.onTerminate then
- self:onTerminate()
- end
-
if self.onGameStart then
disconnect(g_game, { onGameStart = self.onGameStart })
end
@@ -207,14 +205,22 @@ function Controller:terminate()
disconnect(g_game, { onGameEnd = self.onGameEnd })
end
+ if self.onTerminate then
+ self:onTerminate()
+ end
+
for i, event in pairs(self.keyboardEvents) do
g_keyboard['unbind' .. event.name](event.args[1], event.args[2], event.args[3])
end
- for i, opcode in pairs(self.opcodes) do
+ for i, opcode in pairs(self.extendedOpcodes) do
ProtocolGame.unregisterExtendedOpcode(opcode)
end
+ for _, opcode in ipairs(self.opcodes) do
+ ProtocolGame.unregisterOpcode(opcode)
+ end
+
for type, events in pairs(self.events) do
if events ~= nil then
for _, event in pairs(events) do
@@ -239,6 +245,7 @@ function Controller:terminate()
self.attrs = nil
self.events = nil
self.dataUI = nil
+ self.extendedOpcodes = nil
self.opcodes = nil
self.keyboardEvents = nil
self.keyboardAnchor = nil
@@ -267,6 +274,11 @@ end
function Controller:registerExtendedOpcode(opcode, fnc)
ProtocolGame.registerExtendedOpcode(opcode, fnc)
+ table.insert(self.extendedOpcodes, opcode)
+end
+
+function Controller:registerOpcode(opcode, fnc)
+ ProtocolGame.registerOpcode(opcode, fnc)
table.insert(self.opcodes, opcode)
end
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cdb1f8882b..c797894b25 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,6 +4,11 @@ project(otclient)
# *****************************************************************************
# Options
# *****************************************************************************
+if(CMAKE_BASE_NAME STREQUAL "em++")
+ set(WASM ON)
+ message(STATUS "WASM: ON")
+endif()
+
option(TOGGLE_FRAMEWORK_GRAPHICS "Use Graphics " ON)
option(TOGGLE_FRAMEWORK_SOUND "Use SOUND " ON)
option(TOGGLE_FRAMEWORK_XML "Use XML " ON)
@@ -67,7 +72,7 @@ endif()
if (TOGGLE_FRAMEWORK_EDITOR)
set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DFRAMEWORK_EDITOR)
endif()
-if (ANDROID)
+if (ANDROID OR WASM)
set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DOPENGL_ES=2)
endif()
@@ -207,7 +212,7 @@ if(APPLE)
find_library(FOUNDATION Foundation REQUIRED)
find_library(IOKIT IOKit REQUIRED)
endif()
-if(UNIX AND NOT ANDROID)
+if(UNIX AND NOT ANDROID AND NOT WASM)
find_package(X11 REQUIRED)
endif()
if(WIN32)
@@ -220,7 +225,9 @@ if(TOGGLE_DIRECTX)
find_package(DirectX REQUIRED)
endif()
if(TOGGLE_FRAMEWORK_SOUND)
- find_package(OpenAL CONFIG REQUIRED)
+ if(NOT WASM)
+ find_package(OpenAL CONFIG REQUIRED)
+ endif()
find_package(VorbisFile REQUIRED)
find_package(Vorbis REQUIRED)
find_package(Ogg REQUIRED)
@@ -235,9 +242,14 @@ if(ANDROID)
find_package(game-activity REQUIRED CONFIG)
find_package(EGL REQUIRED)
else()
- find_package(OpenGL REQUIRED)
- find_package(GLEW REQUIRED)
- find_package(LuaJIT REQUIRED)
+ if(NOT WASM)
+ find_package(OpenGL REQUIRED)
+ find_package(GLEW REQUIRED)
+ find_package(LuaJIT REQUIRED)
+ else()
+ set(LUA_LIBRARY ${LUA_LIBRARY} ${CMAKE_SOURCE_DIR}/browser/include/lua51/liblua.a)
+ set(BROWSER_INCLUDE_DIR ${BROWSER_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/browser/include)
+ endif()
endif()
# *****************************************************************************
@@ -299,6 +311,8 @@ set(SOURCE_FILES
framework/stdext/qrcodegen.cpp
framework/util/color.cpp
framework/util/crypt.cpp
+ framework/proxy/proxy.cpp
+ framework/proxy/proxy_client.cpp
client/animatedtext.cpp
client/animator.cpp
@@ -420,7 +434,17 @@ if (TOGGLE_FRAMEWORK_SOUND)
)
endif()
+if (WASM)
+ set(SOURCE_FILES ${SOURCE_FILES}
+ framework/platform/browserplatform.cpp
+ framework/platform/browserwindow.cpp
+ framework/net/webconnection.cpp
+ )
+endif()
+
+
target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_FILES})
+target_link_options(${PROJECT_NAME} PUBLIC -flto=auto)
# *****************************************************************************
# Includes and librarys
@@ -543,6 +567,86 @@ elseif(ANDROID)
log
pugixml::pugixml
)
+
+elseif(WASM)
+ target_include_directories(${PROJECT_NAME}
+ PRIVATE
+ ${CMAKE_SOURCE_DIR}/src
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${Protobuf_INCLUDE_DIRS}
+ ${GMP_INCLUDE_DIR}
+ ${PHYSFS_INCLUDE_DIR}
+ ${GLEW_INCLUDE_DIR}
+ ${PARALLEL_HASHMAP_INCLUDE_DIRS}
+ ${NLOHMANN_JSON_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIR}
+ ${BROWSER_INCLUDE_DIR}
+ )
+ target_link_libraries(${PROJECT_NAME}
+ PRIVATE
+ ${LUA_LIBRARY}
+ ${PHYSFS_LIBRARY}
+ ${ZLIB_LIBRARY}
+ ${PROTOBUF_LIBRARY}
+ ${NLOHMANN_JSON_LIBRARY}
+ ${GLEW_LIBRARY}
+ ${OPENGL_LIBRARIES}
+ ${DirectX_LIBRARY}
+ ${DirectX_LIBRARIES}
+ ${OGG_LIBRARY}
+ ${VORBISFILE_LIBRARY}
+ ${VORBIS_LIBRARY}
+ ${GMP_LIBRARY}
+ ${STDUUID}
+ ${FOUNDATION}
+ ${IOKIT}
+ ${OPENSSL_LIBRARY}
+ ${OPENSSL_CRYPTO_LIBRARY}
+ ${HTTPLIB_LIBRARY}
+
+ protobuf::libprotobuf protobuf
+ absl::log_internal_check_op
+
+ Threads::Threads
+ asio::asio
+ # OpenAL::OpenAL (using emscripten openal api)
+ LibLZMA::LibLZMA
+ pugixml::pugixml
+ ZLIB::ZLIB
+ OpenSSL::SSL
+ OpenSSL::Crypto
+ httplib::httplib
+ )
+
+
+ get_property(linkflags TARGET ${PROJECT_NAME} PROPERTY LINK_FLAGS)
+
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ target_compile_options(${PROJECT_NAME}
+ PRIVATE
+ -Wall -Wextra -Wpedantic
+ )
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
+ set(linkflags "${linkflags} -sASSERTIONS=1")
+ endif()
+
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -ffast-math -frtti -pthread -fexceptions")
+ set(linkflags "${linkflags}
+ -sINVOKE_RUN=0 -sEXIT_RUNTIME=1 -sTEXTDECODER=0 -sCASE_INSENSITIVE_FS=1
+ -lopenal -lidbfs.js -lwebsocket.js -sEXPORTED_RUNTIME_METHODS=ccall -sEXPORTED_FUNCTIONS=_main,_paste_return
+ -sINCOMING_MODULE_JS_API=[locateFile,preRun,postRun,print,printErr,canvas,setStatus,monitorRunDependencies]
+ -sFORCE_FILESYSTEM -sMALLOC=mimalloc -sWEBSOCKET_SUBPROTOCOL=binary -sOFFSCREENCANVAS_SUPPORT=1
+ -sOFFSCREEN_FRAMEBUFFER -sENVIRONMENT=web,worker -sPROXY_TO_PTHREAD -sFULL_ES2=1
+ -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 -sUSE_PTHREADS=1 -sFETCH=1
+ -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency -sALLOW_MEMORY_GROWTH=1
+ --preload-file=../otclientrc.lua@otclientrc.lua --preload-file=../init.lua@init.lua
+ --preload-file=../data@data --preload-file=../mods@mods --preload-file=../modules@modules
+ --shell-file=../browser/shell.html --use-preload-cache")
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ LINK_FLAGS ${linkflags})
+ set(CMAKE_EXECUTABLE_SUFFIX ".html")
+ set(VCPKG_TARGET_TRIPLET "wasm32-emscripten" CACHE STRING "")
+
else() # Linux
target_include_directories(${PROJECT_NAME}
PRIVATE
diff --git a/src/client/animatedtext.cpp b/src/client/animatedtext.cpp
index 27b394c932..d5632f776d 100644
--- a/src/client/animatedtext.cpp
+++ b/src/client/animatedtext.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,11 +21,11 @@
*/
#include "animatedtext.h"
-#include
-#include
#include "game.h"
-#include "map.h"
#include "gameconfig.h"
+#include "map.h"
+#include
+#include
AnimatedText::AnimatedText()
{
diff --git a/src/client/animatedtext.h b/src/client/animatedtext.h
index b6ff5af8d2..4ae90aa41c 100644
--- a/src/client/animatedtext.h
+++ b/src/client/animatedtext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,14 @@
#include "declarations.h"
#include
#include
-#include
#include
// @bindclass
-class AnimatedText : public LuaObject
+class AnimatedText final : public LuaObject
{
public:
AnimatedText();
- AnimatedText(const std::string_view text, int color) : AnimatedText() {
+ AnimatedText(const std::string_view text, const int color) : AnimatedText() {
setText(text);
setColor(color);
}
@@ -42,7 +41,7 @@ class AnimatedText : public LuaObject
void onAppear();
- void setColor(int color) { m_color = Color::from8bit(color); }
+ void setColor(const int color) { m_color = Color::from8bit(color); }
void setText(const std::string_view text) { m_cachedText.setText(text); }
void setOffset(const Point& offset) { m_offset = offset; }
diff --git a/src/client/animator.cpp b/src/client/animator.cpp
index 1c7d86be9c..a0363835e3 100644
--- a/src/client/animator.cpp
+++ b/src/client/animator.cpp
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2010-2022 OTClient
+* Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -43,7 +43,7 @@ void Animator::unserializeAppearance(const appearances::SpriteAnimation& animati
assert(m_startPhase >= -1 && m_startPhase < m_animationPhases);
}
-void Animator::unserialize(int animationPhases, const FileStreamPtr& fin)
+void Animator::unserialize(const int animationPhases, const FileStreamPtr& fin)
{
m_animationPhases = animationPhases;
m_async = fin->getU8() == 0;
@@ -77,7 +77,7 @@ void Animator::serialize(const FileStreamPtr& fin) const
}
}
-void Animator::setPhase(int phase)
+void Animator::setPhase(const int phase)
{
if (m_phase == phase)
return;
@@ -133,7 +133,7 @@ int Animator::getPhase()
return m_phase;
}
-int Animator::getPhaseAt(Timer& timer, float durationFactor) const
+int Animator::getPhaseAt(Timer& timer, const float durationFactor) const
{
const ticks_t time = timer.ticksElapsed();
@@ -195,7 +195,7 @@ int Animator::getLoopPhase()
return m_phase;
}
-int Animator::getPhaseDuration(int phase) const
+int Animator::getPhaseDuration(const int phase) const
{
assert(phase < static_cast(m_phaseDurations.size()));
diff --git a/src/client/animator.h b/src/client/animator.h
index 5a9d3baeec..cdc86b97f6 100644
--- a/src/client/animator.h
+++ b/src/client/animator.h
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2010-2022 OTClient
+* Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/attachableobject.cpp b/src/client/attachableobject.cpp
index 8d796f87cf..7c972925db 100644
--- a/src/client/attachableobject.cpp
+++ b/src/client/attachableobject.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,18 +21,18 @@
*/
#include "attachableobject.h"
-#include
#include
+#include
-#include
-#include
#include
#include
+#include
+
+#include
#include "client.h"
#include "game.h"
#include "map.h"
-#include "tile.h"
#include "uimap.h"
extern ParticleManager g_particles;
@@ -55,7 +55,7 @@ void AttachableObject::attachEffect(const AttachedEffectPtr& obj)
++m_ownerHidden;
if (obj->getDuration() > 0) {
- g_dispatcher.scheduleEvent([self = std::static_pointer_cast(shared_from_this()), effect = obj]() {
+ g_dispatcher.scheduleEvent([self = std::static_pointer_cast(shared_from_this()), effect = obj] {
self->detachEffect(effect);
}, obj->getDuration());
}
@@ -95,7 +95,7 @@ bool AttachableObject::detachEffectById(uint16_t id)
return true;
}
-void AttachableObject::onDetachEffect(const AttachedEffectPtr& effect, bool callEvent)
+void AttachableObject::onDetachEffect(const AttachedEffectPtr& effect, const bool callEvent)
{
if (effect->isHidedOwner())
--m_ownerHidden;
@@ -106,7 +106,7 @@ void AttachableObject::onDetachEffect(const AttachedEffectPtr& effect, bool call
effect->callLuaField("onDetach", attachedObjectToLuaObject());
}
-void AttachableObject::clearAttachedEffects(bool ignoreLuaEvent)
+void AttachableObject::clearAttachedEffects(const bool ignoreLuaEvent)
{
if (!hasAttachedEffects()) return;
for (const auto& e : m_data->attachedEffects)
@@ -117,27 +117,27 @@ void AttachableObject::clearAttachedEffects(bool ignoreLuaEvent)
void AttachableObject::clearTemporaryAttachedEffects()
{
if (!hasAttachedEffects()) return;
- m_data->attachedEffects.erase(std::remove_if(m_data->attachedEffects.begin(), m_data->attachedEffects.end(),
- [this](const AttachedEffectPtr& obj) {
+ std::erase_if(m_data->attachedEffects,
+ [this](const AttachedEffectPtr& obj) {
if (!obj->isPermanent()) {
onDetachEffect(obj);
return true;
}
return false;
- }), m_data->attachedEffects.end());
+ });
}
void AttachableObject::clearPermanentAttachedEffects()
{
if (!hasAttachedEffects()) return;
- m_data->attachedEffects.erase(std::remove_if(m_data->attachedEffects.begin(), m_data->attachedEffects.end(),
- [this](const AttachedEffectPtr& obj) {
+ std::erase_if(m_data->attachedEffects,
+ [this](const AttachedEffectPtr& obj) {
if (obj->isPermanent()) {
onDetachEffect(obj);
return true;
}
return false;
- }), m_data->attachedEffects.end());
+ });
}
AttachedEffectPtr AttachableObject::getAttachedEffectById(uint16_t id)
@@ -152,13 +152,13 @@ AttachedEffectPtr AttachableObject::getAttachedEffectById(uint16_t id)
return *it;
}
-void AttachableObject::drawAttachedEffect(const Point& dest, const LightViewPtr& lightView, bool isOnTop)
+void AttachableObject::drawAttachedEffect(const Point& dest, const LightViewPtr& lightView, const bool isOnTop)
{
if (!hasAttachedEffects()) return;
for (const auto& effect : m_data->attachedEffects) {
effect->draw(dest, isOnTop, lightView);
if (effect->getLoop() == 0) {
- g_dispatcher.addEvent([self = std::static_pointer_cast(shared_from_this()), effect]() {
+ g_dispatcher.addEvent([self = std::static_pointer_cast(shared_from_this()), effect] {
self->detachEffect(effect);
});
}
@@ -228,7 +228,7 @@ void AttachableObject::updateAndAttachParticlesEffects(std::vector&
toRemove.reserve(m_data->attachedParticles.size());
for (const auto& effect : m_data->attachedParticles) {
- auto findPos = std::find(newElements.begin(), newElements.end(), effect->getEffectType()->getName());
+ auto findPos = std::ranges::find(newElements, effect->getEffectType()->getName());
if (findPos == newElements.end())
toRemove.emplace_back(effect->getEffectType()->getName());
else
@@ -262,7 +262,7 @@ void AttachableObject::attachWidget(const UIWidgetPtr& widget) {
getData()->attachedWidgets.emplace_back(widget);
g_map.addAttachedWidgetToObject(widget, std::static_pointer_cast(shared_from_this()));
widget->callLuaField("onAttached", asLuaObject());
- widget->addOnDestroyCallback("attached-widget-destroy", [this, widget]() {
+ widget->addOnDestroyCallback("attached-widget-destroy", [this, widget] {
detachWidget(widget);
});
}
@@ -301,12 +301,12 @@ bool AttachableObject::detachWidget(const UIWidgetPtr widget)
return true;
}
-void AttachableObject::clearAttachedWidgets(bool callEvent)
+void AttachableObject::clearAttachedWidgets(const bool callEvent)
{
if (!hasAttachedWidgets()) return;
// keep the same behavior as detachWidget
- auto oldList = std::move(m_data->attachedWidgets);
+ const auto oldList = std::move(m_data->attachedWidgets);
m_data->attachedWidgets.clear();
for (const auto& widget : oldList) {
diff --git a/src/client/attachableobject.h b/src/client/attachableobject.h
index 35c880ddc0..185ec03562 100644
--- a/src/client/attachableobject.h
+++ b/src/client/attachableobject.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -31,7 +31,7 @@ class AttachableObject : public LuaObject
{
public:
AttachableObject() = default;
- virtual ~AttachableObject();
+ ~AttachableObject() override;
virtual LuaObjectPtr attachedObjectToLuaObject() = 0;
virtual bool isTile() { return false; }
@@ -45,9 +45,9 @@ class AttachableObject : public LuaObject
bool detachEffect(const AttachedEffectPtr& obj);
AttachedEffectPtr getAttachedEffectById(uint16_t id);
- virtual void onStartAttachEffect(const AttachedEffectPtr& /*effect*/) { };
- virtual void onDispatcherAttachEffect(const AttachedEffectPtr& /*effect*/) { };
- virtual void onStartDetachEffect(const AttachedEffectPtr& /*effect*/) { };
+ virtual void onStartAttachEffect(const AttachedEffectPtr& /*effect*/) {};
+ virtual void onDispatcherAttachEffect(const AttachedEffectPtr& /*effect*/) {};
+ virtual void onStartDetachEffect(const AttachedEffectPtr& /*effect*/) {};
bool isOwnerHidden() { return m_ownerHidden > 0; }
@@ -67,7 +67,7 @@ class AttachableObject : public LuaObject
void attachWidget(const UIWidgetPtr& widget);
void clearAttachedWidgets(bool callEvent = true);
bool detachWidgetById(const std::string& id);
- bool detachWidget(const UIWidgetPtr widget);
+ bool detachWidget(UIWidgetPtr widget);
UIWidgetPtr getAttachedWidgetById(const std::string& id);
protected:
@@ -84,7 +84,7 @@ class AttachableObject : public LuaObject
void onDetachEffect(const AttachedEffectPtr& effect, bool callEvent = true);
void drawAttachedParticlesEffect(const Point& dest);
- inline auto getData() {
+ auto getData() {
if (!m_data)
m_data = std::make_shared();
return m_data;
diff --git a/src/client/attachedeffect.cpp b/src/client/attachedeffect.cpp
index ca65cab747..f39e709962 100644
--- a/src/client/attachedeffect.cpp
+++ b/src/client/attachedeffect.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,11 +25,11 @@
#include "lightview.h"
#include
-#include
#include
#include
+#include
-AttachedEffectPtr AttachedEffect::create(uint16_t thingId, ThingCategory category) {
+AttachedEffectPtr AttachedEffect::create(const uint16_t thingId, const ThingCategory category) {
if (!g_things.isValidDatId(thingId, category)) {
g_logger.error(stdext::format("AttachedEffectManager::getInstance(%d, %d): invalid thing with id or category.", thingId, static_cast(category)));
return nullptr;
@@ -71,7 +71,7 @@ int getBounce(const AttachedEffect::Bounce bounce, const ticks_t ticks) {
return minHeight + (height - std::abs(height - static_cast(ticks / (bounce.speed / 100.f)) % static_cast(height * 2)));
}
-void AttachedEffect::draw(const Point& dest, bool isOnTop, const LightViewPtr& lightView, const bool drawThing) {
+void AttachedEffect::draw(const Point& dest, const bool isOnTop, const LightViewPtr& lightView, const bool drawThing) {
if (m_transform)
return;
diff --git a/src/client/attachedeffect.h b/src/client/attachedeffect.h
index a2066d1763..d28035211d 100644
--- a/src/client/attachedeffect.h
+++ b/src/client/attachedeffect.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,15 @@
#pragma once
-#include "thingtype.h"
#include "outfit.h"
+#include "thingtype.h"
-class AttachedEffect : public LuaObject
+class AttachedEffect final : public LuaObject
{
public:
static AttachedEffectPtr create(uint16_t thingId, ThingCategory category);
- void draw(const Point& /*dest*/, bool /*isOnTop*/, const LightViewPtr & = nullptr, const bool drawThing = true);
+ void draw(const Point& /*dest*/, bool /*isOnTop*/, const LightViewPtr & = nullptr, bool drawThing = true);
void drawLight(const Point& /*dest*/, const LightViewPtr&);
uint16_t getId() { return m_id; }
@@ -38,31 +38,31 @@ class AttachedEffect : public LuaObject
AttachedEffectPtr clone();
float getSpeed() { return m_speed / 100.f; }
- void setSpeed(float speed) { m_speed = speed * 100u; }
+ void setSpeed(const float speed) { m_speed = speed * 100u; }
float getOpacity() { return m_opacity / 100.f; }
- void setOpacity(float opacity) { m_opacity = opacity * 100u; }
+ void setOpacity(const float opacity) { m_opacity = opacity * 100u; }
Size getSize() { return m_size; }
void setSize(const Size& s) { m_size = s; }
bool isHidedOwner() { return m_hideOwner; }
- void setHideOwner(bool v) { m_hideOwner = v; }
+ void setHideOwner(const bool v) { m_hideOwner = v; }
bool isTransform() { return m_transform; }
- void setTransform(bool v) { m_transform = v; }
+ void setTransform(const bool v) { m_transform = v; }
bool isDisabledWalkAnimation() { return m_disableWalkAnimation; }
- void setDisableWalkAnimation(bool v) { m_disableWalkAnimation = v; }
+ void setDisableWalkAnimation(const bool v) { m_disableWalkAnimation = v; }
bool isPermanent() { return m_permanent; }
- void setPermanent(bool permanent) { m_permanent = permanent; }
+ void setPermanent(const bool permanent) { m_permanent = permanent; }
uint16_t getDuration() { return m_duration; }
- void setDuration(uint16_t v) { m_duration = v; }
+ void setDuration(const uint16_t v) { m_duration = v; }
int8_t getLoop() { return m_loop; }
- void setLoop(int8_t v) { m_loop = v; }
+ void setLoop(const int8_t v) { m_loop = v; }
void setName(std::string_view n) { m_name = { n.data() }; }
std::string getName() { return m_name; }
@@ -70,17 +70,37 @@ class AttachedEffect : public LuaObject
Otc::Direction getDirection() { return m_direction; }
void setDirection(const Otc::Direction dir) { m_direction = std::min(dir, Otc::NorthWest); }
- void setBounce(uint8_t minHeight, uint8_t height, uint16_t speed) { m_bounce = { minHeight, height , speed }; }
- void setPulse(uint8_t minHeight, uint8_t height, uint16_t speed) { m_pulse = { minHeight, height , speed }; }
- void setFade(uint8_t start, uint8_t end, uint16_t speed) { m_fade = { start, end , speed }; }
-
- void setOnTop(bool onTop) { for (auto& control : m_offsetDirections) control.onTop = onTop; }
+ void setBounce(const uint8_t minHeight, const uint8_t height, const uint16_t speed) {
+ m_bounce = { .minHeight =
+minHeight,
+.height = height, .speed = speed
+ };
+ }
+ void setPulse(const uint8_t minHeight, const uint8_t height, const uint16_t speed) {
+ m_pulse = { .minHeight =
+minHeight,
+.height = height, .speed = speed
+ };
+ }
+ void setFade(const uint8_t start, const uint8_t end, const uint16_t speed) {
+ m_fade = { .minHeight = start, .height =
+end,
+.speed = speed
+ };
+ }
+
+ void setOnTop(const bool onTop) { for (auto& control : m_offsetDirections) control.onTop = onTop; }
void setOffset(int16_t x, int16_t y) { for (auto& control : m_offsetDirections) control.offset = { x, y }; }
- void setOnTopByDir(Otc::Direction direction, bool onTop) { m_offsetDirections[direction].onTop = onTop; }
-
- void setDirOffset(Otc::Direction direction, int8_t x, int8_t y, bool onTop = false) { m_offsetDirections[direction] = { onTop, {x, y} }; }
- void setShader(const std::string_view name);
- void setCanDrawOnUI(bool canDraw) { m_canDrawOnUI = canDraw; }
+ void setOnTopByDir(const Otc::Direction direction, const bool onTop) { m_offsetDirections[direction].onTop = onTop; }
+
+ void setDirOffset(const Otc::Direction direction, int8_t x, int8_t y, const bool onTop = false) {
+ m_offsetDirections[direction] = { .onTop =
+onTop,
+.offset = {x, y}
+ };
+ }
+ void setShader(std::string_view name);
+ void setCanDrawOnUI(const bool canDraw) { m_canDrawOnUI = canDraw; }
bool canDrawOnUI() { return m_canDrawOnUI; }
void move(const Position& fromPosition, const Position& toPosition);
@@ -88,7 +108,7 @@ class AttachedEffect : public LuaObject
void attachEffect(const AttachedEffectPtr& e) { m_effects.emplace_back(e); }
DrawOrder getDrawOrder() { return m_drawOrder; }
- void setDrawOrder(DrawOrder drawOrder) { m_drawOrder = drawOrder; }
+ void setDrawOrder(const DrawOrder drawOrder) { m_drawOrder = drawOrder; }
const Light& getLight() const { return m_light; }
void setLight(const Light& light) { m_light = light; }
@@ -99,7 +119,7 @@ class AttachedEffect : public LuaObject
uint8_t minHeight{ 0 };
uint8_t height{ 0 };
uint16_t speed{ 0 };
- Timer timer;
+ Timer timer{};
};
private:
@@ -116,7 +136,7 @@ class AttachedEffect : public LuaObject
uint8_t m_speed{ 100 };
uint8_t m_opacity{ 100 };
uint8_t m_lastAnimation{ 0 };
- DrawOrder m_drawOrder{ DrawOrder::FIRST };
+ DrawOrder m_drawOrder{ FIRST };
uint16_t m_id{ 0 };
uint16_t m_duration{ 0 };
diff --git a/src/client/attachedeffectmanager.cpp b/src/client/attachedeffectmanager.cpp
index 3449e563e2..af88cc16e1 100644
--- a/src/client/attachedeffectmanager.cpp
+++ b/src/client/attachedeffectmanager.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,19 +23,18 @@
#include "attachedeffectmanager.h"
#include "attachedeffect.h"
#include "thingtypemanager.h"
-#include "spritemanager.h"
#include
AttachedEffectManager g_attachedEffects;
-AttachedEffectPtr AttachedEffectManager::getById(uint16_t id) {
+AttachedEffectPtr AttachedEffectManager::getById(const uint16_t id) {
const auto it = m_effects.find(id);
if (it == m_effects.end()) {
g_logger.error(stdext::format("AttachedEffectManager::getById(%d): not found.", id));
return nullptr;
}
- const auto& obj = (*it).second;
+ const auto& obj = it->second;
if (obj->m_thingId > 0 && !g_things.isValidDatId(obj->m_thingId, obj->m_thingCategory)) {
g_logger.error(stdext::format("AttachedEffectManager::getById(%d): invalid thing with id %d.", id, obj->m_thingId));
return nullptr;
@@ -44,7 +43,7 @@ AttachedEffectPtr AttachedEffectManager::getById(uint16_t id) {
return obj->clone();
}
-AttachedEffectPtr AttachedEffectManager::registerByThing(uint16_t id, const std::string_view name, uint16_t thingId, ThingCategory category) {
+AttachedEffectPtr AttachedEffectManager::registerByThing(uint16_t id, const std::string_view name, const uint16_t thingId, const ThingCategory category) {
const auto it = m_effects.find(id);
if (it != m_effects.end()) {
g_logger.error(stdext::format("AttachedEffectManager::registerByThing(%d, %s): has already been registered.", id, name));
@@ -62,7 +61,7 @@ AttachedEffectPtr AttachedEffectManager::registerByThing(uint16_t id, const std:
return obj;
}
-AttachedEffectPtr AttachedEffectManager::registerByImage(uint16_t id, const std::string_view name, const std::string_view path, bool smooth) {
+AttachedEffectPtr AttachedEffectManager::registerByImage(uint16_t id, const std::string_view name, const std::string_view path, const bool smooth) {
const auto it = m_effects.find(id);
if (it != m_effects.end()) {
g_logger.error(stdext::format("AttachedEffectManager::registerByImage(%d, %s): has already been registered.", id, name));
diff --git a/src/client/attachedeffectmanager.h b/src/client/attachedeffectmanager.h
index 000d8a81b5..b910d3c80e 100644
--- a/src/client/attachedeffectmanager.h
+++ b/src/client/attachedeffectmanager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,11 +28,11 @@
class AttachedEffectManager
{
public:
- AttachedEffectPtr registerByThing(uint16_t id, const std::string_view name, uint16_t thingId, ThingCategory category);
- AttachedEffectPtr registerByImage(uint16_t id, const std::string_view name, const std::string_view path, bool smooth);
+ AttachedEffectPtr registerByThing(uint16_t id, std::string_view name, uint16_t thingId, ThingCategory category);
+ AttachedEffectPtr registerByImage(uint16_t id, std::string_view name, std::string_view path, bool smooth);
AttachedEffectPtr getById(uint16_t id);
- void remove(uint16_t id) { m_effects.erase(id); }
+ void remove(const uint16_t id) { m_effects.erase(id); }
void clear() { m_effects.clear(); }
private:
diff --git a/src/client/client.cpp b/src/client/client.cpp
index 20b6776ca8..2503240b3d 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,19 +22,17 @@
#include "client.h"
#include "game.h"
+#include "gameconfig.h"
#include "map.h"
-#include "uimap.h"
#include "minimap.h"
#include "spriteappearances.h"
#include "spritemanager.h"
-#include "gameconfig.h"
+#include "uimap.h"
-#include
-#include
#include
-#include
+#include
#include
-#include
+#include
Client g_client;
@@ -77,7 +75,7 @@ void Client::preLoad() {
}
}
-void Client::draw(DrawPoolType type)
+void Client::draw(const DrawPoolType type)
{
if (!g_game.isOnline()) {
m_mapWidget = nullptr;
@@ -96,7 +94,7 @@ void Client::draw(DrawPoolType type)
m_mapWidget->draw(type);
}
-bool Client::canDraw(DrawPoolType type) const
+bool Client::canDraw(const DrawPoolType type) const
{
switch (type) {
case DrawPoolType::FOREGROUND:
diff --git a/src/client/client.h b/src/client/client.h
index 210aafb73d..bdfd0cf20b 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,6 @@
#include "uimap.h"
-#include
#include
class Client : public ApplicationDrawEvents
@@ -49,10 +48,10 @@ class Client : public ApplicationDrawEvents
UIMapPtr getMapWidget() { return m_mapWidget; }
float getEffectAlpha() const { return m_effectAlpha; }
- void setEffectAlpha(float v) { m_effectAlpha = v; }
+ void setEffectAlpha(const float v) { m_effectAlpha = v; }
float getMissileAlpha() const { return m_missileAlpha; }
- void setMissileAlpha(float v) { m_missileAlpha = v; }
+ void setMissileAlpha(const float v) { m_missileAlpha = v; }
private:
UIMapPtr m_mapWidget;
diff --git a/src/client/const.h b/src/client/const.h
index 66a82f1821..7fce38ee29 100644
--- a/src/client/const.h
+++ b/src/client/const.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -761,8 +761,8 @@ namespace Otc
INSPECT_NPCTRADE = 1,
INSPECT_PLAYERTRADE = 2,
INSPECT_CYCLOPEDIA = 3
- };
-
+ };
+
enum GameStoreInfoType_t : uint8_t
{
SHOW_NONE = 0,
diff --git a/src/client/container.cpp b/src/client/container.cpp
index 7295816690..31fc994de4 100644
--- a/src/client/container.cpp
+++ b/src/client/container.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,7 +23,7 @@
#include "container.h"
#include "item.h"
-ItemPtr Container::getItem(int slot)
+ItemPtr Container::getItem(const int slot)
{
if (slot < 0 || slot >= static_cast(m_items.size()))
return nullptr;
@@ -62,7 +62,7 @@ void Container::onAddItem(const ItemPtr& item, int slot)
callLuaField("onAddItem", slot, item);
}
-ItemPtr Container::findItemById(uint32_t itemId, int subType) const
+ItemPtr Container::findItemById(const uint32_t itemId, const int subType) const
{
for (const auto& item : m_items)
if (item->getId() == itemId && (subType == -1 || item->getSubType() == subType))
diff --git a/src/client/container.h b/src/client/container.h
index d99eb1000d..5bd51e2207 100644
--- a/src/client/container.h
+++ b/src/client/container.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,14 +28,14 @@
#include
// @bindclass
-class Container : public LuaObject
+class Container final : public LuaObject
{
public:
ItemPtr getItem(int slot);
std::deque getItems() { return m_items; }
int getItemsCount() { return m_items.size(); }
- Position getSlotPosition(int slot) { return { 0xffff, m_id | 0x40, static_cast(slot) }; }
+ Position getSlotPosition(const int slot) { return { 0xffff, m_id | 0x40, static_cast(slot) }; }
int getId() { return m_id; }
int getCapacity() { return m_capacity; }
ItemPtr getContainerItem() { return m_containerItem; }
@@ -49,9 +49,10 @@ class Container : public LuaObject
ItemPtr findItemById(uint32_t itemId, int subType) const;
protected:
- Container(uint8_t id, uint8_t capacity, const std::string_view name, const ItemPtr& containerItem, bool hasParent, bool isUnlocked, bool hasPages, uint16_t containerSize, uint16_t firstIndex)
- :m_id(id), m_capacity(capacity), m_containerItem(containerItem), m_name(name), m_hasParent(hasParent), m_unlocked(isUnlocked), m_hasPages(hasPages), m_size(containerSize), m_firstIndex(firstIndex)
- {}
+ Container(const uint8_t id, const uint8_t capacity, const std::string_view name, ItemPtr containerItem, const bool hasParent, const bool isUnlocked, const bool hasPages, const uint16_t containerSize, const uint16_t firstIndex)
+ :m_id(id), m_capacity(capacity), m_containerItem(std::move(containerItem)), m_name(name), m_hasParent(hasParent), m_unlocked(isUnlocked), m_hasPages(hasPages), m_size(containerSize), m_firstIndex(firstIndex)
+ {
+ }
void onOpen(const ContainerPtr& previousContainer);
void onClose();
diff --git a/src/client/creature.cpp b/src/client/creature.cpp
index c6bc9ae447..404ca37262 100644
--- a/src/client/creature.cpp
+++ b/src/client/creature.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,15 +28,13 @@
#include "map.h"
#include "thingtypemanager.h"
#include "tile.h"
-#include "statictext.h"
#include
#include
#include
#include
-#include
-#include
#include
+#include
#include
double Creature::speedA = 0;
@@ -58,7 +56,7 @@ void Creature::onCreate() {
callLuaField("onCreate");
}
-void Creature::draw(const Point& dest, bool drawThings, const LightViewPtr& /*lightView*/)
+void Creature::draw(const Point& dest, const bool drawThings, const LightViewPtr& /*lightView*/)
{
if (!canBeSeen() || !canDraw())
return;
@@ -105,7 +103,7 @@ void Creature::drawLight(const Point& dest, const LightViewPtr& lightView) {
drawAttachedLightEffect(dest + m_walkOffset * g_drawPool.getScaleFactor(), lightView);
}
-void Creature::draw(const Rect& destRect, uint8_t size, bool center)
+void Creature::draw(const Rect& destRect, const uint8_t size, const bool center)
{
if (!getThingType())
return;
@@ -127,7 +125,7 @@ void Creature::draw(const Rect& destRect, uint8_t size, bool center)
} g_drawPool.releaseFrameBuffer(destRect);
}
-void Creature::drawInformation(const MapPosInfo& mapRect, const Point& dest, int drawFlags)
+void Creature::drawInformation(const MapPosInfo& mapRect, const Point& dest, const int drawFlags)
{
static const Color
DEFAULT_COLOR(96, 96, 96),
@@ -168,7 +166,7 @@ void Creature::drawInformation(const MapPosInfo& mapRect, const Point& dest, int
const int cropSizeText = g_gameConfig.isAdjustCreatureInformationBasedCropSize() ? getExactSize() : 12;
const int cropSizeBackGround = g_gameConfig.isAdjustCreatureInformationBasedCropSize() ? cropSizeText - nameSize.height() : 0;
- bool isScaled = g_app.getCreatureInformationScale() != PlatformWindow::DEFAULT_DISPLAY_DENSITY;
+ const bool isScaled = g_app.getCreatureInformationScale() != PlatformWindow::DEFAULT_DISPLAY_DENSITY;
if (isScaled) {
p.scale(g_app.getCreatureInformationScale());
}
@@ -255,7 +253,7 @@ void Creature::internalDraw(Point dest, const Color& color)
m_shader->setUniformValue(ShaderManager::OUTFIT_ID_UNIFORM, id);
};*/
- bool replaceColorShader = color != Color::white;
+ const bool replaceColorShader = color != Color::white;
if (replaceColorShader)
g_drawPool.setShaderProgram(g_painter->getReplaceColorShader());
else
@@ -362,7 +360,7 @@ void Creature::internalDraw(Point dest, const Color& color)
}
}
-void Creature::turn(Otc::Direction direction)
+void Creature::turn(const Otc::Direction direction)
{
// schedules to set the new direction when walk ends
if (m_walking) {
@@ -392,9 +390,6 @@ void Creature::walk(const Position& oldPos, const Position& newPos)
m_walkTimer.restart();
m_walkedPixels = 0;
- if (++m_walkSteps == 0)
- m_walkSteps = 1;
-
// no direction need to be changed when the walk ends
m_walkTurnDirection = Otc::InvalidDirection;
@@ -416,7 +411,7 @@ void Creature::stopWalk()
terminateWalk();
}
-void Creature::jump(int height, int duration)
+void Creature::jump(const int height, const int duration)
{
if (!m_jumpOffset.isNull())
return;
@@ -561,7 +556,7 @@ void Creature::updateWalkAnimation()
return;
int minFootDelay = 20;
- int maxFootDelay = footAnimPhases > 2 ? 80 : 205;
+ const int maxFootDelay = footAnimPhases > 2 ? 80 : 205;
int footAnimDelay = footAnimPhases;
if (g_game.getFeature(Otc::GameEnhancedAnimations) && footAnimPhases > 2) {
@@ -581,7 +576,7 @@ void Creature::updateWalkAnimation()
}
}
-void Creature::updateWalkOffset(uint8_t totalPixelsWalked)
+void Creature::updateWalkOffset(const uint8_t totalPixelsWalked)
{
m_walkOffset = {};
if (m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest)
@@ -701,13 +696,12 @@ void Creature::terminateWalk()
const auto self = static_self_cast();
m_walkFinishAnimEvent = g_dispatcher.scheduleEvent([self] {
- self->m_walkSteps = 0;
self->m_walkAnimationPhase = 0;
self->m_walkFinishAnimEvent = nullptr;
}, g_game.getServerBeat());
}
-void Creature::setHealthPercent(uint8_t healthPercent)
+void Creature::setHealthPercent(const uint8_t healthPercent)
{
static const Color
COLOR1(0x00, 0xBC, 0x00),
@@ -741,7 +735,7 @@ void Creature::setHealthPercent(uint8_t healthPercent)
onDeath();
}
-void Creature::setDirection(Otc::Direction direction)
+void Creature::setDirection(const Otc::Direction direction)
{
if (direction == Otc::InvalidDirection)
return;
@@ -817,7 +811,7 @@ void Creature::setSpeed(uint16_t speed)
callLuaField("onSpeedChange", m_speed, oldSpeed);
}
-void Creature::setBaseSpeed(uint16_t baseSpeed)
+void Creature::setBaseSpeed(const uint16_t baseSpeed)
{
if (m_baseSpeed == baseSpeed)
return;
@@ -828,18 +822,18 @@ void Creature::setBaseSpeed(uint16_t baseSpeed)
callLuaField("onBaseSpeedChange", baseSpeed, oldBaseSpeed);
}
-void Creature::setType(uint8_t v) { if (m_type != v) callLuaField("onTypeChange", m_type = v); }
-void Creature::setIcon(uint8_t v) { if (m_icon != v) callLuaField("onIconChange", m_icon = v); }
-void Creature::setSkull(uint8_t v) { if (m_skull != v) callLuaField("onSkullChange", m_skull = v); }
-void Creature::setShield(uint8_t v) { if (m_shield != v) callLuaField("onShieldChange", m_shield = v); }
-void Creature::setEmblem(uint8_t v) { if (m_emblem != v) callLuaField("onEmblemChange", m_emblem = v); }
+void Creature::setType(const uint8_t v) { if (m_type != v) callLuaField("onTypeChange", m_type = v); }
+void Creature::setIcon(const uint8_t v) { if (m_icon != v) callLuaField("onIconChange", m_icon = v); }
+void Creature::setSkull(const uint8_t v) { if (m_skull != v) callLuaField("onSkullChange", m_skull = v); }
+void Creature::setShield(const uint8_t v) { if (m_shield != v) callLuaField("onShieldChange", m_shield = v); }
+void Creature::setEmblem(const uint8_t v) { if (m_emblem != v) callLuaField("onEmblemChange", m_emblem = v); }
void Creature::setTypeTexture(const std::string& filename) { m_typeTexture = g_textures.getTexture(filename); }
void Creature::setIconTexture(const std::string& filename) { m_iconTexture = g_textures.getTexture(filename); }
void Creature::setSkullTexture(const std::string& filename) { m_skullTexture = g_textures.getTexture(filename); }
void Creature::setEmblemTexture(const std::string& filename) { m_emblemTexture = g_textures.getTexture(filename); }
-void Creature::setShieldTexture(const std::string& filename, bool blink)
+void Creature::setShieldTexture(const std::string& filename, const bool blink)
{
m_shieldTexture = g_textures.getTexture(filename);
m_showShieldTexture = true;
@@ -854,7 +848,7 @@ void Creature::setShieldTexture(const std::string& filename, bool blink)
m_shieldBlink = blink;
}
-void Creature::addTimedSquare(uint8_t color)
+void Creature::addTimedSquare(const uint8_t color)
{
m_showTimedSquare = true;
m_timedSquareColor = Color::from8bit(color != 0 ? color : 1);
@@ -879,7 +873,7 @@ void Creature::updateShield()
m_showShieldTexture = true;
}
-int getSmoothedElevation(const Creature* creature, int currentElevation, float factor) {
+int getSmoothedElevation(const Creature* creature, const int currentElevation, const float factor) {
const auto& fromPos = creature->getLastStepFromPosition();
const auto& toPos = creature->getLastStepToPosition();
const auto& fromTile = g_map.getTile(fromPos);
@@ -912,7 +906,7 @@ int Creature::getDrawElevation() {
bool Creature::hasSpeedFormula() { return g_game.getFeature(Otc::GameNewSpeedLaw) && speedA != 0 && speedB != 0 && speedC != 0; }
-uint16_t Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir)
+uint16_t Creature::getStepDuration(const bool ignoreDiagonal, const Otc::Direction dir)
{
if (m_speed < 1)
return 0;
@@ -1064,7 +1058,7 @@ void Creature::setTypingIconTexture(const std::string& filename)
m_typingIconTexture = g_textures.getTexture(filename);
}
-void Creature::setTyping(bool typing)
+void Creature::setTyping(const bool typing)
{
m_typing = typing;
}
@@ -1111,7 +1105,7 @@ void Creature::onStartDetachEffect(const AttachedEffectPtr& effect) {
}
}
-void Creature::setStaticWalking(uint16_t v) {
+void Creature::setStaticWalking(const uint16_t v) {
if (m_walkUpdateEvent) {
m_walkUpdateEvent->cancel();
m_walkUpdateEvent = nullptr;
diff --git a/src/client/creature.h b/src/client/creature.h
index 195be8a168..c1feb01661 100644
--- a/src/client/creature.h
+++ b/src/client/creature.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,16 +22,15 @@
#pragma once
-#include
-#include
-#include
#include "mapview.h"
#include "outfit.h"
#include "thing.h"
+#include
+#include
+#include
struct PreyMonster
{
-public:
std::string name;
Outfit outfit;
};
@@ -45,7 +44,7 @@ class Creature : public Thing
static double speedC;
Creature();
- ~Creature();
+ ~Creature() override;
static bool hasSpeedFormula();
@@ -61,9 +60,9 @@ class Creature : public Thing
void internalDraw(Point dest, const Color& color = Color::white);
void drawInformation(const MapPosInfo& mapRect, const Point& dest, int drawFlags);
- void setId(uint32_t id) override { m_id = id; }
- void setMasterId(uint32_t id) { m_masterId = id; }
- void setName(const std::string_view name);
+ void setId(const uint32_t id) override { m_id = id; }
+ void setMasterId(const uint32_t id) { m_masterId = id; }
+ void setName(std::string_view name);
void setHealthPercent(uint8_t healthPercent);
void setDirection(Otc::Direction direction);
void setOutfit(const Outfit& outfit);
@@ -80,8 +79,8 @@ class Creature : public Thing
void setEmblemTexture(const std::string& filename);
void setTypeTexture(const std::string& filename);
void setIconTexture(const std::string& filename);
- void setPassable(bool passable) { m_passable = passable; }
- void setMountShader(const std::string_view name);
+ void setPassable(const bool passable) { m_passable = passable; }
+ void setMountShader(std::string_view name);
void setStaticWalking(uint16_t v);
void onStartAttachEffect(const AttachedEffectPtr& effect) override;
@@ -94,8 +93,6 @@ class Creature : public Thing
void hideStaticSquare() { m_showStaticSquare = false; }
// walk related
- int getWalkSteps() const { return m_walkSteps; }
- void setWalkSteps(uint8_t step) { m_walkSteps = step; }
void turn(Otc::Direction direction);
void jump(int height, int duration);
void allowAppearWalk() { m_allowAppearWalk = true; }
@@ -162,7 +159,7 @@ class Creature : public Thing
void setCovered(bool covered);
bool isDisabledWalkAnimation() { return m_disableWalkAnimation > 0; }
- void setDisableWalkAnimation(bool v) {
+ void setDisableWalkAnimation(const bool v) {
if (v) ++m_disableWalkAnimation; else {
if (m_disableWalkAnimation <= 1) m_disableWalkAnimation = 0;
else --m_disableWalkAnimation;
@@ -173,7 +170,12 @@ class Creature : public Thing
void sendTyping();
bool getTyping() { return m_typing; }
void setTypingIconTexture(const std::string& filename);
- void setBounce(uint8_t minHeight, uint8_t height, uint16_t speed) { m_bounce = { minHeight, height , speed }; }
+ void setBounce(const uint8_t minHeight, const uint8_t height, const uint16_t speed) {
+ m_bounce = { .minHeight =
+minHeight,
+.height = height, .speed = speed
+ };
+ }
void setWidgetInformation(const UIWidgetPtr& info);
UIWidgetPtr getWidgetInformation() { return m_widgetInformation; }
@@ -221,7 +223,7 @@ class Creature : public Thing
uint16_t walkDuration{ 0 };
uint16_t diagonalDuration{ 0 };
- uint16_t getDuration(Otc::Direction dir) const { return Position::isDiagonal(dir) ? diagonalDuration : duration; }
+ uint16_t getDuration(const Otc::Direction dir) const { return Position::isDiagonal(dir) ? diagonalDuration : duration; }
};
UIWidgetPtr m_widgetInformation;
@@ -294,8 +296,6 @@ class Creature : public Thing
uint8_t m_disableWalkAnimation{ 0 };
- uint8_t m_walkSteps{ 0 };
-
// Mount Shader
uint8_t m_mountShaderId{ 0 };
@@ -320,14 +320,14 @@ class Creature : public Thing
};
// @bindclass
-class Npc : public Creature
+class Npc final : public Creature
{
public:
bool isNpc() override { return true; }
};
// @bindclass
-class Monster : public Creature
+class Monster final : public Creature
{
public:
bool isMonster() override { return true; }
diff --git a/src/client/creatures.cpp b/src/client/creatures.cpp
index fbbca0a689..46d2b31532 100644
--- a/src/client/creatures.cpp
+++ b/src/client/creatures.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/creatures.h b/src/client/creatures.h
index b23802dc17..e9aacdae5c 100644
--- a/src/client/creatures.h
+++ b/src/client/creatures.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/declarations.h b/src/client/declarations.h
index 755a029fae..b5061e033f 100644
--- a/src/client/declarations.h
+++ b/src/client/declarations.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,9 @@
#pragma once
+#include "global.h"
#include
#include
-#include "global.h"
// core
class Map;
diff --git a/src/client/effect.cpp b/src/client/effect.cpp
index 54ddcf2154..517f7474bc 100644
--- a/src/client/effect.cpp
+++ b/src/client/effect.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,13 +21,13 @@
*/
#include "effect.h"
-#include
-#include
#include "game.h"
#include "map.h"
#include
+#include
+#include
-void Effect::draw(const Point& dest, bool drawThings, const LightViewPtr& lightView)
+void Effect::draw(const Point& dest, const bool drawThings, const LightViewPtr& lightView)
{
if (!canDraw() || isHided())
return;
@@ -57,11 +57,11 @@ void Effect::draw(const Point& dest, bool drawThings, const LightViewPtr& lightV
const int offsetX = m_position.x - g_map.getCentralPosition().x;
const int offsetY = m_position.y - g_map.getCentralPosition().y;
- int xPattern = unsigned(offsetX) % getNumPatternX();
+ int xPattern = static_cast(offsetX) % getNumPatternX();
xPattern = 1 - xPattern - getNumPatternX();
if (xPattern < 0) xPattern += getNumPatternX();
- int yPattern = unsigned(offsetY) % getNumPatternY();
+ int yPattern = static_cast(offsetY) % getNumPatternY();
if (g_game.getFeature(Otc::GameMapOldEffectRendering)) {
xPattern = offsetX % getNumPatternX();
@@ -76,7 +76,7 @@ void Effect::draw(const Point& dest, bool drawThings, const LightViewPtr& lightV
if (g_drawPool.getCurrentType() == DrawPoolType::MAP) {
if (g_app.isDrawingEffectsOnTop() && !m_drawConductor.agroup) {
m_drawConductor.agroup = true;
- m_drawConductor.order = DrawOrder::FOURTH;
+ m_drawConductor.order = FOURTH;
}
if (drawThings && g_client.getEffectAlpha() < 1.f)
@@ -126,7 +126,7 @@ bool Effect::waitFor(const EffectPtr& effect)
return true;
}
-void Effect::setId(uint32_t id)
+void Effect::setId(const uint32_t id)
{
if (!g_things.isValidDatId(id, ThingCategoryEffect))
return;
@@ -134,7 +134,7 @@ void Effect::setId(uint32_t id)
m_clientId = id;
}
-void Effect::setPosition(const Position& position, uint8_t stackPos, bool hasElevation)
+void Effect::setPosition(const Position& position, const uint8_t stackPos, const bool hasElevation)
{
if (m_clientId == 0)
return;
diff --git a/src/client/effect.h b/src/client/effect.h
index afb1850759..ab991149d3 100644
--- a/src/client/effect.h
+++ b/src/client/effect.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,12 +22,11 @@
#pragma once
-#include
-#include
#include "thing.h"
+#include
- // @bindclass
-class Effect : public Thing
+// @bindclass
+class Effect final : public Thing
{
public:
void draw(const Point& /*dest*/, bool drawThings = true, const LightViewPtr & = nullptr) override;
diff --git a/src/client/game.cpp b/src/client/game.cpp
index 28cad81b75..7f7ac58c48 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,8 +21,6 @@
*/
#include "game.h"
-#include
-#include
#include "container.h"
#include "creature.h"
#include "localplayer.h"
@@ -30,9 +28,11 @@
#include "map.h"
#include "protocolcodes.h"
#include "protocolgame.h"
+#include
+#include
-#include "tile.h"
#include "framework/core/graphicalapplication.h"
+#include "tile.h"
Game g_game;
@@ -384,7 +384,7 @@ void Game::processVipStateChange(const uint32_t id, const uint32_t status)
g_lua.callGlobalField("g_game", "onVipStateChange", id, status, groupID);
}
-void Game::processVipGroupChange(const std::vector>& vipGroups, uint8_t groupsAmountLeft)
+void Game::processVipGroupChange(const std::vector>& vipGroups, const uint8_t groupsAmountLeft)
{
g_lua.callGlobalField("g_game", "onVipGroupChange", vipGroups, groupsAmountLeft);
}
@@ -478,7 +478,7 @@ void Game::processQuestLog(const std::vector>& questMissions)
+void Game::processQuestLine(const uint16_t questId, const std::vector>& questMissions)
{
g_lua.callGlobalField("g_game", "onQuestLine", questId, questMissions);
}
@@ -525,7 +525,7 @@ void Game::processCyclopediaCharacterItemSummary(const CyclopediaCharacterItemSu
}
void Game::processCyclopediaCharacterAppearances(const OutfitColorStruct& currentOutfit, const std::vector& outfits,
- const std::vector& mounts, std::vector& familiars)
+ const std::vector& mounts, const std::vector& familiars)
{
g_lua.callGlobalField("g_game", "onParseCyclopediaCharacterAppearances", currentOutfit, outfits, mounts, familiars);
}
@@ -581,7 +581,7 @@ void Game::processWalkCancel(const Otc::Direction direction)
m_localPlayer->cancelWalk(direction);
}
-void Game::loginWorld(const std::string_view account, const std::string_view password, const std::string_view worldName, const std::string_view worldHost, int worldPort, const std::string_view characterName, const std::string_view authenticatorToken, const std::string_view sessionKey)
+void Game::loginWorld(const std::string_view account, const std::string_view password, const std::string_view worldName, const std::string_view worldHost, const int worldPort, const std::string_view characterName, const std::string_view authenticatorToken, const std::string_view sessionKey)
{
if (m_protocolGame || isOnline())
throw Exception("Unable to login into a world while already online or logging.");
@@ -630,19 +630,9 @@ void Game::safeLogout()
m_protocolGame->sendLogout();
}
-bool Game::walk(const Otc::Direction direction, bool force)
+bool Game::walk(const Otc::Direction direction)
{
- static ScheduledEventPtr nextWalkSchedule = nullptr;
-
- if (direction == Otc::InvalidDirection) {
- if (nextWalkSchedule) {
- nextWalkSchedule->cancel();
- nextWalkSchedule = nullptr;
- }
- return false;
- }
-
- if (!canPerformGameAction())
+ if (!canPerformGameAction() || direction == Otc::InvalidDirection)
return false;
// must cancel auto walking, and wait next try
@@ -652,39 +642,32 @@ bool Game::walk(const Otc::Direction direction, bool force)
return false;
}
- if (!force) {
- if (nextWalkSchedule)
- return false;
+ static ScheduledEventPtr nextWalkSchedule = nullptr;
+ static uint16_t steps = 0;
+ static Timer timer;
- if (m_localPlayer->getWalkSteps() > 0) {
- uint16_t delay = 0;
- if (m_localPlayer->getWalkSteps() == 1) {
- if (m_localPlayer->isWalking())
- return false;
-
- delay = m_walkFirstStepDelay;
- } else if (direction != m_localPlayer->getDirection())
- delay = m_walkTurnDelay;
-
- if (delay > 0) {
- nextWalkSchedule = g_dispatcher.scheduleEvent([this, direction] {
- if (m_localPlayer) {
- m_localPlayer->setWalkSteps(1);
- walk(direction, true);
- }
-
- nextWalkSchedule = nullptr;
- }, delay);
- return false;
- }
- }
- }
+ if (nextWalkSchedule) nextWalkSchedule->cancel();
+ nextWalkSchedule = g_dispatcher.scheduleEvent([this] {
+ nextWalkSchedule = nullptr;
+ steps = 0;
+ }, 150);
// check we can walk and add new walk event if false
if (!m_localPlayer->canWalk(direction)) {
return false;
}
+ if (steps == 1) {
+ if (timer.ticksElapsed() <= m_walkFirstStepDelay)
+ return false;
+ } else if (direction != m_localPlayer->getDirection()) {
+ if (timer.ticksElapsed() <= m_walkTurnDelay)
+ return false;
+ }
+
+ ++steps;
+ timer.restart();
+
const auto& toPos = m_localPlayer->getPosition().translatedToDirection(direction);
// only do prewalks to walkable tiles (like grounds and not walls)
diff --git a/src/client/game.h b/src/client/game.h
index ae4c25b231..64ba1ee92a 100644
--- a/src/client/game.h
+++ b/src/client/game.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +22,13 @@
#pragma once
-#include
-#include
-#include "animatedtext.h"
#include "container.h"
#include "creature.h"
#include "declarations.h"
-#include "item.h"
#include "outfit.h"
#include "protocolgame.h"
+#include
+#include
struct UnjustifiedPoints
{
@@ -419,60 +417,60 @@ class Game
void processPing();
void processPingBack();
- static void processUpdateNeeded(const std::string_view signature);
- static void processLoginError(const std::string_view error);
- static void processLoginAdvice(const std::string_view message);
- static void processLoginWait(const std::string_view message, const uint8_t time);
- static void processSessionEnd(const uint8_t reason);
+ static void processUpdateNeeded(std::string_view signature);
+ static void processLoginError(std::string_view error);
+ static void processLoginAdvice(std::string_view message);
+ static void processLoginWait(std::string_view message, uint8_t time);
+ static void processSessionEnd(uint8_t reason);
static void processLogin();
void processPendingGame();
void processEnterGame();
void processGameStart();
void processGameEnd();
- void processDeath(const uint8_t deathType, const uint8_t penality);
+ void processDeath(uint8_t deathType, uint8_t penality);
void processGMActions(const std::vector& actions);
- void processInventoryChange(const uint8_t slot, const ItemPtr& item);
- void processAttackCancel(const uint32_t seq);
- void processWalkCancel(const Otc::Direction direction);
+ void processInventoryChange(uint8_t slot, const ItemPtr& item);
+ void processAttackCancel(uint32_t seq);
+ void processWalkCancel(Otc::Direction direction);
- static void processPlayerHelpers(const uint16_t helpers);
- void processPlayerModes(const Otc::FightModes fightMode, const Otc::ChaseModes chaseMode, const bool safeMode, const Otc::PVPModes pvpMode);
+ static void processPlayerHelpers(uint16_t helpers);
+ void processPlayerModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeMode, Otc::PVPModes pvpMode);
// message related
- static void processTextMessage(const Otc::MessageMode mode, const std::string_view text);
- static void processTalk(const std::string_view name, const uint16_t level, const Otc::MessageMode mode, const std::string_view text, const uint16_t channelId, const Position& pos);
+ static void processTextMessage(Otc::MessageMode mode, std::string_view text);
+ static void processTalk(std::string_view name, uint16_t level, Otc::MessageMode mode, std::string_view text, uint16_t channelId, const Position& pos);
// container related
- void processOpenContainer(const uint8_t containerId, const ItemPtr& containerItem, const std::string_view name, const uint8_t capacity, const bool hasParent, const std::vector& items, const bool isUnlocked, const bool hasPages, const uint16_t containerSize, const uint16_t firstIndex);
- void processCloseContainer(const uint8_t containerId);
- void processContainerAddItem(const uint8_t containerId, const ItemPtr& item, const uint16_t slot);
- void processContainerUpdateItem(const uint8_t containerId, const uint16_t slot, const ItemPtr& item);
- void processContainerRemoveItem(const uint8_t containerId, const uint16_t slot, const ItemPtr& lastItem);
+ void processOpenContainer(uint8_t containerId, const ItemPtr& containerItem, std::string_view name, uint8_t capacity, bool hasParent, const std::vector& items, bool isUnlocked, bool hasPages, uint16_t containerSize, uint16_t firstIndex);
+ void processCloseContainer(uint8_t containerId);
+ void processContainerAddItem(uint8_t containerId, const ItemPtr& item, uint16_t slot);
+ void processContainerUpdateItem(uint8_t containerId, uint16_t slot, const ItemPtr& item);
+ void processContainerRemoveItem(uint8_t containerId, uint16_t slot, const ItemPtr& lastItem);
// channel related
static void processChannelList(const std::vector>& channelList);
- static void processOpenChannel(const uint16_t channelId, const std::string_view name);
- static void processOpenPrivateChannel(const std::string_view name);
- static void processOpenOwnPrivateChannel(const uint16_t channelId, const std::string_view name);
- static void processCloseChannel(const uint16_t channelId);
+ static void processOpenChannel(uint16_t channelId, std::string_view name);
+ static void processOpenPrivateChannel(std::string_view name);
+ static void processOpenOwnPrivateChannel(uint16_t channelId, std::string_view name);
+ static void processCloseChannel(uint16_t channelId);
// rule violations
- static void processRuleViolationChannel(const uint16_t channelId);
- static void processRuleViolationRemove(const std::string_view name);
- static void processRuleViolationCancel(const std::string_view name);
+ static void processRuleViolationChannel(uint16_t channelId);
+ static void processRuleViolationRemove(std::string_view name);
+ static void processRuleViolationCancel(std::string_view name);
static void processRuleViolationLock();
// vip related
- void processVipAdd(const uint32_t id, const std::string_view name, const uint32_t status, const std::string_view description, const uint32_t iconId, const bool notifyLogin, const std::vector& groupID);
- void processVipStateChange(const uint32_t id, const uint32_t status);
+ void processVipAdd(uint32_t id, std::string_view name, uint32_t status, std::string_view description, uint32_t iconId, bool notifyLogin, const std::vector& groupID);
+ void processVipStateChange(uint32_t id, uint32_t status);
void processVipGroupChange(const std::vector>& vipGroups, uint8_t groupsAmountLeft);
// tutorial hint
- static void processTutorialHint(const uint8_t id);
- static void processAddAutomapFlag(const Position& pos, const uint8_t icon, const std::string_view message);
- static void processRemoveAutomapFlag(const Position& pos, const uint8_t icon, const std::string_view message);
+ static void processTutorialHint(uint8_t id);
+ static void processAddAutomapFlag(const Position& pos, uint8_t icon, std::string_view message);
+ static void processRemoveAutomapFlag(const Position& pos, uint8_t icon, std::string_view message);
// outfit
void processOpenOutfitWindow(const Outfit& currentOutfit, const std::vector>& outfitList,
@@ -484,47 +482,47 @@ class Game
// npc trade
static void processOpenNpcTrade(const std::vector>& items);
- static void processPlayerGoods(const uint64_t money, const std::vector>& goods);
+ static void processPlayerGoods(uint64_t money, const std::vector>& goods);
static void processCloseNpcTrade();
// player trade
- static void processOwnTrade(const std::string_view name, const std::vector& items);
- static void processCounterTrade(const std::string_view name, const std::vector& items);
+ static void processOwnTrade(std::string_view name, const std::vector& items);
+ static void processCounterTrade(std::string_view name, const std::vector& items);
static void processCloseTrade();
// edit text/list
- static void processEditText(const uint32_t id, const uint32_t itemId, const uint16_t maxLength, const std::string_view text, const std::string_view writer, const std::string_view date);
- static void processEditList(const uint32_t id, const uint8_t doorId, const std::string_view text);
+ static void processEditText(uint32_t id, uint32_t itemId, uint16_t maxLength, std::string_view text, std::string_view writer, std::string_view date);
+ static void processEditList(uint32_t id, uint8_t doorId, std::string_view text);
// questlog
static void processQuestLog(const std::vector>& questList);
- static void processQuestLine(const uint16_t questId, const std::vector>& questMissions);
+ static void processQuestLine(uint16_t questId, const std::vector>& questMissions);
// modal dialogs >= 970
- static void processModalDialog(const uint32_t id, const std::string_view title, const std::string_view message, const std::vector>
- & buttonList, const uint8_t enterButton, const uint8_t escapeButton, const std::vector>
- & choiceList, const bool priority);
+ static void processModalDialog(uint32_t id, std::string_view title, std::string_view message, const std::vector>
+ & buttonList, uint8_t enterButton, uint8_t escapeButton, const std::vector>
+ & choiceList, bool priority);
// cyclopedia
- static void processItemDetail(const uint32_t itemId, const std::vector>& descriptions);
+ static void processItemDetail(uint32_t itemId, const std::vector>& descriptions);
static void processBestiaryRaces(const std::vector& bestiaryRaces);
static void processCyclopediaCharacterGeneralStats(const CyclopediaCharacterGeneralStats& stats, const std::vector>& skills,
const std::vector>& combats);
- static void processCyclopediaCharacterCombatStats(const CyclopediaCharacterCombatStats& data, const double mitigation,
+ static void processCyclopediaCharacterCombatStats(const CyclopediaCharacterCombatStats& data, double mitigation,
const std::vector>& additionalSkillsArray,
const std::vector>& forgeSkillsArray, const std::vector& perfectShotDamageRangesArray,
const std::vector>& combatsArray,
const std::vector>& concoctionsArray);
- static void processCyclopediaCharacterGeneralStatsBadge(const uint8_t showAccountInformation, const uint8_t playerOnline, const uint8_t playerPremium,
- const std::string_view loyaltyTitle,
- const std::vector>& badgesVector);
+ static void processCyclopediaCharacterGeneralStatsBadge(uint8_t showAccountInformation, uint8_t playerOnline, uint8_t playerPremium,
+ std::string_view loyaltyTitle,
+ const std::vector>& badgesVector);
static void processCyclopediaCharacterItemSummary(const CyclopediaCharacterItemSummary& data);
static void processCyclopediaCharacterAppearances(const OutfitColorStruct& currentOutfit, const std::vector& outfits,
- const std::vector& mounts, std::vector& familiars);
+ const std::vector& mounts, const std::vector& familiars);
static void processCyclopediaCharacterRecentDeaths(const CyclopediaCharacterRecentDeaths& data);
static void processCyclopediaCharacterRecentPvpKills(const CyclopediaCharacterRecentPvPKills& data);
static void processParseBestiaryRaces(const std::vector& bestiaryData);
- static void processParseBestiaryOverview(const std::string_view raceName, const std::vector& data, const uint16_t animusMasteryPoints);
+ static void processParseBestiaryOverview(std::string_view raceName, const std::vector& data, uint16_t animusMasteryPoints);
static void processUpdateBestiaryMonsterData(const BestiaryMonsterData& data);
static void processUpdateBestiaryCharmsData(const BestiaryCharmsData& charmData);
static void processBosstiaryInfo(const std::vector& boss);
@@ -535,29 +533,29 @@ class Game
public:
// login related
- void loginWorld(const std::string_view account, const std::string_view password, const std::string_view worldName, const std::string_view worldHost, int worldPort, const std::string_view characterName, const std::string_view authenticatorToken, const std::string_view sessionKey);
+ void loginWorld(std::string_view account, std::string_view password, std::string_view worldName, std::string_view worldHost, int worldPort, std::string_view characterName, std::string_view authenticatorToken, std::string_view sessionKey);
void cancelLogin();
void forceLogout();
void safeLogout();
// walk related
- bool walk(const Otc::Direction direction, bool force = false);
+ bool walk(Otc::Direction direction);
void autoWalk(const std::vector& dirs, const Position& startPos);
- void forceWalk(const Otc::Direction direction);
- void turn(const Otc::Direction direction);
+ void forceWalk(Otc::Direction direction);
+ void turn(Otc::Direction direction);
void stop();
// item related
- void look(const ThingPtr& thing, const bool isBattleList = false);
+ void look(const ThingPtr& thing, bool isBattleList = false);
void move(const ThingPtr& thing, const Position& toPos, int count);
- void moveToParentContainer(const ThingPtr& thing, const int count);
+ void moveToParentContainer(const ThingPtr& thing, int count);
void rotate(const ThingPtr& thing);
void wrap(const ThingPtr& thing);
void use(const ThingPtr& thing);
void useWith(const ItemPtr& item, const ThingPtr& toThing);
- void useInventoryItem(const uint16_t itemId);
- void useInventoryItemWith(const uint16_t itemId, const ThingPtr& toThing);
- ItemPtr findItemInContainers(const uint32_t itemId, const int subType);
+ void useInventoryItem(uint16_t itemId);
+ void useInventoryItemWith(uint16_t itemId, const ThingPtr& toThing);
+ ItemPtr findItemInContainers(uint32_t itemId, int subType);
// container related
int open(const ItemPtr& item, const ContainerPtr& previousContainer);
@@ -573,27 +571,27 @@ class Game
void cancelAttackAndFollow();
// talk related
- void talk(const std::string_view message);
- void talkChannel(const Otc::MessageMode mode, const uint16_t channelId, const std::string_view message);
- void talkPrivate(const Otc::MessageMode mode, const std::string_view receiver, const std::string_view message);
+ void talk(std::string_view message);
+ void talkChannel(Otc::MessageMode mode, uint16_t channelId, std::string_view message);
+ void talkPrivate(Otc::MessageMode mode, std::string_view receiver, std::string_view message);
// channel related
- void openPrivateChannel(const std::string_view receiver);
+ void openPrivateChannel(std::string_view receiver);
void requestChannels();
- void joinChannel(const uint16_t channelId);
- void leaveChannel(const uint16_t channelId);
+ void joinChannel(uint16_t channelId);
+ void leaveChannel(uint16_t channelId);
void closeNpcChannel();
void openOwnChannel();
- void inviteToOwnChannel(const std::string_view name);
- void excludeFromOwnChannel(const std::string_view name);
+ void inviteToOwnChannel(std::string_view name);
+ void excludeFromOwnChannel(std::string_view name);
// party related
- void partyInvite(const uint32_t creatureId);
- void partyJoin(const uint32_t creatureId);
- void partyRevokeInvitation(const uint32_t creatureId);
- void partyPassLeadership(const uint32_t creatureId);
+ void partyInvite(uint32_t creatureId);
+ void partyJoin(uint32_t creatureId);
+ void partyRevokeInvitation(uint32_t creatureId);
+ void partyPassLeadership(uint32_t creatureId);
void partyLeave();
- void partyShareExperience(const bool active);
+ void partyShareExperience(bool active);
// outfit related
void requestOutfit();
@@ -602,104 +600,104 @@ class Game
void sendTyping(bool typing);
// vip related
- void addVip(const std::string_view name);
- void removeVip(const uint32_t playerId);
- void editVip(const uint32_t playerId, const std::string_view description, const uint32_t iconId, const bool notifyLogin, const std::vector& groupID = {});
- void editVipGroups(const Otc::GroupsEditInfoType_t action, const uint8_t groupId, const std::string_view groupName);
+ void addVip(std::string_view name);
+ void removeVip(uint32_t playerId);
+ void editVip(uint32_t playerId, std::string_view description, uint32_t iconId, bool notifyLogin, const std::vector& groupID = {});
+ void editVipGroups(Otc::GroupsEditInfoType_t action, uint8_t groupId, std::string_view groupName);
// fight modes related
- void setChaseMode(const Otc::ChaseModes chaseMode);
- void setFightMode(const Otc::FightModes fightMode);
- void setSafeFight(const bool on);
- void setPVPMode(const Otc::PVPModes pvpMode);
+ void setChaseMode(Otc::ChaseModes chaseMode);
+ void setFightMode(Otc::FightModes fightMode);
+ void setSafeFight(bool on);
+ void setPVPMode(Otc::PVPModes pvpMode);
Otc::ChaseModes getChaseMode() { return m_chaseMode; }
Otc::FightModes getFightMode() { return m_fightMode; }
bool isSafeFight() { return m_safeFight; }
Otc::PVPModes getPVPMode() { return m_pvpMode; }
// pvp related
- void setUnjustifiedPoints(const UnjustifiedPoints unjustifiedPoints);
+ void setUnjustifiedPoints(UnjustifiedPoints unjustifiedPoints);
UnjustifiedPoints getUnjustifiedPoints() { return m_unjustifiedPoints; };
- void setOpenPvpSituations(const uint8_t openPvpSituations);
+ void setOpenPvpSituations(uint8_t openPvpSituations);
int getOpenPvpSituations() { return m_openPvpSituations; }
// npc trade related
void inspectNpcTrade(const ItemPtr& item);
- void buyItem(const ItemPtr& item, const uint16_t amount, const bool ignoreCapacity, const bool buyWithBackpack);
- void sellItem(const ItemPtr& item, const uint16_t amount, const bool ignoreEquipped);
+ void buyItem(const ItemPtr& item, uint16_t amount, bool ignoreCapacity, bool buyWithBackpack);
+ void sellItem(const ItemPtr& item, uint16_t amount, bool ignoreEquipped);
void closeNpcTrade();
// player trade related
void requestTrade(const ItemPtr& item, const CreaturePtr& creature);
- void inspectTrade(const bool counterOffer, const uint8_t index);
+ void inspectTrade(bool counterOffer, uint8_t index);
void acceptTrade();
void rejectTrade();
// house window and editable items related
- void editText(const uint32_t id, const std::string_view text);
- void editList(const uint32_t id, const uint8_t doorId, const std::string_view text);
+ void editText(uint32_t id, std::string_view text);
+ void editList(uint32_t id, uint8_t doorId, std::string_view text);
// rule violations (only gms)
- void openRuleViolation(const std::string_view reporter);
- void closeRuleViolation(const std::string_view reporter);
+ void openRuleViolation(std::string_view reporter);
+ void closeRuleViolation(std::string_view reporter);
void cancelRuleViolation();
// reports
- void reportBug(const std::string_view comment);
- void reportRuleViolation(const std::string_view target, const uint8_t reason, const uint8_t action, const std::string_view comment, const std::string_view statement, const uint16_t statementId, const bool ipBanishment);
- void debugReport(const std::string_view a, const std::string_view b, const std::string_view c, const std::string_view d);
+ void reportBug(std::string_view comment);
+ void reportRuleViolation(std::string_view target, uint8_t reason, uint8_t action, std::string_view comment, std::string_view statement, uint16_t statementId, bool ipBanishment);
+ void debugReport(std::string_view a, std::string_view b, std::string_view c, std::string_view d);
// questlog related
void requestQuestLog();
- void requestQuestLine(const uint16_t questId);
+ void requestQuestLine(uint16_t questId);
// 870 only
void equipItem(const ItemPtr& item);
- void mount(const bool mount);
+ void mount(bool mount);
// 910 only
- void requestItemInfo(const ItemPtr& item, const uint8_t index);
+ void requestItemInfo(const ItemPtr& item, uint8_t index);
// >= 970 modal dialog
- void answerModalDialog(const uint32_t dialog, const uint8_t button, const uint8_t choice);
+ void answerModalDialog(uint32_t dialog, uint8_t button, uint8_t choice);
// >= 984 browse field
void browseField(const Position& position);
- void seekInContainer(const uint8_t containerId, const uint16_t index);
+ void seekInContainer(uint8_t containerId, uint16_t index);
// >= 1080 ingame store
- void buyStoreOffer(const uint32_t offerId, const uint8_t productType, const std::string_view name = "");
- void requestTransactionHistory(const uint32_t page, const uint32_t entriesPerPage);
- void requestStoreOffers(const std::string_view categoryName, const uint8_t serviceType = 0);
- void openStore(const uint8_t serviceType = 0, const std::string_view category = "");
- void transferCoins(const std::string_view recipient, const uint16_t amount);
- void openTransactionHistory(const uint8_t entriesPerPage);
+ void buyStoreOffer(uint32_t offerId, uint8_t productType, std::string_view name = "");
+ void requestTransactionHistory(uint32_t page, uint32_t entriesPerPage);
+ void requestStoreOffers(std::string_view categoryName, uint8_t serviceType = 0);
+ void openStore(uint8_t serviceType = 0, std::string_view category = "");
+ void transferCoins(std::string_view recipient, uint16_t amount);
+ void openTransactionHistory(uint8_t entriesPerPage);
//void reportRuleViolation2();
void ping();
void setPingDelay(const int delay) { m_pingDelay = delay; }
// otclient only
- void changeMapAwareRange(const uint8_t xrange, const uint8_t yrange);
+ void changeMapAwareRange(uint8_t xrange, uint8_t yrange);
// dynamic support for game features
void enableFeature(const Otc::GameFeature feature) { m_features.set(feature, true); }
void disableFeature(const Otc::GameFeature feature) { m_features.set(feature, false); }
- void setFeature(const Otc::GameFeature feature, bool enabled) { m_features.set(feature, enabled); }
+ void setFeature(const Otc::GameFeature feature, const bool enabled) { m_features.set(feature, enabled); }
bool getFeature(const Otc::GameFeature feature) { return m_features.test(feature); }
- void setProtocolVersion(const uint16_t version);
+ void setProtocolVersion(uint16_t version);
int getProtocolVersion() { return m_protocolVersion; }
bool isUsingProtobuf() { return getProtocolVersion() >= 1281 && !getFeature(Otc::GameLoadSprInsteadProtobuf); }
- void setClientVersion(const uint16_t version);
+ void setClientVersion(uint16_t version);
int getClientVersion() { return m_clientVersion; }
void setCustomOs(const Otc::OperatingSystem_t os) { m_clientCustomOs = os; }
Otc::OperatingSystem_t getOs();
- void setWalkTurnDelay(uint16_t v) { m_walkTurnDelay = v; }
- void setWalkFirstStepDelay(uint16_t v) { m_walkFirstStepDelay = v; }
+ void setWalkTurnDelay(const uint16_t v) { m_walkTurnDelay = v; }
+ void setWalkFirstStepDelay(const uint16_t v) { m_walkFirstStepDelay = v; }
uint16_t getWalkTurnDelay() { return m_walkTurnDelay; }
uint16_t getWalkFirstStepDelay() { return m_walkFirstStepDelay; }
@@ -722,16 +720,16 @@ class Game
bool isConnectionOk() { return m_protocolGame && m_protocolGame->getElapsedTicksSinceLastRead() < 5000; }
int getPing() { return m_ping; }
- ContainerPtr getContainer(int index) { return m_containers[index]; }
+ ContainerPtr getContainer(const int index) { return m_containers[index]; }
stdext::map getContainers() { return m_containers; }
stdext::map getVips() { return m_vips; }
CreaturePtr getAttackingCreature() { return m_attackingCreature; }
CreaturePtr getFollowingCreature() { return m_followingCreature; }
- void setServerBeat(int beat) { m_serverBeat = beat; }
+ void setServerBeat(const int beat) { m_serverBeat = beat; }
int getServerBeat() { return m_serverBeat; }
- void setCanReportBugs(bool enable) { m_canReportBugs = enable; }
+ void setCanReportBugs(const bool enable) { m_canReportBugs = enable; }
bool canReportBugs() { return m_canReportBugs; }
- void setExpertPvpMode(bool enable) { m_expertPvpMode = enable; }
+ void setExpertPvpMode(const bool enable) { m_expertPvpMode = enable; }
bool getExpertPvpMode() { return m_expertPvpMode; }
LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
@@ -740,56 +738,56 @@ class Game
std::vector getGMActions() { return m_gmActions; }
bool isGM() { return !m_gmActions.empty(); }
- std::string formatCreatureName(const std::string_view name);
+ std::string formatCreatureName(std::string_view name);
int findEmptyContainerId();
// market related
void leaveMarket();
- void browseMarket(const uint8_t browseId, const uint8_t browseType);
- void createMarketOffer(const uint8_t type, const uint16_t itemId, const uint8_t itemTier, const uint16_t amount, const uint64_t price, const uint8_t anonymous);
- void cancelMarketOffer(const uint32_t timestamp, const uint16_t counter);
- void acceptMarketOffer(const uint32_t timestamp, const uint16_t counter, const uint16_t amount);
+ void browseMarket(uint8_t browseId, uint8_t browseType);
+ void createMarketOffer(uint8_t type, uint16_t itemId, uint8_t itemTier, uint16_t amount, uint64_t price, uint8_t anonymous);
+ void cancelMarketOffer(uint32_t timestamp, uint16_t counter);
+ void acceptMarketOffer(uint32_t timestamp, uint16_t counter, uint16_t amount);
// prey related
- void preyAction(const uint8_t slot, const uint8_t actionType, const uint16_t index);
+ void preyAction(uint8_t slot, uint8_t actionType, uint16_t index);
void preyRequest();
// imbuing related
- void applyImbuement(const uint8_t slot, const uint32_t imbuementId, const bool protectionCharm);
- void clearImbuement(const uint8_t slot);
+ void applyImbuement(uint8_t slot, uint32_t imbuementId, bool protectionCharm);
+ void clearImbuement(uint8_t slot);
void closeImbuingWindow();
void imbuementDurations(bool isOpen = false);
- void enableTileThingLuaCallback(bool value) { m_tileThingsLuaCallback = value; }
+ void enableTileThingLuaCallback(const bool value) { m_tileThingsLuaCallback = value; }
bool isTileThingLuaCallbackEnabled() { return m_tileThingsLuaCallback; }
- void stashWithdraw(const uint16_t itemId, const uint32_t count, const uint8_t stackpos);
+ void stashWithdraw(uint16_t itemId, uint32_t count, uint8_t stackpos);
// highscore related
- void requestHighscore(const uint8_t action, const uint8_t category, const uint32_t vocation, const std::string_view world, const uint8_t worldType, const uint8_t battlEye, const uint16_t page, const uint8_t totalPages);
- void processHighscore(const std::string_view serverName, const std::string_view world, const uint8_t worldType, const uint8_t battlEye,
- const std::vector>& vocations,
- const std::vector>& categories,
- const uint16_t page, const uint16_t totalPages,
- const std::vector>& highscores, const uint32_t entriesTs);
+ void requestHighscore(uint8_t action, uint8_t category, uint32_t vocation, std::string_view world, uint8_t worldType, uint8_t battlEye, uint16_t page, uint8_t totalPages);
+ void processHighscore(std::string_view serverName, std::string_view world, uint8_t worldType, uint8_t battlEye,
+ const std::vector>& vocations,
+ const std::vector>& categories,
+ uint16_t page, uint16_t totalPages,
+ const std::vector>& highscores, uint32_t entriesTs);
void requestBless();
- void requestQuickLootBlackWhiteList(const uint8_t filter, const uint16_t size, const std::vector& listedItems);
- void openContainerQuickLoot(const uint8_t action, const uint8_t category, const Position& pos, const uint16_t itemId, const uint8_t stackpos, const bool useMainAsFallback);
+ void requestQuickLootBlackWhiteList(uint8_t filter, uint16_t size, const std::vector& listedItems);
+ void openContainerQuickLoot(uint8_t action, uint8_t category, const Position& pos, uint16_t itemId, uint8_t stackpos, bool useMainAsFallback);
void sendGmTeleport(const Position& pos);
// cyclopedia related
void inspectionNormalObject(const Position& position);
- void inspectionObject(const Otc::InspectObjectTypes inspectionType, const uint16_t itemId, const uint8_t itemCount);
+ void inspectionObject(Otc::InspectObjectTypes inspectionType, uint16_t itemId, uint8_t itemCount);
void requestBestiary();
- void requestBestiaryOverview(const std::string_view catName);
- void requestBestiarySearch(const uint16_t raceId);
- void requestSendBuyCharmRune(const uint8_t runeId, const uint8_t action, const uint16_t raceId);
- void requestSendCharacterInfo(const uint32_t playerId, const Otc::CyclopediaCharacterInfoType_t characterInfoType, const uint16_t entriesPerPage = 0, const uint16_t page = 0);
+ void requestBestiaryOverview(std::string_view catName);
+ void requestBestiarySearch(uint16_t raceId);
+ void requestSendBuyCharmRune(uint8_t runeId, uint8_t action, uint16_t raceId);
+ void requestSendCharacterInfo(uint32_t playerId, Otc::CyclopediaCharacterInfoType_t characterInfoType, uint16_t entriesPerPage = 0, uint16_t page = 0);
void requestBosstiaryInfo();
void requestBossSlootInfo();
- void requestBossSlotAction(const uint8_t action, const uint32_t raceId);
- void sendStatusTrackerBestiary(const uint16_t raceId, const bool status);
+ void requestBossSlotAction(uint8_t action, uint32_t raceId);
+ void sendStatusTrackerBestiary(uint16_t raceId, bool status);
protected:
void enableBotCall() { m_denyBotCall = false; }
void disableBotCall() { m_denyBotCall = true; }
diff --git a/src/client/gameconfig.cpp b/src/client/gameconfig.cpp
index 41cf818d52..f295088946 100644
--- a/src/client/gameconfig.cpp
+++ b/src/client/gameconfig.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,9 +21,9 @@
*/
#include "gameconfig.h"
-#include
-#include
#include
+#include
+#include
GameConfig g_gameConfig;
@@ -31,9 +31,6 @@ static constexpr bool LOAD_SETUP = true;
void GameConfig::init()
{
- if (!LOAD_SETUP)
- return;
-
const std::string& fileName = "/data/setup";
try {
diff --git a/src/client/gameconfig.h b/src/client/gameconfig.h
index 37240e9f15..ec97279370 100644
--- a/src/client/gameconfig.h
+++ b/src/client/gameconfig.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,8 +24,8 @@
#include "declarations.h"
-#include
#include
+#include
// @bindclass
class GameConfig
diff --git a/src/client/global.h b/src/client/global.h
index f47de87298..101f42e430 100644
--- a/src/client/global.h
+++ b/src/client/global.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/houses.cpp b/src/client/houses.cpp
index 756e8e7368..8db725edc5 100644
--- a/src/client/houses.cpp
+++ b/src/client/houses.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/houses.h b/src/client/houses.h
index d9b7b2657d..a208e90a73 100644
--- a/src/client/houses.h
+++ b/src/client/houses.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/item.cpp b/src/client/item.cpp
index b0fab796b3..39f7657026 100644
--- a/src/client/item.cpp
+++ b/src/client/item.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,20 +23,16 @@
#include "item.h"
#include "container.h"
#include "game.h"
-#include "map.h"
#include "spritemanager.h"
#include "thing.h"
#include "thingtypemanager.h"
#include "tile.h"
-#include
#include
-#include
#include
-#include
#include
-ItemPtr Item::create(int id)
+ItemPtr Item::create(const int id)
{
const auto& item = std::make_shared- ();
item->setId(id);
@@ -44,7 +40,7 @@ ItemPtr Item::create(int id)
return item;
}
-void Item::draw(const Point& dest, bool drawThings, const LightViewPtr& lightView)
+void Item::draw(const Point& dest, const bool drawThings, const LightViewPtr& lightView)
{
if (!canDraw(m_color) || isHided())
return;
@@ -60,7 +56,7 @@ void Item::draw(const Point& dest, bool drawThings, const LightViewPtr& lightVie
internalDraw(animationPhase, dest, getHighlightColor(), drawThings, true);
}
-void Item::internalDraw(int animationPhase, const Point& dest, const Color& color, bool drawThings, bool replaceColorShader, const LightViewPtr& lightView)
+void Item::internalDraw(const int animationPhase, const Point& dest, const Color& color, const bool drawThings, const bool replaceColorShader, const LightViewPtr& lightView)
{
if (replaceColorShader)
g_drawPool.setShaderProgram(g_painter->getReplaceColorShader(), true);
@@ -95,14 +91,14 @@ void Item::setConductor()
{
if (isSingleGround()) {
m_drawConductor.agroup = true;
- m_drawConductor.order = DrawOrder::FIRST;
+ m_drawConductor.order = FIRST;
} else if (isSingleGroundBorder() && !hasElevation()) {
m_drawConductor.agroup = true;
- m_drawConductor.order = DrawOrder::SECOND;
+ m_drawConductor.order = SECOND;
}
}
-void Item::setPosition(const Position& position, uint8_t stackPos, bool hasElevation)
+void Item::setPosition(const Position& position, const uint8_t stackPos, const bool hasElevation)
{
Thing::setPosition(position, stackPos);
diff --git a/src/client/item.h b/src/client/item.h
index 10f1f40a22..374e10d729 100644
--- a/src/client/item.h
+++ b/src/client/item.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,8 @@
#pragma once
-#include
-#include "effect.h"
#include "thing.h"
+#include
enum ItemAttr : uint8_t
{
@@ -71,7 +70,7 @@ enum ItemAttr : uint8_t
// @bindclass
#pragma pack(push,1) // disable memory alignment
-class Item : public Thing
+class Item final : public Thing
{
public:
static ItemPtr create(int id);
@@ -81,13 +80,14 @@ class Item : public Thing
void setId(uint32_t id) override;
- void setCountOrSubType(int value) { m_countOrSubType = value; updatePatterns(); }
- void setCount(int count) { m_countOrSubType = count; updatePatterns(); }
- void setSubType(int subType) { m_countOrSubType = subType; updatePatterns(); }
+ void setCountOrSubType(const int value) { m_countOrSubType = value; updatePatterns(); }
+ void setCount(const int count) { m_countOrSubType = count; updatePatterns(); }
+ void setSubType(const int subType) { m_countOrSubType = subType; updatePatterns(); }
void setColor(const Color& c) { if (m_color != c) m_color = c; }
void setPosition(const Position& position, uint8_t stackPos = 0, bool hasElevation = false) override;
void setTooltip(const std::string& str) { m_tooltip = str; }
void setDurationTime(const uint32_t durationTime) { m_durationTime = durationTime; }
+ void setCharges(const uint32_t charges) { m_charges = charges; }
void setTier(const uint8_t tier) { m_tier = tier; }
int getCountOrSubType() { return m_countOrSubType; }
@@ -95,11 +95,12 @@ class Item : public Thing
int getCount() { return isStackable() ? m_countOrSubType : 1; }
std::string getTooltip() { return m_tooltip; }
uint32_t getDurationTime() { return m_durationTime; }
+ uint32_t getCharges() { return m_charges; }
uint8_t getTier() { return m_tier; }
bool isValid() { return getThingType() != nullptr; }
- void setAsync(bool enable) { m_async = enable; }
+ void setAsync(const bool enable) { m_async = enable; }
ItemPtr clone();
ItemPtr asItem() { return static_self_cast
- (); }
@@ -107,7 +108,7 @@ class Item : public Thing
void updatePatterns();
int calculateAnimationPhase();
- int getExactSize(int layer = 0, int /*xPattern*/ = 0, int /*yPattern*/ = 0, int /*zPattern*/ = 0, int /*animationPhase*/ = 0) override {
+ int getExactSize(const int layer = 0, int /*xPattern*/ = 0, int /*yPattern*/ = 0, int /*zPattern*/ = 0, int /*animationPhase*/ = 0) override {
return Thing::getExactSize(layer, m_numPatternX, m_numPatternY, m_numPatternZ, calculateAnimationPhase());
}
@@ -162,11 +163,12 @@ class Item : public Thing
uint16_t m_countOrSubType{ 0 };
uint32_t m_durationTime{ 0 };
+ uint32_t m_charges{ 0 };
uint8_t m_tier{ 0 };
+ uint8_t m_phase{ 0 };
Color m_color{ Color::white };
- uint8_t m_phase{ 0 };
ticks_t m_lastPhase{ 0 };
bool m_async{ true };
diff --git a/src/client/itemtype.cpp b/src/client/itemtype.cpp
index 0c7de00a5c..9c3cd2fdb8 100644
--- a/src/client/itemtype.cpp
+++ b/src/client/itemtype.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/itemtype.h b/src/client/itemtype.h
index 74d25541dd..d111880046 100644
--- a/src/client/itemtype.h
+++ b/src/client/itemtype.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/client/lightview.cpp b/src/client/lightview.cpp
index ce93428a07..eb72aa3c4d 100644
--- a/src/client/lightview.cpp
+++ b/src/client/lightview.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2022 OTClient
+ * Copyright (c) 2010-2024 OTClient
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,11 +22,9 @@
#include "lightview.h"
#include "map.h"
-#include "mapview.h"
-#include "spritemanager.h"
-#include
#include
+#include
#include
LightView::LightView(const Size& size) : m_pool(g_drawPool.get(DrawPoolType::LIGHT)) {
@@ -54,10 +52,9 @@ void LightView::resize(const Size& size, const uint16_t tileSize) {
m_texture->setupSize(m_mapSize);
}
-void LightView::addLightSource(const Point& pos, const Light& light, float brightness)
+void LightView::addLightSource(const Point& pos, const Light& light, const float brightness)
{
- if (!isDark() || light.intensity == 0)
- return;
+ if (!isDark() || light.intensity == 0) return;
if (!m_lightData.lights.empty()) {
auto& prevLight = m_lightData.lights.back();
@@ -67,22 +64,22 @@ void LightView::addLightSource(const Point& pos, const Light& light, float brigh
}
}
- size_t hash = 0;
-
- stdext::hash_union(hash, pos.hash());
+ size_t hash = pos.hash();
stdext::hash_combine(hash, light.intensity);
stdext::hash_combine(hash, light.color);
if (g_drawPool.getOpacity() < 1.f)
stdext::hash_combine(hash, g_drawPool.getOpacity());
- if (m_pool->getHashController().put(hash))
- m_lightData.lights.emplace_back(pos, light.intensity, light.color, std::min(brightness, g_drawPool.getOpacity()));
+ if (m_pool->getHashController().put(hash)) {
+ const float effectiveBrightness = std::min(brightness, g_drawPool.getOpacity());
+ m_lightData.lights.emplace_back(pos, light.intensity, light.color, effectiveBrightness);
+ }
}
void LightView::resetShade(const Point& pos)
{
- size_t index = (pos.y / m_tileSize) * m_mapSize.width() + (pos.x / m_tileSize);
+ const size_t index = (pos.y / m_tileSize) * m_mapSize.width() + (pos.x / m_tileSize);
if (index >= m_lightData.tiles.size()) return;
m_lightData.tiles[index] = m_lightData.lights.size();
}
@@ -135,35 +132,55 @@ void LightView::updateCoords(const Rect& dest, const Rect& src) {
static_cast(size.width()) / m_tileSize, static_cast(size.height()) / m_tileSize));
}
-void LightView::updatePixels() {
- const size_t lightSize = m_lightData.lights.size();
-
- const int mapWidth = m_mapSize.width();
- const int mapHeight = m_mapSize.height();
-
- for (int x = -1; ++x < mapWidth;) {
- for (int y = -1; ++y < mapHeight;) {
- const Point pos(x * m_tileSize + m_tileSize / 2, y * m_tileSize + m_tileSize / 2);
- const int index = (y * mapWidth + x);
- const int colorIndex = index * 4;
- m_pixels[colorIndex] = m_globalLightColor.r();
- m_pixels[colorIndex + 1] = m_globalLightColor.g();
- m_pixels[colorIndex + 2] = m_globalLightColor.b();
- m_pixels[colorIndex + 3] = 255; // alpha channel
- for (size_t i = m_lightData.tiles[index]; i < lightSize; ++i) {
+void LightView::updatePixels()
+{
+ const auto lightSize = m_lightData.lights.size();
+ const auto mapWidth = m_mapSize.width();
+ const auto mapHeight = m_mapSize.height();
+ const auto tileCenterOffset = m_tileSize / 2;
+ const auto invTileSize = 1.0f / m_tileSize;
+
+ auto* pixelData = m_pixels.data();
+
+ for (int y = 0; y < mapHeight; ++y) {
+ for (int x = 0; x < mapWidth; ++x) {
+ const auto centerX = x * m_tileSize + tileCenterOffset;
+ const auto centerY = y * m_tileSize + tileCenterOffset;
+ const auto index = y * mapWidth + x;
+
+ auto r = m_globalLightColor.r();
+ auto g = m_globalLightColor.g();
+ auto b = m_globalLightColor.b();
+
+ for (auto i = m_lightData.tiles[index]; i < lightSize; ++i) {
const auto& light = m_lightData.lights[i];
- const float distance = (std::sqrt((pos.x - light.pos.x) * (pos.x - light.pos.x) +
- (pos.y - light.pos.y) * (pos.y - light.pos.y))) / m_tileSize;
- float intensity = (-distance + light.intensity) * 0.2f;
+ const auto dx = centerX - light.pos.x;
+ const auto dy = centerY - light.pos.y;
+ const auto distanceSq = dx * dx + dy * dy;
+
+ const auto lightRadiusSq = (light.intensity * m_tileSize) * (light.intensity * m_tileSize);
+ if (distanceSq > lightRadiusSq) continue;
+
+ const auto distanceNorm = std::sqrt(distanceSq) * invTileSize;
+ float intensity = (-distanceNorm + light.intensity) * 0.2f;
if (intensity < 0.01f) continue;
- if (intensity > 1.0f) intensity = 1.0f;
+
+ intensity = std::min(intensity, 1.0f);
const auto& lightColor = Color::from8bit(light.color) * intensity;
- m_pixels[colorIndex] = std::max(m_pixels[colorIndex], lightColor.r());
- m_pixels[colorIndex + 1] = std::max(m_pixels[colorIndex + 1], lightColor.g());
- m_pixels[colorIndex + 2] = std::max