mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-07-12 18:31:54 +00:00
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
328743f1e1 | ||
|
|
3178a702a7 | ||
|
|
5e3312850c | ||
|
|
756dc4e44f | ||
|
|
87613f287f | ||
|
|
ac1b00fdaa | ||
|
|
364bcf822a | ||
|
|
d5b2278dc5 | ||
|
|
0bf5ee02e4 | ||
|
|
c2ad76ee66 | ||
|
|
cce5755366 | ||
|
|
e966d3a644 | ||
|
|
9328021339 | ||
|
|
d1134fdd62 | ||
|
|
efa658bc31 | ||
|
|
e59525d2ae | ||
|
|
c6528d444a | ||
|
|
0348db72a5 | ||
|
|
78425aacb1 | ||
|
|
debc2a96e2 | ||
|
|
8ae1a8ff7c | ||
|
|
b8d610987f | ||
|
|
7845131649 | ||
|
|
f0960d48b2 | ||
|
|
dc430d9758 | ||
|
|
dea10c6d56 | ||
|
|
ed00551982 | ||
|
|
d6cac65a8d | ||
|
|
d8f079cb1b | ||
|
|
c8e0bb0db0 | ||
|
|
d9d262d3f1 | ||
|
|
d0a5678290 | ||
|
|
35321b22d9 | ||
|
|
8837b110ab | ||
|
|
09a8c99f3e | ||
|
|
e3b108e00e | ||
|
|
9f382aca42 | ||
|
|
4d1395e522 | ||
|
|
9e36510c6b | ||
|
|
2ca61c3e57 | ||
|
|
07cb19cc30 | ||
|
|
794b254fe7 | ||
|
|
ab7f6f0b57 | ||
|
|
58cc569c75 | ||
|
|
35c463656d | ||
|
|
3801a97722 | ||
|
|
0367c67c85 | ||
|
|
8fdc212cda | ||
|
|
99e7349f6c | ||
|
|
fafe2aefad | ||
|
|
5049f215ba | ||
|
|
3a6123fe36 | ||
|
|
b8b2b687e2 | ||
|
|
d067a8d12f | ||
|
|
1ee45639af | ||
|
|
db192d2cde | ||
|
|
28ce8ac54d | ||
|
|
be0a2f6f14 | ||
|
|
3260a063cb | ||
|
|
feeac2e041 | ||
|
|
18c27b14c8 | ||
|
|
bcfaa6c7fe | ||
|
|
06e7d57e0d | ||
|
|
b340d7c8f9 | ||
|
|
24de0e5fdb | ||
|
|
20408d8dfe | ||
|
|
c1c5db6593 | ||
|
|
884a41f36a | ||
|
|
bbc0908989 | ||
|
|
5996f3cbf4 | ||
|
|
150031861d | ||
|
|
9d8e0a9c4a | ||
|
|
bd9b790e1d | ||
|
|
39b81b6263 | ||
|
|
1e09ec92e3 | ||
|
|
2b253a8248 | ||
|
|
3262bc3a86 | ||
|
|
3a4e554da9 | ||
|
|
35ce8771e5 | ||
|
|
b9092a3cce | ||
|
|
0b4f70a76b | ||
|
|
4bc4624bc9 | ||
|
|
3a6313a3ba | ||
|
|
6e3b5acede | ||
|
|
fe4b29f643 | ||
|
|
fcb89b3c7a | ||
|
|
1a0aaf3123 | ||
|
|
9a26ba0a72 | ||
|
|
6c9c826e19 | ||
|
|
554a9a6806 | ||
|
|
c4c1e93dc8 | ||
|
|
15504e693b | ||
|
|
7d626dc31b | ||
|
|
6df6e3e313 | ||
|
|
4ffdf851c6 | ||
|
|
43707952d2 | ||
|
|
ef3fdba621 | ||
|
|
398426545c | ||
|
|
366a80ffd2 | ||
|
|
c9a8be4fb9 | ||
|
|
424d54b98c | ||
|
|
b261e63233 | ||
|
|
75544e3eec | ||
|
|
9e0dd05d42 | ||
|
|
27d20dd8fa | ||
|
|
4b0079c817 | ||
|
|
30b9ef8ab2 | ||
|
|
7235423c7b | ||
|
|
94a467b361 | ||
|
|
c3743877df | ||
|
|
ab937055e7 | ||
|
|
5c1ed332c4 | ||
|
|
95d687846a | ||
|
|
192c8cf974 | ||
|
|
cf706d4974 |
113
CMakeLists.txt
113
CMakeLists.txt
@@ -1,9 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(Darkflame)
|
||||
|
||||
# check if the path to the source directory contains a space
|
||||
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
|
||||
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
|
||||
endif()
|
||||
|
||||
include(CTest)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
# Read variables from file
|
||||
@@ -70,13 +79,15 @@ if(UNIX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
|
||||
elseif(MSVC)
|
||||
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
|
||||
add_compile_options("/wd4267" "/utf-8")
|
||||
# Also disable non-portable MSVC volatile behavior
|
||||
add_compile_options("/wd4267" "/utf-8" "/volatile:iso")
|
||||
elseif(WIN32)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
# Our output dir
|
||||
set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||
#set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) # unfortunately, forces all libraries to be built in series, which will slow down the build process
|
||||
|
||||
# TODO make this not have to override the build type directories
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
@@ -90,6 +101,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
find_package(MariaDB)
|
||||
|
||||
# Create a /resServer directory
|
||||
make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||
|
||||
@@ -97,7 +110,7 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||
make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
|
||||
# Copy resource files on first build
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
|
||||
message(STATUS "Checking resource file integrity")
|
||||
|
||||
include(Utils)
|
||||
@@ -179,7 +192,7 @@ file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PRO
|
||||
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
|
||||
|
||||
# Copy vanity files on first build
|
||||
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
|
||||
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "root.xml" "dev-tribute.xml" "atm.xml" "demo.xml")
|
||||
|
||||
foreach(file ${VANITY_FILES})
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
|
||||
@@ -202,91 +215,47 @@ foreach(file ${SQL_FILES})
|
||||
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
|
||||
endforeach()
|
||||
|
||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||
if (APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
# Load all of our third party directories
|
||||
add_subdirectory(thirdparty SYSTEM)
|
||||
|
||||
# Create our list of include directories
|
||||
set(INCLUDED_DIRECTORIES
|
||||
"dCommon"
|
||||
"dCommon/dClient"
|
||||
"dCommon/dEnums"
|
||||
|
||||
"dChatFilter"
|
||||
|
||||
"dGame"
|
||||
"dGame/dBehaviors"
|
||||
"dGame/dComponents"
|
||||
"dGame/dGameMessages"
|
||||
"dGame/dInventory"
|
||||
"dGame/dMission"
|
||||
"dGame/dEntity"
|
||||
"dGame/dPropertyBehaviors"
|
||||
"dGame/dPropertyBehaviors/ControlBehaviorMessages"
|
||||
"dGame/dUtilities"
|
||||
|
||||
include_directories(
|
||||
"dPhysics"
|
||||
|
||||
"dNavigation"
|
||||
"dNavigation/dTerrain"
|
||||
|
||||
"dZoneManager"
|
||||
|
||||
"dDatabase"
|
||||
"dDatabase/CDClientDatabase"
|
||||
"dDatabase/CDClientDatabase/CDClientTables"
|
||||
"dDatabase/GameDatabase"
|
||||
"dDatabase/GameDatabase/ITables"
|
||||
"dDatabase/GameDatabase/MySQL"
|
||||
"dDatabase/GameDatabase/MySQL/Tables"
|
||||
|
||||
"dNet"
|
||||
|
||||
"thirdparty/magic_enum/include/magic_enum"
|
||||
"thirdparty/raknet/Source"
|
||||
"thirdparty/tinyxml2"
|
||||
"thirdparty/recastnavigation"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
"thirdparty/cpp-httplib"
|
||||
"thirdparty/MD5"
|
||||
|
||||
"tests"
|
||||
"tests/dCommonTests"
|
||||
"tests/dGameTests"
|
||||
"tests/dGameTests/dComponentsTests"
|
||||
|
||||
SYSTEM "thirdparty/magic_enum/include/magic_enum"
|
||||
SYSTEM "thirdparty/raknet/Source"
|
||||
SYSTEM "thirdparty/tinyxml2"
|
||||
SYSTEM "thirdparty/recastnavigation"
|
||||
SYSTEM "thirdparty/SQLite"
|
||||
SYSTEM "thirdparty/cpplinq"
|
||||
SYSTEM "thirdparty/cpp-httplib"
|
||||
SYSTEM "thirdparty/MD5"
|
||||
)
|
||||
|
||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||
# TODO: Should probably not do this.
|
||||
if(APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
# Actually include the directories from our list
|
||||
foreach(dir ${INCLUDED_DIRECTORIES})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/${dir})
|
||||
endforeach()
|
||||
|
||||
if(NOT WIN32)
|
||||
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include")
|
||||
|
||||
# Add linking directories:
|
||||
link_directories(${PROJECT_BINARY_DIR})
|
||||
|
||||
# Load all of our third party directories
|
||||
add_subdirectory(thirdparty)
|
||||
if (UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags
|
||||
endif()
|
||||
# Glob together all headers that need to be precompiled
|
||||
file(
|
||||
GLOB HEADERS_DDATABASE
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||
)
|
||||
|
||||
file(
|
||||
GLOB HEADERS_DZONEMANAGER
|
||||
LIST_DIRECTORIES false
|
||||
@@ -321,7 +290,7 @@ add_subdirectory(dPhysics)
|
||||
add_subdirectory(dServer)
|
||||
|
||||
# Create a list of common libraries shared between all binaries
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum" "MD5")
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum")
|
||||
|
||||
# Add platform specific common libraries
|
||||
if(UNIX)
|
||||
@@ -343,12 +312,6 @@ target_precompile_headers(
|
||||
${HEADERS_DZONEMANAGER}
|
||||
)
|
||||
|
||||
# Need to specify to use the CXX compiler language here or else we get errors including <string>.
|
||||
target_precompile_headers(
|
||||
dDatabase PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE}>"
|
||||
)
|
||||
|
||||
target_precompile_headers(
|
||||
dCommon PRIVATE
|
||||
${HEADERS_DCOMMON}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
PROJECT_VERSION_MAJOR=1
|
||||
PROJECT_VERSION_MINOR=1
|
||||
PROJECT_VERSION_PATCH=1
|
||||
PROJECT_VERSION_MAJOR=2
|
||||
PROJECT_VERSION_MINOR=3
|
||||
PROJECT_VERSION_PATCH=0
|
||||
|
||||
# Debugging
|
||||
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
|
||||
|
||||
@@ -23,8 +23,7 @@ RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Grab libraries and load them
|
||||
COPY --from=build /app/build/mariadbcpp/src/mariadb_connector_cpp-build/libmariadbcpp.so /usr/local/lib/
|
||||
COPY --from=build /app/build/mariadbcpp/src/mariadb_connector_cpp-build/libmariadb/libmariadb/libmariadb.so.3 /usr/local/lib
|
||||
COPY --from=build /app/build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
|
||||
RUN ldconfig
|
||||
|
||||
# Server bins
|
||||
@@ -32,7 +31,7 @@ COPY --from=build /app/build/*Server /app/
|
||||
|
||||
# Necessary suplimentary files
|
||||
COPY --from=build /app/build/*.ini /app/configs/
|
||||
COPY --from=build /app/build/vanity/*.* /app/vanity/*
|
||||
COPY --from=build /app/build/vanity/*.* /app/vanity/
|
||||
COPY --from=build /app/build/navmeshes /app/navmeshes
|
||||
COPY --from=build /app/build/migrations /app/migrations
|
||||
COPY --from=build /app/build/*.dcf /app/
|
||||
@@ -40,7 +39,7 @@ COPY --from=build /app/build/*.dcf /app/
|
||||
# backup of config and vanity files to copy to the host incase
|
||||
# of a mount clobbering the copy from above
|
||||
COPY --from=build /app/build/*.ini /app/default-configs/
|
||||
COPY --from=build /app/build/vanity/*.* /app/default-vanity/*
|
||||
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
|
||||
|
||||
# needed as the container runs with the root user
|
||||
# and therefore sudo doesn't exist
|
||||
|
||||
@@ -51,7 +51,7 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
|
||||
|
||||
### Windows packages
|
||||
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
|
||||
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.18**</font> or later!).
|
||||
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
|
||||
|
||||
### MacOS packages
|
||||
Ensure you have [brew](https://brew.sh) installed.
|
||||
@@ -73,7 +73,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
|
||||
```
|
||||
|
||||
#### Required CMake version
|
||||
This project uses <font size="4">**CMake version 3.18**</font> or higher and as such you will need to ensure you have this version installed.
|
||||
This project uses <font size="4">**CMake version 3.25**</font> or higher and as such you will need to ensure you have this version installed.
|
||||
You can check your CMake version by using the following command in a terminal.
|
||||
```bash
|
||||
cmake --version
|
||||
|
||||
17
cmake/FindGoogleTest.cmake
Normal file
17
cmake/FindGoogleTest.cmake
Normal file
@@ -0,0 +1,17 @@
|
||||
include(FetchContent)
|
||||
|
||||
message(STATUS "Fetching gtest...")
|
||||
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG release-1.12.1
|
||||
)
|
||||
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(GoogleTest)
|
||||
|
||||
message(STATUS "gtest fetched and is now ready.")
|
||||
set(GoogleTest_FOUND TRUE)
|
||||
@@ -23,14 +23,14 @@ if(WIN32 AND NOT MARIADB_BUILD_SOURCE)
|
||||
set(MARIADB_CONNECTOR_CPP_MSI "mariadb-connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}-win64.msi")
|
||||
|
||||
if(NOT EXISTS "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}" )
|
||||
message("Downloading mariadb connector/c")
|
||||
message(STATUS "Downloading mariadb connector/c")
|
||||
file(DOWNLOAD https://dlm.mariadb.com/${MARIADB_CONNECTOR_C_BUCKET}/Connectors/c/connector-c-${MARIADB_CONNECTOR_C_VERSION}/${MARIADB_CONNECTOR_C_MSI}
|
||||
"${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}"
|
||||
EXPECTED_HASH MD5=${MARIADB_CONNECTOR_C_MD5})
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}" )
|
||||
message("Downloading mariadb connector/c++")
|
||||
message(STATUS "Downloading mariadb connector/c++")
|
||||
file(DOWNLOAD https://dlm.mariadb.com/${MARIADB_CONNECTOR_CPP_BUCKET}/Connectors/cpp/connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}/${MARIADB_CONNECTOR_CPP_MSI}
|
||||
"${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}"
|
||||
EXPECTED_HASH MD5=${MARIADB_CONNECTOR_CPP_MD5})
|
||||
@@ -43,27 +43,28 @@ if(WIN32 AND NOT MARIADB_BUILD_SOURCE)
|
||||
file(TO_NATIVE_PATH "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}" MSI_DIR)
|
||||
execute_process(COMMAND msiexec /a ${MSI_DIR} /qn TARGETDIR=${MSIEXEC_TARGETDIR})
|
||||
endif()
|
||||
set(MARIADBC_SHARED_LIBRARY_LOCATION "${MARIADB_C_CONNECTOR_DIR}/lib/libmariadb.dll")
|
||||
|
||||
if(NOT EXISTS "${MARIADB_CPP_CONNECTOR_DIR}")
|
||||
file(TO_NATIVE_PATH "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}" MSI_DIR)
|
||||
execute_process(COMMAND msiexec /a ${MSI_DIR} /qn TARGETDIR=${MSIEXEC_TARGETDIR})
|
||||
endif()
|
||||
|
||||
set(MARIADB_SHARED_LIBRARY_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.dll")
|
||||
set(MARIADBCPP_SHARED_LIBRARY_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.dll")
|
||||
set(MARIADB_IMPLIB_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.lib")
|
||||
set(MARIADB_INCLUDE_DIR "${MARIADB_CPP_CONNECTOR_DIR}/include/mariadb")
|
||||
|
||||
add_custom_target(mariadb_connector_cpp)
|
||||
add_custom_command(TARGET mariadb_connector_cpp POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.dll"
|
||||
"${MARIADB_C_CONNECTOR_DIR}/lib/libmariadb.dll"
|
||||
"${MARIADBCPP_SHARED_LIBRARY_LOCATION}"
|
||||
"${MARIADBC_SHARED_LIBRARY_LOCATION}"
|
||||
"${PROJECT_BINARY_DIR}")
|
||||
|
||||
# MariaDB uses plugins that the database needs to load, the prebuilt binaries by default will try to find the libraries in system directories,
|
||||
# so set this define and the servers will set the MARIADB_PLUGIN_DIR environment variable to the appropriate directory.
|
||||
# Plugin directory is determined at dll load time (this will happen before main()) so we need to delay the dll load so that we can set the environment variable
|
||||
add_link_options(/DELAYLOAD:${MARIADB_SHARED_LIBRARY_LOCATION})
|
||||
add_link_options(/DELAYLOAD:${MARIADBCPP_SHARED_LIBRARY_LOCATION})
|
||||
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${MARIADB_CPP_CONNECTOR_DIR}/plugin")
|
||||
else() # Build from source
|
||||
|
||||
@@ -85,77 +86,61 @@ else() # Build from source
|
||||
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
|
||||
endif()
|
||||
|
||||
set(MARIADBCPP_INSTALL_DIR ${PROJECT_BINARY_DIR}/prefix)
|
||||
set(MARIADBCPP_LIBRARY_DIR ${PROJECT_BINARY_DIR}/mariadbcpp)
|
||||
set(MARIADBCPP_PLUGIN_DIR ${MARIADBCPP_LIBRARY_DIR}/plugin)
|
||||
set(MARIADBCPP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp)
|
||||
set(MARIADB_INCLUDE_DIR "${MARIADBCPP_SOURCE_DIR}/include")
|
||||
ExternalProject_Add(mariadb_connector_cpp
|
||||
SOURCE_DIR ${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp
|
||||
CMAKE_ARGS -Wno-dev
|
||||
-DCMAKE_BUILD_RPATH_USE_ORIGIN=${CMAKE_BUILD_RPATH_USE_ORIGIN}
|
||||
-DCMAKE_INSTALL_PREFIX=./mariadbcpp # Points the connector to the correct plugin directory
|
||||
-DINSTALL_PLUGINDIR=plugin
|
||||
${MARIADB_EXTRA_CMAKE_ARGS}
|
||||
PREFIX "${PROJECT_BINARY_DIR}/mariadbcpp"
|
||||
BUILD_COMMAND cmake --build . --config RelWithDebInfo -j${MARIADB_CONNECTOR_COMPILE_JOBS}
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
ExternalProject_Get_Property(mariadb_connector_cpp BINARY_DIR)
|
||||
PREFIX "${PROJECT_BINARY_DIR}/thirdparty/mariadb-connector-cpp"
|
||||
SOURCE_DIR ${MARIADBCPP_SOURCE_DIR}
|
||||
INSTALL_DIR ${MARIADBCPP_INSTALL_DIR}
|
||||
CMAKE_ARGS -Wno-dev
|
||||
-DWITH_UNIT_TESTS=OFF
|
||||
-DMARIADB_LINK_DYNAMIC=OFF
|
||||
-DCMAKE_BUILD_RPATH_USE_ORIGIN=${CMAKE_BUILD_RPATH_USE_ORIGIN}
|
||||
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
||||
-DINSTALL_LIBDIR=${MARIADBCPP_LIBRARY_DIR}
|
||||
-DINSTALL_PLUGINDIR=${MARIADBCPP_PLUGIN_DIR}
|
||||
${MARIADB_EXTRA_CMAKE_ARGS}
|
||||
BUILD_ALWAYS true
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(MARIADB_SHARED_LIBRARY_NAME mariadbcpp.dll)
|
||||
set(MARIADB_PLUGIN_SUFFIX .dll)
|
||||
set(MARIADB_IMPLIB_LOCATION "${BINARY_DIR}/RelWithDebInfo/mariadbcpp.lib")
|
||||
set(MARIADB_IMPLIB_LOCATION "${MARIADBCPP_LIBRARY_DIR}/mariadbcpp.lib")
|
||||
|
||||
# When built from source windows only seems to check same folder as exe instead specified folder, so use
|
||||
# environment variable to force it
|
||||
add_link_options(/DELAYLOAD:mariadbcpp.dll)
|
||||
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${PROJECT_BINARY_DIR}/mariadbcpp/plugin")
|
||||
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${MARIADBCPP_PLUGIN_DIR}")
|
||||
else()
|
||||
set(MARIADB_SHARED_LIBRARY_NAME libmariadbcpp${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
set(MARIADB_PLUGIN_SUFFIX .so)
|
||||
set(MARIADB_PLUGIN_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(isMultiConfig)
|
||||
set(MARIADB_SHARED_LIBRARY_LOCATION "${BINARY_DIR}/RelWithDebInfo/${MARIADB_SHARED_LIBRARY_NAME}")
|
||||
set(MARIADB_SHARED_LIBRARY_COPY_LOCATION "${PROJECT_BINARY_DIR}/$<CONFIG>")
|
||||
set(MARIADB_PLUGINS_LOCATION "${BINARY_DIR}/libmariadb/RelWithDebInfo")
|
||||
else()
|
||||
set(MARIADB_SHARED_LIBRARY_LOCATION "${BINARY_DIR}/${MARIADB_SHARED_LIBRARY_NAME}")
|
||||
set(MARIADB_SHARED_LIBRARY_COPY_LOCATION "${PROJECT_BINARY_DIR}")
|
||||
set(MARIADB_PLUGINS_LOCATION "${BINARY_DIR}/libmariadb")
|
||||
set(MARIADBCPP_SHARED_LIBRARY_LOCATION "${MARIADBCPP_LIBRARY_DIR}/${MARIADB_SHARED_LIBRARY_NAME}")
|
||||
if(WIN32)
|
||||
set(MARIADBC_SHARED_LIBRARY_LOCATION "${MARIADBCPP_LIBRARY_DIR}/libmariadb.lib")
|
||||
endif()
|
||||
|
||||
set(MARIADB_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include/")
|
||||
|
||||
add_custom_command(TARGET mariadb_connector_cpp POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
${BINARY_DIR}/mariadbcpp/plugin
|
||||
${MARIADB_SHARED_LIBRARY_COPY_LOCATION}
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${MARIADB_SHARED_LIBRARY_LOCATION}
|
||||
${MARIADB_SHARED_LIBRARY_COPY_LOCATION}
|
||||
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${MARIADB_PLUGINS_LOCATION}/caching_sha2_password${MARIADB_PLUGIN_SUFFIX}
|
||||
${MARIADB_PLUGINS_LOCATION}/client_ed25519${MARIADB_PLUGIN_SUFFIX}
|
||||
${MARIADB_PLUGINS_LOCATION}/dialog${MARIADB_PLUGIN_SUFFIX}
|
||||
${MARIADB_PLUGINS_LOCATION}/mysql_clear_password${MARIADB_PLUGIN_SUFFIX}
|
||||
${MARIADB_PLUGINS_LOCATION}/sha256_password${MARIADB_PLUGIN_SUFFIX}
|
||||
${BINARY_DIR}/mariadbcpp/plugin)
|
||||
endif()
|
||||
|
||||
# Remove the CMakeLists.txt file from the tests folder for the maria-db-connector so we dont compile the tests.
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/test/CMakeLists.txt")
|
||||
file(REMOVE "${CMAKE_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/test/CMakeLists.txt")
|
||||
endif()
|
||||
|
||||
# Create mariadb connector library object
|
||||
add_library(mariadbConnCpp SHARED IMPORTED GLOBAL)
|
||||
set_property(TARGET mariadbConnCpp PROPERTY IMPORTED_LOCATION ${MARIADB_SHARED_LIBRARY_LOCATION})
|
||||
add_library(MariaDB::ConnCpp SHARED IMPORTED GLOBAL)
|
||||
add_dependencies(MariaDB::ConnCpp mariadb_connector_cpp)
|
||||
set_target_properties(MariaDB::ConnCpp PROPERTIES
|
||||
IMPORTED_LOCATION "${MARIADBCPP_SHARED_LIBRARY_LOCATION}")
|
||||
|
||||
if(WIN32)
|
||||
set_property(TARGET mariadbConnCpp PROPERTY IMPORTED_IMPLIB ${MARIADB_IMPLIB_LOCATION})
|
||||
set_target_properties(MariaDB::ConnCpp PROPERTIES
|
||||
IMPORTED_IMPLIB "${MARIADB_IMPLIB_LOCATION}")
|
||||
elseif(APPLE)
|
||||
set_target_properties(MariaDB::ConnCpp PROPERTIES
|
||||
IMPORTED_SONAME "libmariadbcpp")
|
||||
endif()
|
||||
|
||||
# Add directories to include lists
|
||||
target_include_directories(mariadbConnCpp INTERFACE ${MARIADB_INCLUDE_DIR})
|
||||
add_dependencies(mariadbConnCpp mariadb_connector_cpp)
|
||||
target_include_directories(MariaDB::ConnCpp SYSTEM INTERFACE ${MARIADB_INCLUDE_DIR})
|
||||
|
||||
set(MariaDB_FOUND TRUE)
|
||||
@@ -27,8 +27,8 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
||||
ExportWordlistToDCF(filepath + ".dcf", true);
|
||||
}
|
||||
|
||||
if (BinaryIO::DoesFileExist("blacklist.dcf")) {
|
||||
ReadWordlistDCF("blacklist.dcf", false);
|
||||
if (BinaryIO::DoesFileExist("blocklist.dcf")) {
|
||||
ReadWordlistDCF("blocklist.dcf", false);
|
||||
}
|
||||
|
||||
//Read player names that are ok as well:
|
||||
@@ -44,20 +44,20 @@ dChatFilter::~dChatFilter() {
|
||||
m_DeniedWords.clear();
|
||||
}
|
||||
|
||||
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool whiteList) {
|
||||
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool allowList) {
|
||||
std::ifstream file(filepath);
|
||||
if (file) {
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
|
||||
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
||||
if (whiteList) m_ApprovedWords.push_back(CalculateHash(line));
|
||||
if (allowList) m_ApprovedWords.push_back(CalculateHash(line));
|
||||
else m_DeniedWords.push_back(CalculateHash(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
|
||||
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
|
||||
std::ifstream file(filepath, std::ios::binary);
|
||||
if (file) {
|
||||
fileHeader hdr;
|
||||
@@ -70,13 +70,13 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
|
||||
if (hdr.formatVersion == formatVersion) {
|
||||
size_t wordsToRead = 0;
|
||||
BinaryIO::BinaryRead(file, wordsToRead);
|
||||
if (whiteList) m_ApprovedWords.reserve(wordsToRead);
|
||||
if (allowList) m_ApprovedWords.reserve(wordsToRead);
|
||||
else m_DeniedWords.reserve(wordsToRead);
|
||||
|
||||
size_t word = 0;
|
||||
for (size_t i = 0; i < wordsToRead; ++i) {
|
||||
BinaryIO::BinaryRead(file, word);
|
||||
if (whiteList) m_ApprovedWords.push_back(word);
|
||||
if (allowList) m_ApprovedWords.push_back(word);
|
||||
else m_DeniedWords.push_back(word);
|
||||
}
|
||||
|
||||
@@ -90,14 +90,14 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteList) {
|
||||
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowList) {
|
||||
std::ofstream file(filepath, std::ios::binary | std::ios_base::out);
|
||||
if (file) {
|
||||
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header));
|
||||
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion));
|
||||
BinaryIO::BinaryWrite(file, size_t(whiteList ? m_ApprovedWords.size() : m_DeniedWords.size()));
|
||||
BinaryIO::BinaryWrite(file, size_t(allowList ? m_ApprovedWords.size() : m_DeniedWords.size()));
|
||||
|
||||
for (size_t word : whiteList ? m_ApprovedWords : m_DeniedWords) {
|
||||
for (size_t word : allowList ? m_ApprovedWords : m_DeniedWords) {
|
||||
BinaryIO::BinaryWrite(file, word);
|
||||
}
|
||||
|
||||
@@ -105,10 +105,10 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteLis
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList) {
|
||||
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
|
||||
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
|
||||
if (message.empty()) return { };
|
||||
if (!whiteList && m_DeniedWords.empty()) return { { 0, message.length() } };
|
||||
if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
|
||||
|
||||
std::stringstream sMessage(message);
|
||||
std::string segment;
|
||||
@@ -126,16 +126,16 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
|
||||
|
||||
size_t hash = CalculateHash(segment);
|
||||
|
||||
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && whiteList) {
|
||||
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && whiteList) {
|
||||
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
|
||||
m_UserUnapprovedWordCache.push_back(hash);
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !whiteList) {
|
||||
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
|
||||
m_UserUnapprovedWordCache.push_back(hash);
|
||||
listOfBadSegments.emplace_back(position, originalSegment.length());
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ public:
|
||||
dChatFilter(const std::string& filepath, bool dontGenerateDCF);
|
||||
~dChatFilter();
|
||||
|
||||
void ReadWordlistPlaintext(const std::string& filepath, bool whiteList);
|
||||
bool ReadWordlistDCF(const std::string& filepath, bool whiteList);
|
||||
void ExportWordlistToDCF(const std::string& filepath, bool whiteList);
|
||||
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList = true);
|
||||
void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
|
||||
bool ReadWordlistDCF(const std::string& filepath, bool allowList);
|
||||
void ExportWordlistToDCF(const std::string& filepath, bool allowList);
|
||||
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
|
||||
|
||||
private:
|
||||
bool m_DontGenerateDCF;
|
||||
|
||||
@@ -5,10 +5,12 @@ set(DCHATSERVER_SOURCES
|
||||
)
|
||||
|
||||
add_executable(ChatServer "ChatServer.cpp")
|
||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
||||
target_include_directories(dChatServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
|
||||
target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter")
|
||||
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
|
||||
|
||||
add_library(dChatServer ${DCHATSERVER_SOURCES})
|
||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
|
||||
|
||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
|
||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
@@ -13,7 +13,7 @@
|
||||
// The only thing not auto-handled is instance activities force joining the team on the server.
|
||||
|
||||
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receivingPlayer);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -59,7 +59,7 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
||||
bitStream.Write(LUWString(ignoredPlayer.playerName, 36));
|
||||
}
|
||||
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
Game::server->Send(bitStream, packet->systemAddress, false);
|
||||
}
|
||||
|
||||
void ChatIgnoreList::AddIgnore(Packet* packet) {
|
||||
@@ -131,7 +131,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
|
||||
bitStream.Write(playerNameSend);
|
||||
bitStream.Write(ignoredPlayerId);
|
||||
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
Game::server->Send(bitStream, packet->systemAddress, false);
|
||||
}
|
||||
|
||||
void ChatIgnoreList::RemoveIgnore(Packet* packet) {
|
||||
@@ -167,5 +167,5 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
|
||||
LUWString playerNameSend(removedIgnoreStr, 33);
|
||||
bitStream.Write(playerNameSend);
|
||||
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
Game::server->Send(bitStream, packet->systemAddress, false);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
#include "eObjectBits.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "eClientMessageType.h"
|
||||
#include "eGameMessageType.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "ChatPackets.h"
|
||||
|
||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
//Get from the packet which player we want to do something with:
|
||||
@@ -60,7 +60,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
|
||||
//Now, we need to send the friendlist to the server they came from:
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -355,6 +355,67 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
|
||||
inStream.Read(player.gmLevel);
|
||||
}
|
||||
|
||||
|
||||
void ChatPacketHandler::HandleWho(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
FindPlayerRequest request;
|
||||
request.Deserialize(inStream);
|
||||
|
||||
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
||||
if (!sender) return;
|
||||
|
||||
const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString());
|
||||
bool online = player;
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(request.requestor);
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
|
||||
bitStream.Write<uint8_t>(online);
|
||||
bitStream.Write(player.zoneID.GetMapID());
|
||||
bitStream.Write(player.zoneID.GetInstanceID());
|
||||
bitStream.Write(player.zoneID.GetCloneID());
|
||||
bitStream.Write(request.playerName);
|
||||
|
||||
SystemAddress sysAddr = sender.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
ShowAllRequest request;
|
||||
request.Deserialize(inStream);
|
||||
|
||||
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
|
||||
if (!sender) return;
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(request.requestor);
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE);
|
||||
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
|
||||
bitStream.Write(Game::playerContainer.GetPlayerCount());
|
||||
bitStream.Write(Game::playerContainer.GetSimCount());
|
||||
bitStream.Write<uint8_t>(request.displayIndividualPlayers);
|
||||
bitStream.Write<uint8_t>(request.displayZoneData);
|
||||
if (request.displayZoneData || request.displayIndividualPlayers){
|
||||
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
|
||||
if (!playerData) continue;
|
||||
bitStream.Write<uint8_t>(0); // structure packing
|
||||
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
|
||||
if (request.displayZoneData) {
|
||||
bitStream.Write(playerData.zoneID.GetMapID());
|
||||
bitStream.Write(playerData.zoneID.GetInstanceID());
|
||||
bitStream.Write(playerData.zoneID.GetCloneID());
|
||||
}
|
||||
}
|
||||
}
|
||||
SystemAddress sysAddr = sender.sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
// the structure the client uses to send this packet is shared in many chat messages
|
||||
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
|
||||
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
@@ -454,7 +515,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||
|
||||
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(routeTo.playerID);
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
|
||||
@@ -696,7 +757,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
|
||||
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -711,7 +772,7 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD
|
||||
|
||||
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -738,7 +799,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b
|
||||
|
||||
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -763,7 +824,7 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
|
||||
|
||||
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -780,7 +841,7 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i
|
||||
|
||||
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -809,7 +870,7 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr
|
||||
|
||||
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -835,7 +896,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
|
||||
|
||||
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -869,7 +930,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
|
||||
[bool] - is FTP*/
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(friendData.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -906,7 +967,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
@@ -920,7 +981,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
|
||||
|
||||
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
// Portion that will get routed:
|
||||
@@ -943,7 +1004,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
|
||||
|
||||
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace ChatPacketHandler {
|
||||
void HandleFriendResponse(Packet* packet);
|
||||
void HandleRemoveFriend(Packet* packet);
|
||||
void HandleGMLevelUpdate(Packet* packet);
|
||||
void HandleWho(Packet* packet);
|
||||
void HandleShowAll(Packet* packet);
|
||||
|
||||
void HandleChatMessage(Packet* packet);
|
||||
void HandlePrivateChatMessage(Packet* packet);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "PlayerContainer.h"
|
||||
#include "ChatPacketHandler.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "eWorldMessageType.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "StringifiedEnum.h"
|
||||
@@ -101,7 +100,7 @@ int main(int argc, char** argv) {
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
std::string ourIP = "localhost";
|
||||
const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
|
||||
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(1501);
|
||||
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(2005);
|
||||
const auto externalIPString = Game::config->GetValue("external_ip");
|
||||
if (!externalIPString.empty()) ourIP = externalIPString;
|
||||
|
||||
@@ -182,47 +181,29 @@ int main(int argc, char** argv) {
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
|
||||
LOG("A server has disconnected, erasing their connected players from the list.");
|
||||
}
|
||||
|
||||
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||
LOG("A server is connecting, awaiting user list.");
|
||||
}
|
||||
} else if (packet->length < 4 || packet->data[0] != ID_USER_PACKET_ENUM) return; // Nothing left to process or not the right packet type
|
||||
|
||||
if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue.
|
||||
CINSTREAM;
|
||||
inStream.SetReadOffset(BYTES_TO_BITS(1));
|
||||
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
|
||||
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
|
||||
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
|
||||
Game::playerContainer.InsertPlayer(packet);
|
||||
break;
|
||||
eConnectionType connection;
|
||||
eChatMessageType chatMessageID;
|
||||
|
||||
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
|
||||
Game::playerContainer.RemovePlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::MUTE_UPDATE:
|
||||
inStream.Read(connection);
|
||||
if (connection != eConnectionType::CHAT) return;
|
||||
inStream.Read(chatMessageID);
|
||||
|
||||
switch (chatMessageID) {
|
||||
case eChatMessageType::GM_MUTE:
|
||||
Game::playerContainer.MuteUpdate(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::CREATE_TEAM:
|
||||
case eChatMessageType::CREATE_TEAM:
|
||||
Game::playerContainer.CreateTeamServer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::ANNOUNCEMENT: {
|
||||
//we just forward this packet to every connected server
|
||||
CINSTREAM;
|
||||
Game::server->Send(&inStream, packet->systemAddress, true); //send to everyone except origin
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
|
||||
eChatMessageType chat_message_type = static_cast<eChatMessageType>(packet->data[3]);
|
||||
switch (chat_message_type) {
|
||||
case eChatMessageType::GET_FRIENDS_LIST:
|
||||
ChatPacketHandler::HandleFriendlistRequest(packet);
|
||||
break;
|
||||
@@ -296,6 +277,23 @@ void HandlePacket(Packet* packet) {
|
||||
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
||||
break;
|
||||
case eChatMessageType::LOGIN_SESSION_NOTIFY:
|
||||
Game::playerContainer.InsertPlayer(packet);
|
||||
break;
|
||||
case eChatMessageType::GM_ANNOUNCE:{
|
||||
// we just forward this packet to every connected server
|
||||
inStream.ResetReadPointer();
|
||||
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
|
||||
}
|
||||
break;
|
||||
case eChatMessageType::UNEXPECTED_DISCONNECT:
|
||||
Game::playerContainer.RemovePlayer(packet);
|
||||
break;
|
||||
case eChatMessageType::WHO:
|
||||
ChatPacketHandler::HandleWho(packet);
|
||||
break;
|
||||
case eChatMessageType::SHOW_ALL:
|
||||
ChatPacketHandler::HandleShowAll(packet);
|
||||
break;
|
||||
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
|
||||
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
|
||||
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
|
||||
@@ -308,7 +306,6 @@ void HandlePacket(Packet* packet) {
|
||||
case eChatMessageType::GUILD_KICK:
|
||||
case eChatMessageType::GUILD_GET_STATUS:
|
||||
case eChatMessageType::GUILD_GET_ALL:
|
||||
case eChatMessageType::SHOW_ALL:
|
||||
case eChatMessageType::BLUEPRINT_MODERATED:
|
||||
case eChatMessageType::BLUEPRINT_MODEL_READY:
|
||||
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
|
||||
@@ -323,7 +320,6 @@ void HandlePacket(Packet* packet) {
|
||||
case eChatMessageType::CSR_REQUEST:
|
||||
case eChatMessageType::CSR_REPLY:
|
||||
case eChatMessageType::GM_KICK:
|
||||
case eChatMessageType::GM_ANNOUNCE:
|
||||
case eChatMessageType::WORLD_ROUTE_PACKET:
|
||||
case eChatMessageType::GET_ZONE_POPULATIONS:
|
||||
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
|
||||
@@ -332,33 +328,18 @@ void HandlePacket(Packet* packet) {
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
||||
case eChatMessageType::UGCC_REQUEST:
|
||||
case eChatMessageType::WHO:
|
||||
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
||||
case eChatMessageType::ACHIEVEMENT_NOTIFY:
|
||||
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||
case eChatMessageType::UNEXPECTED_DISCONNECT:
|
||||
case eChatMessageType::PLAYER_READY:
|
||||
case eChatMessageType::GET_DONATION_TOTAL:
|
||||
case eChatMessageType::UPDATE_DONATION:
|
||||
case eChatMessageType::PRG_CSR_COMMAND:
|
||||
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
|
||||
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
|
||||
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chat_message_type).data(), chat_message_type);
|
||||
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
|
||||
break;
|
||||
default:
|
||||
LOG("Unknown CHAT Message id: %i", chat_message_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) {
|
||||
switch (static_cast<eWorldMessageType>(packet->data[3])) {
|
||||
case eWorldMessageType::ROUTE_PACKET: {
|
||||
LOG("Routing packet from world");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG("Unknown World id: %i", int(packet->data[3]));
|
||||
}
|
||||
LOG("Unknown CHAT Message id: %i", chatMessageID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#include "BitStreamUtils.h"
|
||||
#include "Database.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
#include "eChatMessageType.h"
|
||||
|
||||
void PlayerContainer::Initialize() {
|
||||
m_MaxNumberOfBestFriends =
|
||||
@@ -49,6 +49,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
data.sysAddr = packet->systemAddress;
|
||||
|
||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
m_PlayerCount++;
|
||||
|
||||
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
|
||||
|
||||
@@ -87,6 +88,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
}
|
||||
}
|
||||
|
||||
m_PlayerCount--;
|
||||
LOG("Removed user: %llu", playerID);
|
||||
m_Players.erase(playerID);
|
||||
|
||||
@@ -145,12 +147,12 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||
|
||||
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
|
||||
|
||||
bitStream.Write(player);
|
||||
bitStream.Write(time);
|
||||
|
||||
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||
@@ -352,7 +354,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
||||
|
||||
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::TEAM_GET_STATUS);
|
||||
|
||||
bitStream.Write(team->teamID);
|
||||
bitStream.Write(deleteTeam);
|
||||
@@ -365,7 +367,7 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
}
|
||||
}
|
||||
|
||||
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
|
||||
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
||||
@@ -390,7 +392,7 @@ LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
|
||||
}
|
||||
|
||||
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
|
||||
return m_Players[playerID];
|
||||
return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
|
||||
}
|
||||
|
||||
PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) {
|
||||
|
||||
@@ -71,6 +71,9 @@ public:
|
||||
const PlayerData& GetPlayerData(const std::string& playerName);
|
||||
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
||||
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
||||
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
||||
uint32_t GetSimCount() { return m_SimCount; };
|
||||
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
|
||||
|
||||
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
|
||||
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
|
||||
@@ -93,5 +96,7 @@ private:
|
||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||
uint32_t m_MaxNumberOfFriends = 50;
|
||||
uint32_t m_PlayerCount = 0;
|
||||
uint32_t m_SimCount = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,12 +9,11 @@
|
||||
* AMF3 Deserializer written by EmosewaMC
|
||||
*/
|
||||
|
||||
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
|
||||
if (!inStream) return nullptr;
|
||||
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream& inStream) {
|
||||
AMFBaseValue* returnValue = nullptr;
|
||||
// Read in the value type from the bitStream
|
||||
eAmf marker;
|
||||
inStream->Read(marker);
|
||||
inStream.Read(marker);
|
||||
// Based on the typing, create the value associated with that and return the base value class
|
||||
switch (marker) {
|
||||
case eAmf::Undefined: {
|
||||
@@ -79,13 +78,13 @@ AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream* inStream) {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) {
|
||||
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream& inStream) {
|
||||
bool byteFlag = true;
|
||||
uint32_t actualNumber{};
|
||||
uint8_t numberOfBytesRead{};
|
||||
while (byteFlag && numberOfBytesRead < 4) {
|
||||
uint8_t byte{};
|
||||
inStream->Read(byte);
|
||||
inStream.Read(byte);
|
||||
// Parse the byte
|
||||
if (numberOfBytesRead < 3) {
|
||||
byteFlag = byte & static_cast<uint8_t>(1 << 7);
|
||||
@@ -101,7 +100,7 @@ uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) {
|
||||
return actualNumber;
|
||||
}
|
||||
|
||||
const std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||
const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
|
||||
auto length = ReadU29(inStream);
|
||||
// Check if this is a reference
|
||||
bool isReference = length % 2 == 1;
|
||||
@@ -109,7 +108,7 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||
length = length >> 1;
|
||||
if (isReference) {
|
||||
std::string value(length, 0);
|
||||
inStream->Read(&value[0], length);
|
||||
inStream.Read(&value[0], length);
|
||||
// Empty strings are never sent by reference
|
||||
if (!value.empty()) accessedElements.push_back(value);
|
||||
return value;
|
||||
@@ -119,20 +118,20 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
|
||||
}
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) {
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
|
||||
double value;
|
||||
inStream->Read<double>(value);
|
||||
inStream.Read<double>(value);
|
||||
return new AMFDoubleValue(value);
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
|
||||
auto arrayValue = new AMFArrayValue();
|
||||
|
||||
// Read size of dense array
|
||||
auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
|
||||
const auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
|
||||
// Then read associative portion
|
||||
while (true) {
|
||||
auto key = ReadString(inStream);
|
||||
const auto key = ReadString(inStream);
|
||||
// No more associative values when we encounter an empty string key
|
||||
if (key.size() == 0) break;
|
||||
arrayValue->Insert(key, Read(inStream));
|
||||
@@ -144,10 +143,10 @@ AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) {
|
||||
return arrayValue;
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) {
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
|
||||
return new AMFStringValue(ReadString(inStream));
|
||||
}
|
||||
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) {
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
|
||||
return new AMFIntValue(ReadU29(inStream));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
* @param inStream inStream to read value from.
|
||||
* @return Returns an AMFValue with all the information from the bitStream in it.
|
||||
*/
|
||||
AMFBaseValue* Read(RakNet::BitStream* inStream);
|
||||
AMFBaseValue* Read(RakNet::BitStream& inStream);
|
||||
private:
|
||||
/**
|
||||
* @brief Private method to read a U29 integer from a bitstream
|
||||
@@ -23,7 +23,7 @@ private:
|
||||
* @param inStream bitstream to read data from
|
||||
* @return The number as an unsigned 29 bit integer
|
||||
*/
|
||||
uint32_t ReadU29(RakNet::BitStream* inStream);
|
||||
static uint32_t ReadU29(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* @brief Reads a string from a bitstream
|
||||
@@ -31,7 +31,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return The read string
|
||||
*/
|
||||
const std::string ReadString(RakNet::BitStream* inStream);
|
||||
const std::string ReadString(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFDouble value from a bitStream
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Double value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfDouble(RakNet::BitStream* inStream);
|
||||
AMFBaseValue* ReadAmfDouble(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFArray from a bitStream
|
||||
@@ -47,7 +47,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Array value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfArray(RakNet::BitStream* inStream);
|
||||
AMFBaseValue* ReadAmfArray(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFString from a bitStream
|
||||
@@ -55,7 +55,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return String value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfString(RakNet::BitStream* inStream);
|
||||
AMFBaseValue* ReadAmfString(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* @brief Read an AMFInteger from a bitStream
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Integer value represented as an AMFValue
|
||||
*/
|
||||
AMFBaseValue* ReadAmfInteger(RakNet::BitStream* inStream);
|
||||
AMFBaseValue* ReadAmfInteger(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* List of strings read so far saved to be read by reference.
|
||||
|
||||
@@ -42,6 +42,7 @@ class AMFValue : public AMFBaseValue {
|
||||
public:
|
||||
AMFValue() = default;
|
||||
AMFValue(const ValueType value) : m_Data{ value } {}
|
||||
|
||||
virtual ~AMFValue() override = default;
|
||||
|
||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override;
|
||||
|
||||
@@ -53,7 +53,7 @@ void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
|
||||
* A private function to write an value to a RakNet::BitStream
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
void WriteUInt29(RakNet::BitStream& bs, uint32_t v) {
|
||||
unsigned char b4 = static_cast<unsigned char>(v);
|
||||
if (v < 0x00200000) {
|
||||
b4 = b4 & 0x7F;
|
||||
@@ -65,10 +65,10 @@ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
unsigned char b2;
|
||||
v = v >> 7;
|
||||
b2 = static_cast<unsigned char>(v) | 0x80;
|
||||
bs->Write(b2);
|
||||
bs.Write(b2);
|
||||
}
|
||||
|
||||
bs->Write(b3);
|
||||
bs.Write(b3);
|
||||
}
|
||||
} else {
|
||||
unsigned char b1;
|
||||
@@ -82,19 +82,19 @@ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
|
||||
v = v >> 7;
|
||||
b1 = static_cast<unsigned char>(v) | 0x80;
|
||||
|
||||
bs->Write(b1);
|
||||
bs->Write(b2);
|
||||
bs->Write(b3);
|
||||
bs.Write(b1);
|
||||
bs.Write(b2);
|
||||
bs.Write(b3);
|
||||
}
|
||||
|
||||
bs->Write(b4);
|
||||
bs.Write(b4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a flag number to a RakNet::BitStream
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||
void WriteFlagNumber(RakNet::BitStream& bs, uint32_t v) {
|
||||
v = (v << 1) | 0x01;
|
||||
WriteUInt29(bs, v);
|
||||
}
|
||||
@@ -104,9 +104,9 @@ void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
|
||||
void WriteAMFString(RakNet::BitStream& bs, const std::string& str) {
|
||||
WriteFlagNumber(bs, static_cast<uint32_t>(str.size()));
|
||||
bs->Write(str.c_str(), static_cast<uint32_t>(str.size()));
|
||||
bs.Write(str.c_str(), static_cast<uint32_t>(str.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,8 +114,8 @@ void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
|
||||
bs->Write(value);
|
||||
void WriteAMFU16(RakNet::BitStream& bs, uint16_t value) {
|
||||
bs.Write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,8 +123,8 @@ void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
|
||||
bs->Write(value);
|
||||
void WriteAMFU32(RakNet::BitStream& bs, uint32_t value) {
|
||||
bs.Write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,40 +132,40 @@ void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
|
||||
*
|
||||
* RakNet writes in the correct byte order - do not reverse this.
|
||||
*/
|
||||
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
|
||||
bs->Write(value);
|
||||
void WriteAMFU64(RakNet::BitStream& bs, uint64_t value) {
|
||||
bs.Write(value);
|
||||
}
|
||||
|
||||
// Writes an AMFIntegerValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value) {
|
||||
WriteUInt29(this, value.GetValue());
|
||||
WriteUInt29(*this, value.GetValue());
|
||||
}
|
||||
|
||||
// Writes an AMFDoubleValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value) {
|
||||
double d = value.GetValue();
|
||||
WriteAMFU64(this, *reinterpret_cast<uint64_t*>(&d));
|
||||
WriteAMFU64(*this, *reinterpret_cast<uint64_t*>(&d));
|
||||
}
|
||||
|
||||
// Writes an AMFStringValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value) {
|
||||
WriteAMFString(this, value.GetValue());
|
||||
WriteAMFString(*this, value.GetValue());
|
||||
}
|
||||
|
||||
// Writes an AMFArrayValue to BitStream
|
||||
template<>
|
||||
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value) {
|
||||
uint32_t denseSize = value.GetDense().size();
|
||||
WriteFlagNumber(this, denseSize);
|
||||
WriteFlagNumber(*this, denseSize);
|
||||
|
||||
auto it = value.GetAssociative().begin();
|
||||
auto end = value.GetAssociative().end();
|
||||
|
||||
while (it != end) {
|
||||
WriteAMFString(this, it->first);
|
||||
WriteAMFString(*this, it->first);
|
||||
this->Write<AMFBaseValue&>(*it->second);
|
||||
it++;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,15 @@ foreach(file ${DCOMMON_DCLIENT_SOURCES})
|
||||
set(DCOMMON_SOURCES ${DCOMMON_SOURCES} "dClient/${file}")
|
||||
endforeach()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
|
||||
|
||||
add_library(dCommon STATIC ${DCOMMON_SOURCES})
|
||||
|
||||
target_link_libraries(dCommon bcrypt dDatabase tinyxml2)
|
||||
target_include_directories(dCommon
|
||||
PUBLIC "." "dClient" "dEnums"
|
||||
PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -65,4 +69,6 @@ else ()
|
||||
)
|
||||
endif ()
|
||||
|
||||
target_link_libraries(dCommon ZLIB::ZLIB)
|
||||
target_link_libraries(dCommon
|
||||
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
|
||||
INTERFACE dDatabase)
|
||||
|
||||
@@ -120,6 +120,8 @@ void CatchUnhandled(int sig) {
|
||||
if (eptr) std::rethrow_exception(eptr);
|
||||
} catch(const std::exception& e) {
|
||||
LOG("Caught exception: '%s'", e.what());
|
||||
} catch (...) {
|
||||
LOG("Caught unknown exception.");
|
||||
}
|
||||
|
||||
#ifndef INCLUDE_BACKTRACE
|
||||
|
||||
@@ -8,23 +8,23 @@
|
||||
#include <map>
|
||||
|
||||
template <typename T>
|
||||
inline size_t MinSize(size_t size, const std::basic_string_view<T>& string) {
|
||||
if (size == size_t(-1) || size > string.size()) {
|
||||
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
|
||||
if (size == SIZE_MAX || size > string.size()) {
|
||||
return string.size();
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsLeadSurrogate(char16_t c) {
|
||||
inline bool IsLeadSurrogate(const char16_t c) {
|
||||
return (0xD800 <= c) && (c <= 0xDBFF);
|
||||
}
|
||||
|
||||
inline bool IsTrailSurrogate(char16_t c) {
|
||||
inline bool IsTrailSurrogate(const char16_t c) {
|
||||
return (0xDC00 <= c) && (c <= 0xDFFF);
|
||||
}
|
||||
|
||||
inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
|
||||
inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
|
||||
if (cp <= 0x007F) {
|
||||
ret.push_back(static_cast<uint8_t>(cp));
|
||||
} else if (cp <= 0x07FF) {
|
||||
@@ -46,16 +46,16 @@ inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
|
||||
|
||||
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
|
||||
|
||||
bool _IsSuffixChar(uint8_t c) {
|
||||
bool static _IsSuffixChar(const uint8_t c) {
|
||||
return (c & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
size_t rem = slice.length();
|
||||
bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
const size_t rem = slice.length();
|
||||
if (slice.empty()) return false;
|
||||
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
|
||||
if (rem > 0) {
|
||||
uint8_t first = bytes[0];
|
||||
const uint8_t first = bytes[0];
|
||||
if (first < 0x80) { // 1 byte character
|
||||
out = static_cast<uint32_t>(first & 0x7F);
|
||||
slice.remove_prefix(1);
|
||||
@@ -64,7 +64,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
// middle byte, not valid at start, fall through
|
||||
} else if (first < 0xE0) { // two byte character
|
||||
if (rem > 1) {
|
||||
uint8_t second = bytes[1];
|
||||
const uint8_t second = bytes[1];
|
||||
if (_IsSuffixChar(second)) {
|
||||
out = (static_cast<uint32_t>(first & 0x1F) << 6)
|
||||
+ static_cast<uint32_t>(second & 0x3F);
|
||||
@@ -74,8 +74,8 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
}
|
||||
} else if (first < 0xF0) { // three byte character
|
||||
if (rem > 2) {
|
||||
uint8_t second = bytes[1];
|
||||
uint8_t third = bytes[2];
|
||||
const uint8_t second = bytes[1];
|
||||
const uint8_t third = bytes[2];
|
||||
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
|
||||
out = (static_cast<uint32_t>(first & 0x0F) << 12)
|
||||
+ (static_cast<uint32_t>(second & 0x3F) << 6)
|
||||
@@ -86,9 +86,9 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
}
|
||||
} else if (first < 0xF8) { // four byte character
|
||||
if (rem > 3) {
|
||||
uint8_t second = bytes[1];
|
||||
uint8_t third = bytes[2];
|
||||
uint8_t fourth = bytes[3];
|
||||
const uint8_t second = bytes[1];
|
||||
const uint8_t third = bytes[2];
|
||||
const uint8_t fourth = bytes[3];
|
||||
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
|
||||
out = (static_cast<uint32_t>(first & 0x07) << 18)
|
||||
+ (static_cast<uint32_t>(second & 0x3F) << 12)
|
||||
@@ -107,7 +107,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
|
||||
}
|
||||
|
||||
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
|
||||
bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
||||
bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t size) {
|
||||
if (output.length() >= size) return false;
|
||||
if (U < 0x10000) {
|
||||
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
|
||||
@@ -120,7 +120,7 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
||||
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
|
||||
// U' must be less than or equal to 0xFFFFF. That is, U' can be
|
||||
// represented in 20 bits.
|
||||
uint32_t Ut = U - 0x10000;
|
||||
const uint32_t Ut = U - 0x10000;
|
||||
|
||||
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
|
||||
// 0xDC00, respectively. These integers each have 10 bits free to
|
||||
@@ -141,25 +141,25 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
|
||||
} else return false;
|
||||
}
|
||||
|
||||
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view& string, size_t size) {
|
||||
size_t newSize = MinSize(size, string);
|
||||
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view string, const size_t size) {
|
||||
const size_t newSize = MinSize(size, string);
|
||||
std::u16string output;
|
||||
output.reserve(newSize);
|
||||
std::string_view iterator = string;
|
||||
|
||||
uint32_t c;
|
||||
while (_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
|
||||
while (details::_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
|
||||
return output;
|
||||
}
|
||||
|
||||
//! Converts an std::string (ASCII) to UCS-2 / UTF-16
|
||||
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t size) {
|
||||
size_t newSize = MinSize(size, string);
|
||||
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const size_t size) {
|
||||
const size_t newSize = MinSize(size, string);
|
||||
std::u16string ret;
|
||||
ret.reserve(newSize);
|
||||
|
||||
for (size_t i = 0; i < newSize; i++) {
|
||||
char c = string[i];
|
||||
for (size_t i = 0; i < newSize; ++i) {
|
||||
const char c = string[i];
|
||||
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
|
||||
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
|
||||
}
|
||||
@@ -169,18 +169,18 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t
|
||||
|
||||
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
|
||||
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
|
||||
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t size) {
|
||||
size_t newSize = MinSize(size, string);
|
||||
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
|
||||
const size_t newSize = MinSize(size, string);
|
||||
std::string ret;
|
||||
ret.reserve(newSize);
|
||||
|
||||
for (size_t i = 0; i < newSize; i++) {
|
||||
char16_t u = string[i];
|
||||
for (size_t i = 0; i < newSize; ++i) {
|
||||
const char16_t u = string[i];
|
||||
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
|
||||
char16_t next = string[i + 1];
|
||||
const char16_t next = string[i + 1];
|
||||
if (IsTrailSurrogate(next)) {
|
||||
i += 1;
|
||||
char32_t cp = 0x10000
|
||||
const char32_t cp = 0x10000
|
||||
+ ((static_cast<char32_t>(u) - 0xD800) << 10)
|
||||
+ (static_cast<char32_t>(next) - 0xDC00);
|
||||
PushUTF8CodePoint(ret, cp);
|
||||
@@ -195,40 +195,40 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string& a, const std::string& b) {
|
||||
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b) {
|
||||
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
|
||||
}
|
||||
|
||||
// MARK: Bits
|
||||
|
||||
//! Sets a specific bit in a signed 64-bit integer
|
||||
int64_t GeneralUtils::SetBit(int64_t value, uint32_t index) {
|
||||
int64_t GeneralUtils::SetBit(int64_t value, const uint32_t index) {
|
||||
return value |= 1ULL << index;
|
||||
}
|
||||
|
||||
//! Clears a specific bit in a signed 64-bit integer
|
||||
int64_t GeneralUtils::ClearBit(int64_t value, uint32_t index) {
|
||||
int64_t GeneralUtils::ClearBit(int64_t value, const uint32_t index) {
|
||||
return value &= ~(1ULL << index);
|
||||
}
|
||||
|
||||
//! Checks a specific bit in a signed 64-bit integer
|
||||
bool GeneralUtils::CheckBit(int64_t value, uint32_t index) {
|
||||
bool GeneralUtils::CheckBit(int64_t value, const uint32_t index) {
|
||||
return value & (1ULL << index);
|
||||
}
|
||||
|
||||
bool GeneralUtils::ReplaceInString(std::string& str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = str.find(from);
|
||||
bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from, const std::string_view to) {
|
||||
const size_t start_pos = str.find(from);
|
||||
if (start_pos == std::string::npos)
|
||||
return false;
|
||||
str.replace(start_pos, from.length(), to);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t delimiter) {
|
||||
std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
|
||||
std::vector<std::wstring> vector = std::vector<std::wstring>();
|
||||
std::wstring current;
|
||||
|
||||
for (const auto& c : str) {
|
||||
for (const wchar_t c : str) {
|
||||
if (c == delimiter) {
|
||||
vector.push_back(current);
|
||||
current = L"";
|
||||
@@ -237,15 +237,15 @@ std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t d
|
||||
}
|
||||
}
|
||||
|
||||
vector.push_back(current);
|
||||
vector.push_back(std::move(current));
|
||||
return vector;
|
||||
}
|
||||
|
||||
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str, char16_t delimiter) {
|
||||
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
|
||||
std::vector<std::u16string> vector = std::vector<std::u16string>();
|
||||
std::u16string current;
|
||||
|
||||
for (const auto& c : str) {
|
||||
for (const char16_t c : str) {
|
||||
if (c == delimiter) {
|
||||
vector.push_back(current);
|
||||
current = u"";
|
||||
@@ -254,17 +254,15 @@ std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str,
|
||||
}
|
||||
}
|
||||
|
||||
vector.push_back(current);
|
||||
vector.push_back(std::move(current));
|
||||
return vector;
|
||||
}
|
||||
|
||||
std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char delimiter) {
|
||||
std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
|
||||
std::vector<std::string> vector = std::vector<std::string>();
|
||||
std::string current = "";
|
||||
|
||||
for (size_t i = 0; i < str.length(); i++) {
|
||||
char c = str[i];
|
||||
|
||||
for (const char c : str) {
|
||||
if (c == delimiter) {
|
||||
vector.push_back(current);
|
||||
current = "";
|
||||
@@ -273,54 +271,53 @@ std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char
|
||||
}
|
||||
}
|
||||
|
||||
vector.push_back(current);
|
||||
|
||||
vector.push_back(std::move(current));
|
||||
return vector;
|
||||
}
|
||||
|
||||
std::u16string GeneralUtils::ReadWString(RakNet::BitStream* inStream) {
|
||||
std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
|
||||
uint32_t length;
|
||||
inStream->Read<uint32_t>(length);
|
||||
inStream.Read<uint32_t>(length);
|
||||
|
||||
std::u16string string;
|
||||
for (auto i = 0; i < length; i++) {
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
uint16_t c;
|
||||
inStream->Read(c);
|
||||
inStream.Read(c);
|
||||
string.push_back(c);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) {
|
||||
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
|
||||
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
|
||||
std::map<uint32_t, std::string> filenames{};
|
||||
for (auto& t : std::filesystem::directory_iterator(folder)) {
|
||||
auto filename = t.path().filename().string();
|
||||
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
||||
filenames.insert(std::make_pair(index, filename));
|
||||
std::map<uint32_t, std::string> filenames{};
|
||||
for (const auto& t : std::filesystem::directory_iterator(folder)) {
|
||||
auto filename = t.path().filename().string();
|
||||
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
|
||||
filenames.emplace(index, std::move(filename));
|
||||
}
|
||||
|
||||
// Now sort the map by the oldest migration.
|
||||
std::vector<std::string> sortedFiles{};
|
||||
auto fileIterator = filenames.begin();
|
||||
std::map<uint32_t, std::string>::iterator oldest = filenames.begin();
|
||||
auto fileIterator = filenames.cbegin();
|
||||
auto oldest = filenames.cbegin();
|
||||
while (!filenames.empty()) {
|
||||
if (fileIterator == filenames.end()) {
|
||||
if (fileIterator == filenames.cend()) {
|
||||
sortedFiles.push_back(oldest->second);
|
||||
filenames.erase(oldest);
|
||||
fileIterator = filenames.begin();
|
||||
oldest = filenames.begin();
|
||||
fileIterator = filenames.cbegin();
|
||||
oldest = filenames.cbegin();
|
||||
continue;
|
||||
}
|
||||
if (oldest->first > fileIterator->first) oldest = fileIterator;
|
||||
fileIterator++;
|
||||
++fileIterator;
|
||||
}
|
||||
|
||||
return sortedFiles;
|
||||
}
|
||||
|
||||
#ifdef DARKFLAME_PLATFORM_MACOS
|
||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||
|
||||
// MacOS floating-point parse function specializations
|
||||
namespace GeneralUtils::details {
|
||||
|
||||
@@ -3,17 +3,18 @@
|
||||
// C++
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "BitStream.h"
|
||||
#include "NiPoint3.h"
|
||||
|
||||
#include "dPlatforms.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
@@ -32,29 +33,31 @@ namespace GeneralUtils {
|
||||
//! Converts a plain ASCII string to a UTF-16 string
|
||||
/*!
|
||||
\param string The string to convert
|
||||
\param size A size to trim the string to. Default is -1 (No trimming)
|
||||
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
||||
\return An UTF-16 representation of the string
|
||||
*/
|
||||
std::u16string ASCIIToUTF16(const std::string_view& string, size_t size = -1);
|
||||
std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
||||
|
||||
//! Converts a UTF-8 String to a UTF-16 string
|
||||
/*!
|
||||
\param string The string to convert
|
||||
\param size A size to trim the string to. Default is -1 (No trimming)
|
||||
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
||||
\return An UTF-16 representation of the string
|
||||
*/
|
||||
std::u16string UTF8ToUTF16(const std::string_view& string, size_t size = -1);
|
||||
std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
|
||||
|
||||
//! Internal, do not use
|
||||
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
||||
namespace details {
|
||||
//! Internal, do not use
|
||||
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
|
||||
}
|
||||
|
||||
//! Converts a UTF-16 string to a UTF-8 string
|
||||
/*!
|
||||
\param string The string to convert
|
||||
\param size A size to trim the string to. Default is -1 (No trimming)
|
||||
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
|
||||
\return An UTF-8 representation of the string
|
||||
*/
|
||||
std::string UTF16ToWTF8(const std::u16string_view& string, size_t size = -1);
|
||||
std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
|
||||
|
||||
/**
|
||||
* Compares two basic strings but does so ignoring case sensitivity
|
||||
@@ -62,7 +65,7 @@ namespace GeneralUtils {
|
||||
* \param b the second string to compare against the first string
|
||||
* @return if the two strings are equal
|
||||
*/
|
||||
bool CaseInsensitiveStringCompare(const std::string& a, const std::string& b);
|
||||
bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
|
||||
|
||||
// MARK: Bits
|
||||
|
||||
@@ -70,9 +73,9 @@ namespace GeneralUtils {
|
||||
|
||||
//! Sets a bit on a numerical value
|
||||
template <typename T>
|
||||
inline void SetBit(T& value, eObjectBits bits) {
|
||||
inline void SetBit(T& value, const eObjectBits bits) {
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
auto index = static_cast<size_t>(bits);
|
||||
const auto index = static_cast<size_t>(bits);
|
||||
if (index > (sizeof(T) * 8) - 1) {
|
||||
return;
|
||||
}
|
||||
@@ -82,9 +85,9 @@ namespace GeneralUtils {
|
||||
|
||||
//! Clears a bit on a numerical value
|
||||
template <typename T>
|
||||
inline void ClearBit(T& value, eObjectBits bits) {
|
||||
inline void ClearBit(T& value, const eObjectBits bits) {
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
auto index = static_cast<size_t>(bits);
|
||||
const auto index = static_cast<size_t>(bits);
|
||||
if (index > (sizeof(T) * 8 - 1)) {
|
||||
return;
|
||||
}
|
||||
@@ -97,14 +100,14 @@ namespace GeneralUtils {
|
||||
\param value The value to set the bit for
|
||||
\param index The index of the bit
|
||||
*/
|
||||
int64_t SetBit(int64_t value, uint32_t index);
|
||||
int64_t SetBit(int64_t value, const uint32_t index);
|
||||
|
||||
//! Clears a specific bit in a signed 64-bit integer
|
||||
/*!
|
||||
\param value The value to clear the bit from
|
||||
\param index The index of the bit
|
||||
*/
|
||||
int64_t ClearBit(int64_t value, uint32_t index);
|
||||
int64_t ClearBit(int64_t value, const uint32_t index);
|
||||
|
||||
//! Checks a specific bit in a signed 64-bit integer
|
||||
/*!
|
||||
@@ -112,19 +115,19 @@ namespace GeneralUtils {
|
||||
\param index The index of the bit
|
||||
\return Whether or not the bit is set
|
||||
*/
|
||||
bool CheckBit(int64_t value, uint32_t index);
|
||||
bool CheckBit(int64_t value, const uint32_t index);
|
||||
|
||||
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
|
||||
bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
|
||||
|
||||
std::u16string ReadWString(RakNet::BitStream* inStream);
|
||||
std::u16string ReadWString(RakNet::BitStream& inStream);
|
||||
|
||||
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter);
|
||||
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
|
||||
|
||||
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter);
|
||||
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
|
||||
|
||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
|
||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
|
||||
|
||||
// Concept constraining to enum types
|
||||
template <typename T>
|
||||
@@ -144,7 +147,7 @@ namespace GeneralUtils {
|
||||
|
||||
// If a boolean, present an alias to an intermediate integral type for parsing
|
||||
template <Numeric T> requires std::same_as<T, bool>
|
||||
struct numeric_parse<T> { using type = uint32_t; };
|
||||
struct numeric_parse<T> { using type = uint8_t; };
|
||||
|
||||
// Shorthand type alias
|
||||
template <Numeric T>
|
||||
@@ -156,8 +159,9 @@ namespace GeneralUtils {
|
||||
* @returns An std::optional containing the desired value if it is equivalent to the string
|
||||
*/
|
||||
template <Numeric T>
|
||||
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) {
|
||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) {
|
||||
numeric_parse_t<T> result;
|
||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
||||
|
||||
const char* const strEnd = str.data() + str.size();
|
||||
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
|
||||
@@ -166,7 +170,7 @@ namespace GeneralUtils {
|
||||
return isParsed ? static_cast<T>(result) : std::optional<T>{};
|
||||
}
|
||||
|
||||
#ifdef DARKFLAME_PLATFORM_MACOS
|
||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||
|
||||
// MacOS floating-point parse helper function specializations
|
||||
namespace details {
|
||||
@@ -181,8 +185,10 @@ namespace GeneralUtils {
|
||||
* @returns An std::optional containing the desired value if it is equivalent to the string
|
||||
*/
|
||||
template <std::floating_point T>
|
||||
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) noexcept
|
||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
||||
try {
|
||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
||||
|
||||
size_t parseNum;
|
||||
const T result = details::_parse<T>(str, parseNum);
|
||||
const bool isParsed = str.length() == parseNum;
|
||||
@@ -202,7 +208,7 @@ namespace GeneralUtils {
|
||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string& strX, const std::string& strY, const std::string& strZ) {
|
||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
|
||||
const auto x = TryParse<float>(strX);
|
||||
if (!x) return std::nullopt;
|
||||
|
||||
@@ -214,17 +220,17 @@ namespace GeneralUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* The TryParse overload for handling NiPoint3 by passingn a reference to a vector of three strings
|
||||
* @param str The string vector representing the X, Y, and Xcoordinates
|
||||
* The TryParse overload for handling NiPoint3 by passing a span of three strings
|
||||
* @param str The string vector representing the X, Y, and Z coordinates
|
||||
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::vector<std::string>& str) {
|
||||
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
|
||||
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::u16string to_u16string(T value) {
|
||||
std::u16string to_u16string(const T value) {
|
||||
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
|
||||
}
|
||||
|
||||
@@ -243,7 +249,7 @@ namespace GeneralUtils {
|
||||
\param max The maximum to generate to
|
||||
*/
|
||||
template <typename T>
|
||||
inline T GenerateRandomNumber(std::size_t min, std::size_t max) {
|
||||
inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
|
||||
// Make sure it is a numeric type
|
||||
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||
|
||||
@@ -264,16 +270,16 @@ namespace GeneralUtils {
|
||||
* @returns The enum entry's value in its underlying type
|
||||
*/
|
||||
template <Enum eType>
|
||||
constexpr typename std::underlying_type_t<eType> CastUnderlyingType(const eType entry) noexcept {
|
||||
return static_cast<typename std::underlying_type_t<eType>>(entry);
|
||||
constexpr std::underlying_type_t<eType> ToUnderlying(const eType entry) noexcept {
|
||||
return static_cast<std::underlying_type_t<eType>>(entry);
|
||||
}
|
||||
|
||||
// on Windows we need to undef these or else they conflict with our numeric limits calls
|
||||
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline T GenerateRandomNumber() {
|
||||
|
||||
92
dCommon/Implementation.h
Normal file
92
dCommon/Implementation.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef __IMPLEMENTATION_H__
|
||||
#define __IMPLEMENTATION_H__
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
/**
|
||||
* @brief A way to defer the implementation of an action.
|
||||
*
|
||||
* @tparam R The result of the action.
|
||||
* @tparam T The types of the arguments that the implementation requires.
|
||||
*/
|
||||
template <typename R, typename... T>
|
||||
class Implementation {
|
||||
public:
|
||||
typedef std::function<std::optional<R>(T...)> ImplementationFunction;
|
||||
|
||||
/**
|
||||
* @brief Sets the implementation of the action.
|
||||
*
|
||||
* @param implementation The implementation of the action.
|
||||
*/
|
||||
void SetImplementation(const ImplementationFunction& implementation) {
|
||||
this->implementation = implementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears the implementation of the action.
|
||||
*/
|
||||
void ClearImplementation() {
|
||||
implementation.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the implementation is set.
|
||||
*
|
||||
* @return true If the implementation is set.
|
||||
* @return false If the implementation is not set.
|
||||
*/
|
||||
bool IsSet() const {
|
||||
return implementation.has_value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Executes the implementation if it is set.
|
||||
*
|
||||
* @param args The arguments to pass to the implementation.
|
||||
* @return std::optional<R> The optional result of the implementation. If the result is not set, it indicates that the default action should be taken.
|
||||
*/
|
||||
std::optional<R> Execute(T... args) const {
|
||||
return IsSet() ? implementation.value()(args...) : std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exectues the implementation if it is set, otherwise returns a default value.
|
||||
*
|
||||
* @param args The arguments to pass to the implementation.
|
||||
* @param defaultValue The default value to return if the implementation is not set or should not be deferred.
|
||||
*/
|
||||
R ExecuteWithDefault(T... args, const R& defaultValue) const {
|
||||
return Execute(args...).value_or(defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* = operator overload.
|
||||
*/
|
||||
Implementation& operator=(const Implementation& other) {
|
||||
implementation = other.implementation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* = operator overload.
|
||||
*/
|
||||
Implementation& operator=(const ImplementationFunction& implementation) {
|
||||
this->implementation = implementation;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* () operator overload.
|
||||
*/
|
||||
std::optional<R> operator()(T... args) {
|
||||
return !IsSet() ? std::nullopt : implementation(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<ImplementationFunction> implementation;
|
||||
|
||||
};
|
||||
|
||||
#endif //!__IMPLEMENTATION_H__
|
||||
@@ -31,22 +31,22 @@ public:
|
||||
|
||||
virtual ~LDFBaseData() {}
|
||||
|
||||
virtual void WriteToPacket(RakNet::BitStream* packet) = 0;
|
||||
virtual void WriteToPacket(RakNet::BitStream& packet) const = 0;
|
||||
|
||||
virtual const std::u16string& GetKey() = 0;
|
||||
virtual const std::u16string& GetKey() const = 0;
|
||||
|
||||
virtual eLDFType GetValueType() = 0;
|
||||
virtual eLDFType GetValueType() const = 0;
|
||||
|
||||
/** Gets a string from the key/value pair
|
||||
* @param includeKey Whether or not to include the key in the data
|
||||
* @param includeTypeId Whether or not to include the type id in the data
|
||||
* @return The string representation of the data
|
||||
*/
|
||||
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0;
|
||||
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0;
|
||||
|
||||
virtual std::string GetValueAsString() = 0;
|
||||
virtual std::string GetValueAsString() const = 0;
|
||||
|
||||
virtual LDFBaseData* Copy() = 0;
|
||||
virtual LDFBaseData* Copy() const = 0;
|
||||
|
||||
/**
|
||||
* Given an input string, return the data as a LDF key.
|
||||
@@ -62,17 +62,17 @@ private:
|
||||
T value;
|
||||
|
||||
//! Writes the key to the packet
|
||||
void WriteKey(RakNet::BitStream* packet) {
|
||||
packet->Write<uint8_t>(this->key.length() * sizeof(uint16_t));
|
||||
void WriteKey(RakNet::BitStream& packet) const {
|
||||
packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
|
||||
for (uint32_t i = 0; i < this->key.length(); ++i) {
|
||||
packet->Write<uint16_t>(this->key[i]);
|
||||
packet.Write<uint16_t>(this->key[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//! Writes the value to the packet
|
||||
void WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
packet->Write(this->value);
|
||||
void WriteValue(RakNet::BitStream& packet) const {
|
||||
packet.Write<uint8_t>(this->GetValueType());
|
||||
packet.Write(this->value);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
/*!
|
||||
\return The value
|
||||
*/
|
||||
const T& GetValue(void) { return this->value; }
|
||||
const T& GetValue(void) const { return this->value; }
|
||||
|
||||
//! Sets the value
|
||||
/*!
|
||||
@@ -102,13 +102,13 @@ public:
|
||||
/*!
|
||||
\return The value string
|
||||
*/
|
||||
std::string GetValueString(void) { return ""; }
|
||||
std::string GetValueString(void) const { return ""; }
|
||||
|
||||
//! Writes the data to a packet
|
||||
/*!
|
||||
\param packet The packet
|
||||
*/
|
||||
void WriteToPacket(RakNet::BitStream* packet) override {
|
||||
void WriteToPacket(RakNet::BitStream& packet) const override {
|
||||
this->WriteKey(packet);
|
||||
this->WriteValue(packet);
|
||||
}
|
||||
@@ -117,13 +117,13 @@ public:
|
||||
/*!
|
||||
\return The key
|
||||
*/
|
||||
const std::u16string& GetKey(void) override { return this->key; }
|
||||
const std::u16string& GetKey(void) const override { return this->key; }
|
||||
|
||||
//! Gets the LDF Type
|
||||
/*!
|
||||
\return The LDF value type
|
||||
*/
|
||||
eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; }
|
||||
eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; }
|
||||
|
||||
//! Gets the string data
|
||||
/*!
|
||||
@@ -131,7 +131,7 @@ public:
|
||||
\param includeTypeId Whether or not to include the type id in the data
|
||||
\return The string representation of the data
|
||||
*/
|
||||
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override {
|
||||
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override {
|
||||
if (GetValueType() == -1) {
|
||||
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
|
||||
}
|
||||
@@ -154,11 +154,11 @@ public:
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string GetValueAsString() override {
|
||||
std::string GetValueAsString() const override {
|
||||
return this->GetValueString();
|
||||
}
|
||||
|
||||
LDFBaseData* Copy() override {
|
||||
LDFBaseData* Copy() const override {
|
||||
return new LDFData<T>(key, value);
|
||||
}
|
||||
|
||||
@@ -166,58 +166,58 @@ public:
|
||||
};
|
||||
|
||||
// LDF Types
|
||||
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; };
|
||||
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; };
|
||||
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; };
|
||||
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; };
|
||||
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; };
|
||||
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; };
|
||||
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; };
|
||||
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; };
|
||||
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; };
|
||||
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
|
||||
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
|
||||
template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
|
||||
template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
|
||||
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
|
||||
template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
|
||||
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
|
||||
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
|
||||
template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
|
||||
|
||||
// The specialized version for std::u16string (UTF-16)
|
||||
template<>
|
||||
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const {
|
||||
packet.Write<uint8_t>(this->GetValueType());
|
||||
|
||||
packet->Write<uint32_t>(this->value.length());
|
||||
packet.Write<uint32_t>(this->value.length());
|
||||
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
||||
packet->Write<uint16_t>(this->value[i]);
|
||||
packet.Write<uint16_t>(this->value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// The specialized version for bool
|
||||
template<>
|
||||
inline void LDFData<bool>::WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
|
||||
packet.Write<uint8_t>(this->GetValueType());
|
||||
|
||||
packet->Write<uint8_t>(this->value);
|
||||
packet.Write<uint8_t>(this->value);
|
||||
}
|
||||
|
||||
// The specialized version for std::string (UTF-8)
|
||||
template<>
|
||||
inline void LDFData<std::string>::WriteValue(RakNet::BitStream* packet) {
|
||||
packet->Write<uint8_t>(this->GetValueType());
|
||||
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
|
||||
packet.Write<uint8_t>(this->GetValueType());
|
||||
|
||||
packet->Write<uint32_t>(this->value.length());
|
||||
packet.Write<uint32_t>(this->value.length());
|
||||
for (uint32_t i = 0; i < this->value.length(); ++i) {
|
||||
packet->Write<uint8_t>(this->value[i]);
|
||||
packet.Write<uint8_t>(this->value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template<> inline std::string LDFData<std::u16string>::GetValueString() {
|
||||
template<> inline std::string LDFData<std::u16string>::GetValueString() const {
|
||||
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
|
||||
}
|
||||
|
||||
template<> inline std::string LDFData<int32_t>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<float>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<double>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<uint32_t>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<bool>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<uint64_t>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<LWOOBJID>::GetValueString() { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<int32_t>::GetValueString() const { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<float>::GetValueString() const { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<double>::GetValueString() const { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<uint32_t>::GetValueString() const { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<bool>::GetValueString() const { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<uint64_t>::GetValueString() const { return std::to_string(this->value); }
|
||||
template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return std::to_string(this->value); }
|
||||
|
||||
template<> inline std::string LDFData<std::string>::GetValueString() { return this->value; }
|
||||
template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
|
||||
|
||||
#endif //!__LDFFORMAT__H__
|
||||
|
||||
73
dCommon/Observable.h
Normal file
73
dCommon/Observable.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef __OBSERVABLE_H__
|
||||
#define __OBSERVABLE_H__
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* @brief An event which can be observed by multiple observers.
|
||||
*
|
||||
* @tparam T The types of the arguments to be passed to the observers.
|
||||
*/
|
||||
template <typename... T>
|
||||
class Observable {
|
||||
public:
|
||||
typedef std::function<void(T...)> Observer;
|
||||
|
||||
/**
|
||||
* @brief Adds an observer to the event.
|
||||
*
|
||||
* @param observer The observer to add.
|
||||
*/
|
||||
void AddObserver(const Observer& observer) {
|
||||
observers.push_back(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an observer from the event.
|
||||
*
|
||||
* @param observer The observer to remove.
|
||||
*/
|
||||
void RemoveObserver(const Observer& observer) {
|
||||
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Notifies all observers of the event.
|
||||
*
|
||||
* @param args The arguments to pass to the observers.
|
||||
*/
|
||||
void Notify(T... args) {
|
||||
for (const auto& observer : observers) {
|
||||
observer(args...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* += operator overload.
|
||||
*/
|
||||
Observable& operator+=(const Observer& observer) {
|
||||
AddObserver(observer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* -= operator overload.
|
||||
*/
|
||||
Observable& operator-=(const Observer& observer) {
|
||||
RemoveObserver(observer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* () operator overload.
|
||||
*/
|
||||
void operator()(T... args) {
|
||||
Notify(args...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Observer> observers;
|
||||
};
|
||||
|
||||
#endif //!__OBSERVABLE_H__
|
||||
@@ -6,28 +6,14 @@
|
||||
|
||||
|
||||
struct RemoteInputInfo {
|
||||
RemoteInputInfo() {
|
||||
m_RemoteInputX = 0;
|
||||
m_RemoteInputY = 0;
|
||||
m_IsPowersliding = false;
|
||||
m_IsModified = false;
|
||||
}
|
||||
|
||||
void operator=(const RemoteInputInfo& other) {
|
||||
m_RemoteInputX = other.m_RemoteInputX;
|
||||
m_RemoteInputY = other.m_RemoteInputY;
|
||||
m_IsPowersliding = other.m_IsPowersliding;
|
||||
m_IsModified = other.m_IsModified;
|
||||
}
|
||||
|
||||
bool operator==(const RemoteInputInfo& other) {
|
||||
return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified;
|
||||
}
|
||||
|
||||
float m_RemoteInputX;
|
||||
float m_RemoteInputY;
|
||||
bool m_IsPowersliding;
|
||||
bool m_IsModified;
|
||||
float m_RemoteInputX = 0;
|
||||
float m_RemoteInputY = 0;
|
||||
bool m_IsPowersliding = false;
|
||||
bool m_IsModified = false;
|
||||
};
|
||||
|
||||
struct LocalSpaceInfo {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "dConfig.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
@@ -34,8 +34,8 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
|
||||
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
|
||||
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
|
||||
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
|
||||
#define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false);
|
||||
#define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
#define SEND_PACKET Game::server->Send(bitStream, sysAddr, false);
|
||||
#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
|
||||
//=========== TYPEDEFS ==========
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef __ECHATINTERNALMESSAGETYPE__H__
|
||||
#define __ECHATINTERNALMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum eChatInternalMessageType : uint32_t {
|
||||
PLAYER_ADDED_NOTIFICATION = 0,
|
||||
PLAYER_REMOVED_NOTIFICATION,
|
||||
ADD_FRIEND,
|
||||
ADD_BEST_FRIEND,
|
||||
ADD_TO_TEAM,
|
||||
ADD_BLOCK,
|
||||
REMOVE_FRIEND,
|
||||
REMOVE_BLOCK,
|
||||
REMOVE_FROM_TEAM,
|
||||
DELETE_TEAM,
|
||||
REPORT,
|
||||
PRIVATE_CHAT,
|
||||
PRIVATE_CHAT_RESPONSE,
|
||||
ANNOUNCEMENT,
|
||||
MAIL_COUNT_UPDATE,
|
||||
MAIL_SEND_NOTIFY,
|
||||
REQUEST_USER_LIST,
|
||||
FRIEND_LIST,
|
||||
ROUTE_TO_PLAYER,
|
||||
TEAM_UPDATE,
|
||||
MUTE_UPDATE,
|
||||
CREATE_TEAM,
|
||||
};
|
||||
|
||||
#endif //!__ECHATINTERNALMESSAGETYPE__H__
|
||||
@@ -72,7 +72,9 @@ enum class eChatMessageType :uint32_t {
|
||||
UPDATE_DONATION,
|
||||
PRG_CSR_COMMAND,
|
||||
HEARTBEAT_REQUEST_FROM_WORLD,
|
||||
UPDATE_FREE_TRIAL_STATUS
|
||||
UPDATE_FREE_TRIAL_STATUS,
|
||||
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
|
||||
CREATE_TEAM,
|
||||
};
|
||||
|
||||
#endif //!__ECHATMESSAGETYPE__H__
|
||||
|
||||
@@ -5,8 +5,7 @@ enum class eConnectionType : uint16_t {
|
||||
SERVER = 0,
|
||||
AUTH,
|
||||
CHAT,
|
||||
CHAT_INTERNAL,
|
||||
WORLD,
|
||||
WORLD = 4,
|
||||
CLIENT,
|
||||
MASTER
|
||||
};
|
||||
|
||||
@@ -790,9 +790,10 @@ enum class eGameMessageType : uint16_t {
|
||||
GET_MISSION_TYPE_STATES = 853,
|
||||
GET_TIME_PLAYED = 854,
|
||||
SET_MISSION_VIEWED = 855,
|
||||
SLASH_COMMAND_TEXT_FEEDBACK = 856,
|
||||
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 857,
|
||||
HKX_VEHICLE_LOADED = 856,
|
||||
SLASH_COMMAND_TEXT_FEEDBACK = 857,
|
||||
BROADCAST_TEXT_TO_CHATBOX = 858,
|
||||
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 859,
|
||||
OPEN_PROPERTY_MANAGEMENT = 860,
|
||||
OPEN_PROPERTY_VENDOR = 861,
|
||||
VOTE_ON_PROPERTY = 862,
|
||||
|
||||
59
dCommon/dEnums/eWaypointCommandType.h
Normal file
59
dCommon/dEnums/eWaypointCommandType.h
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
#ifndef __EWAYPOINTCOMMANDTYPES__H__
|
||||
#define __EWAYPOINTCOMMANDTYPES__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eWaypointCommandType : uint32_t {
|
||||
INVALID,
|
||||
BOUNCE,
|
||||
STOP,
|
||||
GROUP_EMOTE,
|
||||
SET_VARIABLE,
|
||||
CAST_SKILL,
|
||||
EQUIP_INVENTORY,
|
||||
UNEQUIP_INVENTORY,
|
||||
DELAY,
|
||||
EMOTE,
|
||||
TELEPORT,
|
||||
PATH_SPEED,
|
||||
REMOVE_NPC,
|
||||
CHANGE_WAYPOINT,
|
||||
DELETE_SELF,
|
||||
KILL_SELF,
|
||||
SPAWN_OBJECT,
|
||||
PLAY_SOUND,
|
||||
};
|
||||
|
||||
class WaypointCommandType {
|
||||
public:
|
||||
static eWaypointCommandType StringToWaypointCommandType(std::string commandString) {
|
||||
const std::map<std::string, eWaypointCommandType> WaypointCommandTypeMap = {
|
||||
{"bounce", eWaypointCommandType::BOUNCE},
|
||||
{"stop", eWaypointCommandType::STOP},
|
||||
{"groupemote", eWaypointCommandType::GROUP_EMOTE},
|
||||
{"setvar", eWaypointCommandType::SET_VARIABLE},
|
||||
{"castskill", eWaypointCommandType::CAST_SKILL},
|
||||
{"eqInvent", eWaypointCommandType::EQUIP_INVENTORY},
|
||||
{"unInvent", eWaypointCommandType::UNEQUIP_INVENTORY},
|
||||
{"delay", eWaypointCommandType::DELAY},
|
||||
{"femote", eWaypointCommandType::EMOTE},
|
||||
{"emote", eWaypointCommandType::EMOTE},
|
||||
{"teleport", eWaypointCommandType::TELEPORT},
|
||||
{"pathspeed", eWaypointCommandType::PATH_SPEED},
|
||||
{"removeNPC", eWaypointCommandType::REMOVE_NPC},
|
||||
{"changeWP", eWaypointCommandType::CHANGE_WAYPOINT},
|
||||
{"DeleteSelf", eWaypointCommandType::DELETE_SELF},
|
||||
{"killself", eWaypointCommandType::KILL_SELF},
|
||||
{"removeself", eWaypointCommandType::DELETE_SELF},
|
||||
{"spawnOBJ", eWaypointCommandType::SPAWN_OBJECT},
|
||||
{"playSound", eWaypointCommandType::PLAY_SOUND},
|
||||
};
|
||||
|
||||
auto intermed = WaypointCommandTypeMap.find(commandString);
|
||||
return (intermed != WaypointCommandTypeMap.end()) ? intermed->second : eWaypointCommandType::INVALID;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif //!__EWAYPOINTCOMMANDTYPES__H__
|
||||
@@ -29,8 +29,8 @@ enum class eWorldMessageType : uint32_t {
|
||||
ROUTE_PACKET, // Social?
|
||||
POSITION_UPDATE,
|
||||
MAIL,
|
||||
WORD_CHECK, // Whitelist word check
|
||||
STRING_CHECK, // Whitelist string check
|
||||
WORD_CHECK, // AllowList word check
|
||||
STRING_CHECK, // AllowList string check
|
||||
GET_PLAYERS_IN_ZONE,
|
||||
REQUEST_UGC_MANIFEST_INFO,
|
||||
BLUEPRINT_GET_ALL_DATA_REQUEST,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "CDScriptComponentTable.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
#include "CDTamingBuildPuzzleTable.h"
|
||||
#include "CDVendorComponentTable.h"
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "CDPackageComponentTable.h"
|
||||
@@ -41,8 +42,6 @@
|
||||
#include "CDRewardCodesTable.h"
|
||||
#include "CDPetComponentTable.h"
|
||||
|
||||
#include <exception>
|
||||
|
||||
#ifndef CDCLIENT_CACHE_ALL
|
||||
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
|
||||
// A vanilla CDClient takes about 46MB of memory + the regular world data.
|
||||
@@ -55,13 +54,6 @@
|
||||
#define CDCLIENT_DONT_CACHE_TABLE(x)
|
||||
#endif
|
||||
|
||||
class CDClientConnectionException : public std::exception {
|
||||
public:
|
||||
virtual const char* what() const throw() {
|
||||
return "CDClientDatabase is not connected!";
|
||||
}
|
||||
};
|
||||
|
||||
// Using a macro to reduce repetitive code and issues from copy and paste.
|
||||
// As a note, ## in a macro is used to concatenate two tokens together.
|
||||
|
||||
@@ -108,11 +100,14 @@ DEFINE_TABLE_STORAGE(CDRewardCodesTable);
|
||||
DEFINE_TABLE_STORAGE(CDRewardsTable);
|
||||
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||
DEFINE_TABLE_STORAGE(CDZoneTableTable);
|
||||
|
||||
void CDClientManager::LoadValuesFromDatabase() {
|
||||
if (!CDClientDatabase::isConnected) throw CDClientConnectionException();
|
||||
if (!CDClientDatabase::isConnected) {
|
||||
throw std::runtime_error{ "CDClientDatabase is not connected!" };
|
||||
}
|
||||
|
||||
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDActivitiesTable::Instance().LoadValuesFromDatabase();
|
||||
@@ -152,6 +147,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
||||
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
|
||||
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDZoneTableTable::Instance().LoadValuesFromDatabase();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ void CDLootTableTable::LoadValuesFromDatabase() {
|
||||
CDLootTable entry;
|
||||
uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1);
|
||||
|
||||
entries[lootTableIndex].push_back(ReadRow(tableData));
|
||||
entries[lootTableIndex].emplace_back(ReadRow(tableData));
|
||||
tableData.nextRow();
|
||||
}
|
||||
for (auto& [id, table] : entries) {
|
||||
@@ -66,7 +66,7 @@ void CDLootTableTable::LoadValuesFromDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
|
||||
const LootTableEntries& CDLootTableTable::GetTable(const uint32_t tableId) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto itr = entries.find(tableId);
|
||||
if (itr != entries.end()) {
|
||||
@@ -79,7 +79,7 @@ const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
|
||||
|
||||
while (!tableData.eof()) {
|
||||
CDLootTable entry;
|
||||
entries[tableId].push_back(ReadRow(tableData));
|
||||
entries[tableId].emplace_back(ReadRow(tableData));
|
||||
tableData.nextRow();
|
||||
}
|
||||
SortTable(entries[tableId]);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDLootTable {
|
||||
uint32_t itemid; //!< The LOT of the item
|
||||
uint32_t LootTableIndex; //!< The Loot Table Index
|
||||
@@ -20,6 +22,5 @@ private:
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
const LootTableEntries& GetTable(uint32_t tableId);
|
||||
const LootTableEntries& GetTable(const uint32_t tableId);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ void CDMissionEmailTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionEmail");
|
||||
while (!tableData.eof()) {
|
||||
CDMissionEmail entry;
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.ID = tableData.getIntField("ID", -1);
|
||||
entry.messageType = tableData.getIntField("messageType", -1);
|
||||
entry.notificationGroup = tableData.getIntField("notificationGroup", -1);
|
||||
@@ -30,11 +30,8 @@ void CDMissionEmailTable::LoadValuesFromDatabase() {
|
||||
entry.locStatus = tableData.getIntField("locStatus", -1);
|
||||
entry.gate_version = tableData.getStringField("gate_version", "");
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
//! Queries the table with a custom "where" clause
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDMissionEmail {
|
||||
uint32_t ID;
|
||||
uint32_t messageType;
|
||||
|
||||
@@ -20,18 +20,15 @@ void CDMissionNPCComponentTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionNPCComponent");
|
||||
while (!tableData.eof()) {
|
||||
CDMissionNPCComponent entry;
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.missionID = tableData.getIntField("missionID", -1);
|
||||
entry.offersMission = tableData.getIntField("offersMission", -1) == 1 ? true : false;
|
||||
entry.acceptsMission = tableData.getIntField("acceptsMission", -1) == 1 ? true : false;
|
||||
entry.gate_version = tableData.getStringField("gate_version", "");
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
//! Queries the table with a custom "where" clause
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDMissionNPCComponent {
|
||||
uint32_t id; //!< The ID
|
||||
uint32_t missionID; //!< The Mission ID
|
||||
@@ -17,4 +19,3 @@ public:
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDMissionNPCComponent> Query(std::function<bool(CDMissionNPCComponent)> predicate);
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ void CDMissionTasksTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionTasks");
|
||||
while (!tableData.eof()) {
|
||||
CDMissionTasks entry;
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
|
||||
entry.taskType = tableData.getIntField("taskType", -1);
|
||||
@@ -35,11 +35,8 @@ void CDMissionTasksTable::LoadValuesFromDatabase() {
|
||||
UNUSED(entry.localize = tableData.getIntField("localize", -1) == 1 ? true : false);
|
||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
std::vector<CDMissionTasks> CDMissionTasksTable::Query(std::function<bool(CDMissionTasks)> predicate) {
|
||||
@@ -51,7 +48,7 @@ std::vector<CDMissionTasks> CDMissionTasksTable::Query(std::function<bool(CDMiss
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<CDMissionTasks*> CDMissionTasksTable::GetByMissionID(uint32_t missionID) {
|
||||
std::vector<CDMissionTasks*> CDMissionTasksTable::GetByMissionID(const uint32_t missionID) {
|
||||
std::vector<CDMissionTasks*> tasks;
|
||||
|
||||
// TODO: this should not be linear(?) and also shouldnt need to be a pointer
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDMissionTasks {
|
||||
uint32_t id; //!< The Mission ID that the task belongs to
|
||||
UNUSED(uint32_t locStatus); //!< ???
|
||||
@@ -25,7 +27,7 @@ public:
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDMissionTasks> Query(std::function<bool(CDMissionTasks)> predicate);
|
||||
|
||||
std::vector<CDMissionTasks*> GetByMissionID(uint32_t missionID);
|
||||
std::vector<CDMissionTasks*> GetByMissionID(const uint32_t missionID);
|
||||
|
||||
// TODO: Remove this and replace it with a proper lookup function.
|
||||
const CDTable::StorageType& GetEntries() const;
|
||||
|
||||
@@ -22,7 +22,7 @@ void CDMissionsTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Missions");
|
||||
while (!tableData.eof()) {
|
||||
CDMissions entry;
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.defined_type = tableData.getStringField("defined_type", "");
|
||||
entry.defined_subtype = tableData.getStringField("defined_subtype", "");
|
||||
@@ -76,7 +76,6 @@ void CDMissionsTable::LoadValuesFromDatabase() {
|
||||
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
|
||||
entry.reward_bankinventory = tableData.getIntField("reward_bankinventory", -1);
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
tableData.finalize();
|
||||
|
||||
@@ -75,4 +75,3 @@ public:
|
||||
|
||||
static CDMissions Default;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ void CDMovementAIComponentTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MovementAIComponent");
|
||||
while (!tableData.eof()) {
|
||||
CDMovementAIComponent entry;
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.MovementType = tableData.getStringField("MovementType", "");
|
||||
entry.WanderChance = tableData.getFloatField("WanderChance", -1.0f);
|
||||
@@ -30,11 +30,8 @@ void CDMovementAIComponentTable::LoadValuesFromDatabase() {
|
||||
entry.WanderRadius = tableData.getFloatField("WanderRadius", -1.0f);
|
||||
entry.attachedPath = tableData.getStringField("attachedPath", "");
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
std::vector<CDMovementAIComponent> CDMovementAIComponentTable::Query(std::function<bool(CDMovementAIComponent)> predicate) {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDMovementAIComponent {
|
||||
uint32_t id;
|
||||
std::string MovementType;
|
||||
|
||||
@@ -20,17 +20,14 @@ void CDObjectSkillsTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ObjectSkills");
|
||||
while (!tableData.eof()) {
|
||||
CDObjectSkills entry;
|
||||
auto &entry = entries.emplace_back();
|
||||
entry.objectTemplate = tableData.getIntField("objectTemplate", -1);
|
||||
entry.skillID = tableData.getIntField("skillID", -1);
|
||||
entry.castOnType = tableData.getIntField("castOnType", -1);
|
||||
entry.AICombatWeight = tableData.getIntField("AICombatWeight", -1);
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObjectSkills)> predicate) {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDObjectSkills {
|
||||
uint32_t objectTemplate; //!< The LOT of the item
|
||||
uint32_t skillID; //!< The Skill ID of the object
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "CDObjectsTable.h"
|
||||
|
||||
namespace {
|
||||
CDObjects m_default;
|
||||
CDObjects ObjDefault;
|
||||
};
|
||||
|
||||
void CDObjectsTable::LoadValuesFromDatabase() {
|
||||
@@ -20,8 +20,10 @@ void CDObjectsTable::LoadValuesFromDatabase() {
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Objects");
|
||||
auto& entries = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
CDObjects entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
const uint32_t lot = tableData.getIntField("id", 0);
|
||||
|
||||
auto& entry = entries[lot];
|
||||
entry.id = lot;
|
||||
entry.name = tableData.getStringField("name", "");
|
||||
UNUSED_COLUMN(entry.placeable = tableData.getIntField("placeable", -1);)
|
||||
entry.type = tableData.getStringField("type", "");
|
||||
@@ -36,35 +38,34 @@ void CDObjectsTable::LoadValuesFromDatabase() {
|
||||
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "");)
|
||||
UNUSED_COLUMN(entry.HQ_valid = tableData.getIntField("HQ_valid", -1);)
|
||||
|
||||
entries.insert(std::make_pair(entry.id, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
|
||||
m_default.id = 0;
|
||||
ObjDefault.id = 0;
|
||||
}
|
||||
|
||||
const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) {
|
||||
const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
const auto& it = entries.find(LOT);
|
||||
const auto& it = entries.find(lot);
|
||||
if (it != entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Objects WHERE id = ?;");
|
||||
query.bind(1, static_cast<int32_t>(LOT));
|
||||
query.bind(1, static_cast<int32_t>(lot));
|
||||
|
||||
auto tableData = query.execQuery();
|
||||
if (tableData.eof()) {
|
||||
entries.insert(std::make_pair(LOT, m_default));
|
||||
return m_default;
|
||||
entries.emplace(lot, ObjDefault);
|
||||
return ObjDefault;
|
||||
}
|
||||
|
||||
// Now get the data
|
||||
while (!tableData.eof()) {
|
||||
CDObjects entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
const uint32_t lot = tableData.getIntField("id", 0);
|
||||
|
||||
auto& entry = entries[lot];
|
||||
entry.id = lot;
|
||||
entry.name = tableData.getStringField("name", "");
|
||||
UNUSED(entry.placeable = tableData.getIntField("placeable", -1));
|
||||
entry.type = tableData.getStringField("type", "");
|
||||
@@ -79,17 +80,15 @@ const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) {
|
||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
UNUSED(entry.HQ_valid = tableData.getIntField("HQ_valid", -1));
|
||||
|
||||
entries.insert(std::make_pair(entry.id, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
|
||||
const auto& it2 = entries.find(LOT);
|
||||
const auto& it2 = entries.find(lot);
|
||||
if (it2 != entries.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
return m_default;
|
||||
return ObjDefault;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDObjects {
|
||||
uint32_t id; //!< The LOT of the object
|
||||
std::string name; //!< The internal name of the object
|
||||
@@ -24,6 +26,6 @@ class CDObjectsTable : public CDTable<CDObjectsTable, std::map<uint32_t, CDObjec
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Gets an entry by ID
|
||||
const CDObjects& GetByID(uint32_t LOT);
|
||||
const CDObjects& GetByID(const uint32_t lot);
|
||||
};
|
||||
|
||||
|
||||
@@ -19,12 +19,11 @@ void CDPackageComponentTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PackageComponent");
|
||||
while (!tableData.eof()) {
|
||||
CDPackageComponent entry;
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
|
||||
entry.packageType = tableData.getIntField("packageType", -1);
|
||||
|
||||
entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct CDPackageComponent {
|
||||
uint32_t id;
|
||||
uint32_t LootMatrixIndex;
|
||||
|
||||
@@ -50,7 +50,7 @@ void CDPetComponentTable::LoadValuesFromDatabase() {
|
||||
}
|
||||
|
||||
void CDPetComponentTable::LoadValuesFromDefaults() {
|
||||
GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry));
|
||||
GetEntriesMutable().emplace(defaultEntry.id, defaultEntry);
|
||||
}
|
||||
|
||||
CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {
|
||||
|
||||
@@ -4,32 +4,31 @@ void CDPhysicsComponentTable::LoadValuesFromDatabase() {
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent");
|
||||
auto& entries = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
CDPhysicsComponent entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
const uint32_t componentID = tableData.getIntField("id", -1);
|
||||
|
||||
auto& entry = entries[componentID];
|
||||
entry.id = componentID;
|
||||
entry.bStatic = tableData.getIntField("static", -1) != 0;
|
||||
entry.physicsAsset = tableData.getStringField("physics_asset", "");
|
||||
UNUSED(entry->jump = tableData.getIntField("jump", -1) != 0);
|
||||
UNUSED(entry->doublejump = tableData.getIntField("doublejump", -1) != 0);
|
||||
entry.speed = tableData.getFloatField("speed", -1);
|
||||
UNUSED(entry->rotSpeed = tableData.getFloatField("rotSpeed", -1));
|
||||
entry.playerHeight = tableData.getFloatField("playerHeight");
|
||||
entry.playerRadius = tableData.getFloatField("playerRadius");
|
||||
UNUSED_COLUMN(entry.jump = tableData.getIntField("jump", -1) != 0;)
|
||||
UNUSED_COLUMN(entry.doubleJump = tableData.getIntField("doublejump", -1) != 0;)
|
||||
entry.speed = static_cast<float>(tableData.getFloatField("speed", -1));
|
||||
UNUSED_COLUMN(entry.rotSpeed = tableData.getFloatField("rotSpeed", -1);)
|
||||
entry.playerHeight = static_cast<float>(tableData.getFloatField("playerHeight"));
|
||||
entry.playerRadius = static_cast<float>(tableData.getFloatField("playerRadius"));
|
||||
entry.pcShapeType = tableData.getIntField("pcShapeType");
|
||||
entry.collisionGroup = tableData.getIntField("collisionGroup");
|
||||
UNUSED(entry->airSpeed = tableData.getFloatField("airSpeed"));
|
||||
UNUSED(entry->boundaryAsset = tableData.getStringField("boundaryAsset"));
|
||||
UNUSED(entry->jumpAirSpeed = tableData.getFloatField("jumpAirSpeed"));
|
||||
UNUSED(entry->friction = tableData.getFloatField("friction"));
|
||||
UNUSED(entry->gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset"));
|
||||
UNUSED_COLUMN(entry.airSpeed = tableData.getFloatField("airSpeed");)
|
||||
UNUSED_COLUMN(entry.boundaryAsset = tableData.getStringField("boundaryAsset");)
|
||||
UNUSED_COLUMN(entry.jumpAirSpeed = tableData.getFloatField("jumpAirSpeed");)
|
||||
UNUSED_COLUMN(entry.friction = tableData.getFloatField("friction");)
|
||||
UNUSED_COLUMN(entry.gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset");)
|
||||
|
||||
entries.insert(std::make_pair(entry.id, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(uint32_t componentID) {
|
||||
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(const uint32_t componentID) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto itr = entries.find(componentID);
|
||||
return itr != entries.end() ? &itr->second : nullptr;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "CDTable.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct CDPhysicsComponent {
|
||||
@@ -7,7 +8,7 @@ struct CDPhysicsComponent {
|
||||
bool bStatic;
|
||||
std::string physicsAsset;
|
||||
UNUSED(bool jump);
|
||||
UNUSED(bool doublejump);
|
||||
UNUSED(bool doubleJump);
|
||||
float speed;
|
||||
UNUSED(float rotSpeed);
|
||||
float playerHeight;
|
||||
@@ -26,5 +27,5 @@ public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
static const std::string GetTableName() { return "PhysicsComponent"; };
|
||||
CDPhysicsComponent* GetByID(uint32_t componentID);
|
||||
CDPhysicsComponent* GetByID(const uint32_t componentID);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "CDTamingBuildPuzzleTable.h"
|
||||
|
||||
void CDTamingBuildPuzzleTable::LoadValuesFromDatabase() {
|
||||
// First, get the size of the table
|
||||
uint32_t size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM TamingBuildPuzzles");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
tableSize.nextRow();
|
||||
}
|
||||
|
||||
// Reserve the size
|
||||
auto& entries = GetEntriesMutable();
|
||||
entries.reserve(size);
|
||||
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM TamingBuildPuzzles");
|
||||
while (!tableData.eof()) {
|
||||
const auto lot = static_cast<LOT>(tableData.getIntField("NPCLot", LOT_NULL));
|
||||
entries.emplace(lot, CDTamingBuildPuzzle{
|
||||
.puzzleModelLot = lot,
|
||||
.validPieces{ tableData.getStringField("ValidPiecesLXF") },
|
||||
.timeLimit = static_cast<float>(tableData.getFloatField("Timelimit", 30.0f)),
|
||||
.numValidPieces = tableData.getIntField("NumValidPieces", 6),
|
||||
.imaginationCost = tableData.getIntField("imagCostPerBuild", 10)
|
||||
});
|
||||
tableData.nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
const CDTamingBuildPuzzle* CDTamingBuildPuzzleTable::GetByLOT(const LOT lot) const {
|
||||
const auto& entries = GetEntries();
|
||||
const auto itr = entries.find(lot);
|
||||
return itr != entries.cend() ? &itr->second : nullptr;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "CDTable.h"
|
||||
|
||||
/**
|
||||
* Information for the minigame to be completed
|
||||
*/
|
||||
struct CDTamingBuildPuzzle {
|
||||
UNUSED_COLUMN(uint32_t id = 0;)
|
||||
|
||||
// The LOT of the object that is to be created
|
||||
LOT puzzleModelLot = LOT_NULL;
|
||||
|
||||
// The LOT of the NPC
|
||||
UNUSED_COLUMN(LOT npcLot = LOT_NULL;)
|
||||
|
||||
// The .lxfml file that contains the bricks required to build the model
|
||||
std::string validPieces{};
|
||||
|
||||
// The .lxfml file that contains the bricks NOT required to build the model
|
||||
UNUSED_COLUMN(std::string invalidPieces{};)
|
||||
|
||||
// Difficulty value
|
||||
UNUSED_COLUMN(int32_t difficulty = 1;)
|
||||
|
||||
// The time limit to complete the build
|
||||
float timeLimit = 30.0f;
|
||||
|
||||
// The number of pieces required to complete the minigame
|
||||
int32_t numValidPieces = 6;
|
||||
|
||||
// Number of valid pieces
|
||||
UNUSED_COLUMN(int32_t totalNumPieces = 16;)
|
||||
|
||||
// Model name
|
||||
UNUSED_COLUMN(std::string modelName{};)
|
||||
|
||||
// The .lxfml file that contains the full model
|
||||
UNUSED_COLUMN(std::string fullModel{};)
|
||||
|
||||
// The duration of the pet taming minigame
|
||||
UNUSED_COLUMN(float duration = 45.0f;)
|
||||
|
||||
// The imagination cost for the tamer to start the minigame
|
||||
int32_t imaginationCost = 10;
|
||||
};
|
||||
|
||||
class CDTamingBuildPuzzleTable : public CDTable<CDTamingBuildPuzzleTable, std::unordered_map<LOT, CDTamingBuildPuzzle>> {
|
||||
public:
|
||||
/**
|
||||
* Load values from the CD client database
|
||||
*/
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
/**
|
||||
* Gets the pet ability table corresponding to the pet LOT
|
||||
* @returns A pointer to the corresponding table, or nullptr if one cannot be found
|
||||
*/
|
||||
[[nodiscard]]
|
||||
const CDTamingBuildPuzzle* GetByLOT(const LOT lot) const;
|
||||
};
|
||||
@@ -36,5 +36,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
|
||||
"CDRewardsTable.cpp"
|
||||
"CDScriptComponentTable.cpp"
|
||||
"CDSkillBehaviorTable.cpp"
|
||||
"CDTamingBuildPuzzleTable.cpp"
|
||||
"CDVendorComponentTable.cpp"
|
||||
"CDZoneTableTable.cpp" PARENT_SCOPE)
|
||||
|
||||
@@ -9,4 +9,28 @@ foreach(file ${DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES})
|
||||
set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} "CDClientTables/${file}")
|
||||
endforeach()
|
||||
|
||||
set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} PARENT_SCOPE)
|
||||
add_library(dDatabaseCDClient STATIC ${DDATABASE_CDCLIENTDATABASE_SOURCES})
|
||||
target_include_directories(dDatabaseCDClient PUBLIC "."
|
||||
"CDClientTables"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
||||
)
|
||||
target_link_libraries(dDatabaseCDClient PRIVATE sqlite3)
|
||||
|
||||
if (${CDCLIENT_CACHE_ALL})
|
||||
add_compile_definitions(dDatabaseCDClient PRIVATE CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
|
||||
endif()
|
||||
|
||||
file(
|
||||
GLOB HEADERS_DDATABASE_CDCLIENT
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||
CDClientTables/*.h
|
||||
*.h
|
||||
)
|
||||
|
||||
# Need to specify to use the CXX compiler language here or else we get errors including <string>.
|
||||
target_precompile_headers(
|
||||
dDatabaseCDClient PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE_CDCLIENT}>"
|
||||
)
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
set(DDATABASE_SOURCES)
|
||||
|
||||
add_subdirectory(CDClientDatabase)
|
||||
|
||||
foreach(file ${DDATABASE_CDCLIENTDATABASE_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "CDClientDatabase/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(GameDatabase)
|
||||
|
||||
foreach(file ${DDATABASE_GAMEDATABASE_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "GameDatabase/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
|
||||
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
|
||||
|
||||
if (${CDCLIENT_CACHE_ALL})
|
||||
add_compile_definitions(dDatabase CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
|
||||
endif()
|
||||
add_library(dDatabase STATIC "MigrationRunner.cpp")
|
||||
target_include_directories(dDatabase PUBLIC ".")
|
||||
target_link_libraries(dDatabase
|
||||
PUBLIC dDatabaseCDClient dDatabaseGame)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
set(DDATABASE_GAMEDATABASE_SOURCES
|
||||
"Database.cpp"
|
||||
"MigrationRunner.cpp"
|
||||
)
|
||||
|
||||
add_subdirectory(MySQL)
|
||||
@@ -9,4 +8,25 @@ foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES})
|
||||
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}")
|
||||
endforeach()
|
||||
|
||||
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} PARENT_SCOPE)
|
||||
add_library(dDatabaseGame STATIC ${DDATABASE_GAMEDATABASE_SOURCES})
|
||||
target_include_directories(dDatabaseGame PUBLIC "."
|
||||
"ITables" PRIVATE "MySQL"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
||||
)
|
||||
target_link_libraries(dDatabaseGame
|
||||
PUBLIC MariaDB::ConnCpp
|
||||
INTERFACE dCommon)
|
||||
|
||||
# Glob together all headers that need to be precompiled
|
||||
file(
|
||||
GLOB HEADERS_DDATABASE_GAME
|
||||
LIST_DIRECTORIES false
|
||||
ITables/*.h
|
||||
)
|
||||
|
||||
# Need to specify to use the CXX compiler language here or else we get errors including <string>.
|
||||
target_precompile_headers(
|
||||
dDatabaseGame PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE_GAME}>"
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
enum class eGameMasterLevel : uint8_t;
|
||||
|
||||
677
dGame/AdditionalEntityData.cpp
Normal file
677
dGame/AdditionalEntityData.cpp
Normal file
@@ -0,0 +1,677 @@
|
||||
#include "AdditionalEntityData.h"
|
||||
|
||||
#include "NejlikaData.h"
|
||||
|
||||
#include <DestroyableComponent.h>
|
||||
#include <LevelProgressionComponent.h>
|
||||
#include <InventoryComponent.h>
|
||||
#include <BaseCombatAIComponent.h>
|
||||
#include <TeamManager.h>
|
||||
#include <ControllablePhysicsComponent.h>
|
||||
#include <Item.h>
|
||||
|
||||
#include <queue>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, ModifierOperator op, bool resistance) const
|
||||
{
|
||||
float total = 0;
|
||||
|
||||
for (const auto& modifier : activeModifiers) {
|
||||
if (modifier.GetConvertTo() != ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modifier.GetType() != type || modifier.GetOperator() != op || modifier.IsResistance() != resistance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
total += modifier.GetValue();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, const std::vector<ModifierInstance>& additionalModifiers, ModifierOperator op, bool resistance) const {
|
||||
float total = 0;
|
||||
|
||||
for (const auto& modifier : additionalModifiers) {
|
||||
if (modifier.GetConvertTo() != ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modifier.GetType() != type || modifier.GetOperator() != op || modifier.IsResistance() != resistance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
total += modifier.GetValue();
|
||||
}
|
||||
|
||||
return total + CalculateModifier(type, op, resistance);
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, int32_t level) const
|
||||
{
|
||||
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
|
||||
|
||||
if (!templateDataOpt.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& templateData = *templateDataOpt.value();
|
||||
|
||||
const auto scaler = templateData.GetScaler(type, false, level);
|
||||
|
||||
float additive = CalculateModifier(type, ModifierOperator::Additive, false);
|
||||
|
||||
if (scaler != 0 && additive >= scaler) {
|
||||
additive -= scaler;
|
||||
}
|
||||
|
||||
float multiplicative = CalculateModifier(type, ModifierOperator::Multiplicative, false);
|
||||
|
||||
return (scaler + additive) * (1 + multiplicative / 100);
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type) const {
|
||||
return CalculateModifier(type, level);
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateFinalModifier(ModifierType type, const std::vector<ModifierInstance>& additionalModifiers, int32_t level) const
|
||||
{
|
||||
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
|
||||
|
||||
if (!templateDataOpt.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& templateData = *templateDataOpt.value();
|
||||
|
||||
const auto scaler = templateData.GetScaler(type, false, level);
|
||||
|
||||
float additive = CalculateModifier(type, additionalModifiers, ModifierOperator::Additive, false);
|
||||
|
||||
if (scaler != 0 && additive >= scaler) {
|
||||
additive -= scaler;
|
||||
}
|
||||
|
||||
float multiplicative = CalculateModifier(type, additionalModifiers, ModifierOperator::Multiplicative, false);
|
||||
|
||||
static const std::unordered_set<ModifierType> elementalDamage = {
|
||||
ModifierType::Fire,
|
||||
ModifierType::Cold,
|
||||
ModifierType::Lightning
|
||||
};
|
||||
|
||||
if (type == ModifierType::Health) {
|
||||
if (lot == 1) additive += 25;
|
||||
additive += CalculateModifier(ModifierType::Physique, additionalModifiers, ModifierOperator::Additive, false) * 2.5f;
|
||||
additive += CalculateModifier(ModifierType::Cunning, additionalModifiers, ModifierOperator::Additive, false) * 1.0f;
|
||||
additive += CalculateModifier(ModifierType::Spirit, additionalModifiers, ModifierOperator::Additive, false) * 1.0f;
|
||||
}
|
||||
else if (type == ModifierType::Imagination) {
|
||||
additive += CalculateModifier(ModifierType::Spirit, additionalModifiers, ModifierOperator::Additive, false) * 2.0f;
|
||||
}
|
||||
else if (type == ModifierType::Seperation || type == ModifierType::InternalDisassembly || type == ModifierType::Physical) {
|
||||
multiplicative += CalculateModifier(ModifierType::Cunning, additionalModifiers, ModifierOperator::Additive, false) * 0.33f;
|
||||
}
|
||||
else if (type == ModifierType::Pierce) {
|
||||
multiplicative += CalculateModifier(ModifierType::Cunning, additionalModifiers, ModifierOperator::Additive, false) * 0.285f;
|
||||
}
|
||||
else if (nejlika::IsOverTimeType(type) || nejlika::IsNormalDamageType(type)) {
|
||||
multiplicative += CalculateModifier(ModifierType::Spirit, additionalModifiers, ModifierOperator::Additive, false) * 0.33f;
|
||||
}
|
||||
else if (type == ModifierType::ImaginationRegen) {
|
||||
const auto spirit = CalculateModifier(ModifierType::Spirit, additionalModifiers, ModifierOperator::Additive, false);
|
||||
additive += spirit * 0.01f + 1;
|
||||
multiplicative += spirit * 0.25f;
|
||||
}
|
||||
else if (type == ModifierType::HealthRegen) {
|
||||
const auto physique = CalculateModifier(ModifierType::Physique, additionalModifiers, ModifierOperator::Additive, false);
|
||||
additive += physique * 0.04f;
|
||||
}
|
||||
else if (type == ModifierType::Defensive) {
|
||||
additive += CalculateModifier(ModifierType::Physique, additionalModifiers, ModifierOperator::Additive, false) * 0.5f;
|
||||
if (lot == 1) additive += level * 10;
|
||||
}
|
||||
else if (type == ModifierType::Offensive) {
|
||||
additive += CalculateModifier(ModifierType::Cunning, additionalModifiers, ModifierOperator::Additive, false) * 0.5f;
|
||||
if (lot == 1) additive += level * 10;
|
||||
}
|
||||
|
||||
if (elementalDamage.contains(type)) {
|
||||
additive += CalculateModifier(ModifierType::Elemental, additionalModifiers, ModifierOperator::Additive, false) / elementalDamage.size();
|
||||
multiplicative += CalculateModifier(ModifierType::Elemental, additionalModifiers, ModifierOperator::Multiplicative, false) / elementalDamage.size();
|
||||
}
|
||||
|
||||
if (nejlika::IsNormalDamageType(type) || nejlika::IsOverTimeType(type)) {
|
||||
additive += CalculateModifier(ModifierType::Damage, additionalModifiers, ModifierOperator::Additive, false);
|
||||
multiplicative += CalculateModifier(ModifierType::Damage, additionalModifiers, ModifierOperator::Multiplicative, false);
|
||||
}
|
||||
|
||||
float total = (scaler + additive) * (1 + multiplicative / 100);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateResistance(ModifierType type) const
|
||||
{
|
||||
type = nejlika::GetResistanceType(type);
|
||||
|
||||
return CalculateModifier(type, ModifierOperator::Multiplicative, true);
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateMultiplier(ModifierType type) const
|
||||
{
|
||||
return 100 + CalculateModifier(type, ModifierOperator::Multiplicative, false);
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::AdditionalEntityData::TriggerUpgradeItems(UpgradeTriggerType triggerType, const TriggerParameters& params) {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> result;
|
||||
|
||||
for (const auto& itemID : upgradeItems) {
|
||||
auto* item = inventoryComponent->FindItemById(itemID);
|
||||
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto upgradeDataOpt = NejlikaData::GetUpgradeTemplate(item->GetLot());
|
||||
|
||||
if (!upgradeDataOpt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& upgradeData = *upgradeDataOpt.value();
|
||||
|
||||
const auto modifiers = upgradeData.Trigger(item->GetCount(), triggerType, id, params);
|
||||
|
||||
result.insert(result.end(), modifiers.begin(), modifiers.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::AdditionalEntityData::TriggerUpgradeItems(UpgradeTriggerType triggerType) {
|
||||
return TriggerUpgradeItems(triggerType, {});
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::InitializeSkills() {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct entry {
|
||||
LWOOBJID id;
|
||||
int32_t priority;
|
||||
|
||||
entry(LWOOBJID id, int32_t priority) : id(id), priority(priority) {}
|
||||
};
|
||||
|
||||
std::vector<entry> items;
|
||||
|
||||
for (const auto& itemID : upgradeItems) {
|
||||
auto* item = inventoryComponent->FindItemById(itemID);
|
||||
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto priority = item->GetSlot();
|
||||
|
||||
items.push_back(entry(itemID, priority));
|
||||
}
|
||||
|
||||
std::sort(items.begin(), items.end(), [](const entry& a, const entry& b) {
|
||||
return a.priority < b.priority;
|
||||
});
|
||||
|
||||
for (const auto& item : items) {
|
||||
AddSkills(item.id);
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::AddSkills(LWOOBJID item) {
|
||||
if (!upgradeItems.contains(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* itemData = inventoryComponent->FindItemById(item);
|
||||
|
||||
if (itemData == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto upgradeDataOpt = NejlikaData::GetUpgradeTemplate(itemData->GetLot());
|
||||
|
||||
if (!upgradeDataOpt.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& upgradeData = *upgradeDataOpt.value();
|
||||
|
||||
LOG("Adding skills for item %i", id);
|
||||
|
||||
upgradeData.AddSkills(id);
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::RemoveSkills(LOT lot) {
|
||||
const auto upgradeDataOpt = NejlikaData::GetUpgradeTemplate(lot);
|
||||
|
||||
if (!upgradeDataOpt.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& upgradeData = *upgradeDataOpt.value();
|
||||
|
||||
LOG("Removing skills for item %i", id);
|
||||
|
||||
upgradeData.RemoveSkills(id);
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::AdditionalEntityData::CalculateMainWeaponDamage() {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Item* mainWeapon = nullptr;
|
||||
|
||||
for (const auto& [location, item] : inventoryComponent->GetEquippedItems()) {
|
||||
if (location == "special_r") {
|
||||
mainWeapon = inventoryComponent->FindItemById(item.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainWeapon == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto additionalItemDataOpt = NejlikaData::GetAdditionalItemData(mainWeapon->GetId());
|
||||
|
||||
if (!additionalItemDataOpt.has_value()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& additionalItemData = *additionalItemDataOpt.value();
|
||||
|
||||
std::vector<ModifierInstance> result;
|
||||
|
||||
for (const auto& modifier : additionalItemData.GetModifierInstances()) {
|
||||
if (nejlika::IsNormalDamageType(modifier.GetType()) || nejlika::IsOverTimeType(modifier.GetType()) || nejlika::IsDurationType(modifier.GetType())) {
|
||||
if (modifier.GetOperator() == ModifierOperator::Additive && modifier.GetUpgradeName().empty()) {
|
||||
result.push_back(modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::RollStandardModifiers(int32_t level) {
|
||||
standardModifiers.clear();
|
||||
|
||||
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
|
||||
|
||||
if (templateDataOpt.has_value()) {
|
||||
const auto& templateData = *templateDataOpt.value();
|
||||
|
||||
const auto modifiers = templateData.GenerateModifiers(level);
|
||||
|
||||
standardModifiers.insert(standardModifiers.end(), modifiers.begin(), modifiers.end());
|
||||
}
|
||||
|
||||
const auto objectDataVec = NejlikaData::GetModifierNameTemplates(ModifierNameType::Object);
|
||||
|
||||
for (const auto& objectData : objectDataVec) {
|
||||
if (objectData.GetLOT() != lot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto modifiers = objectData.GenerateModifiers(level);
|
||||
|
||||
standardModifiers.insert(standardModifiers.end(), modifiers.begin(), modifiers.end());
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::TriggerPassiveRegeneration() {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto healthRegen = CalculateFinalModifier(ModifierType::HealthRegen, {}, level);
|
||||
const auto imaginationRegen = CalculateFinalModifier(ModifierType::ImaginationRegen, {}, level);
|
||||
|
||||
if (healthRegen > 0) {
|
||||
destroyable->SetHealth(std::min(destroyable->GetHealth() + static_cast<int32_t>(healthRegen), static_cast<int32_t>(destroyable->GetMaxHealth())));
|
||||
}
|
||||
|
||||
if (imaginationRegen > 0) {
|
||||
destroyable->SetImagination(std::min(destroyable->GetImagination() + static_cast<int32_t>(imaginationRegen), static_cast<int32_t>(destroyable->GetMaxImagination())));
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(entity);
|
||||
|
||||
// Trigger it again in 1 second
|
||||
entity->AddCallbackTimer(1.0f, [this]() {
|
||||
TriggerPassiveRegeneration();
|
||||
});
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateMultiplier(ModifierType type, const std::vector<ModifierInstance>& additionalModifiers) const {
|
||||
return 100 + CalculateModifier(type, additionalModifiers, ModifierOperator::Multiplicative, false);
|
||||
}
|
||||
|
||||
std::unordered_map<ModifierType, std::unordered_map<ModifierType, float>> nejlika::AdditionalEntityData::CalculateDamageConversion(std::vector<ModifierInstance>& additionalModifiers) const {
|
||||
std::unordered_map<ModifierType, std::unordered_map<ModifierType, float>> conversion;
|
||||
|
||||
for (const auto& modifier : activeModifiers) {
|
||||
if (modifier.GetConvertTo() == ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
conversion[modifier.GetType()][modifier.GetConvertTo()] += modifier.GetValue();
|
||||
}
|
||||
|
||||
for (const auto& modifier : additionalModifiers) {
|
||||
if (modifier.GetConvertTo() == ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
conversion[modifier.GetType()][modifier.GetConvertTo()] += modifier.GetValue();
|
||||
}
|
||||
|
||||
// Third pass: adjust bidirectional conversions
|
||||
auto copy = conversion; // Create a copy to iterate over
|
||||
for (const auto& [type, convertMap] : copy) {
|
||||
for (const auto& [convertTo, value] : convertMap) {
|
||||
if (conversion[convertTo][type] > 0) {
|
||||
if (value > conversion[convertTo][type]) {
|
||||
conversion[type][convertTo] -= conversion[convertTo][type];
|
||||
conversion[convertTo][type] = 0; // Ensure no negative values
|
||||
} else {
|
||||
conversion[convertTo][type] -= value;
|
||||
conversion[type][convertTo] = 0; // Ensure no negative values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fourth pass: if a type converts to multiple types, and the sum of the conversion values is greater than 100, normalize the values
|
||||
for (const auto& [type, convertMap] : conversion) {
|
||||
float sum = 0;
|
||||
for (const auto& [convertTo, value] : convertMap) {
|
||||
sum += value;
|
||||
}
|
||||
|
||||
if (sum > 100) {
|
||||
for (const auto& [convertTo, value] : convertMap) {
|
||||
conversion[type][convertTo] = value / sum * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conversion;
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::ApplyToEntity() {
|
||||
const auto templateDataOpt = NejlikaData::GetEntityTemplate(lot);
|
||||
|
||||
if (!templateDataOpt.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& templateData = *templateDataOpt.value();
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* levelProgression = entity->GetComponent<LevelProgressionComponent>();
|
||||
|
||||
if (levelProgression != nullptr) {
|
||||
this->level = levelProgression->GetLevel();
|
||||
}
|
||||
else {
|
||||
this->level = templateData.GetMinLevel();
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
RollStandardModifiers(level);
|
||||
}
|
||||
|
||||
activeModifiers = standardModifiers;
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent) {
|
||||
for (const auto& [location, item] : inventoryComponent->GetEquippedItems()) {
|
||||
const auto itemDataOpt = NejlikaData::GetAdditionalItemData(item.id);
|
||||
|
||||
if (!itemDataOpt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& itemData = *itemDataOpt.value();
|
||||
|
||||
const auto& itemModifiers = itemData.GetModifierInstances();
|
||||
|
||||
for (const auto& modifier : itemModifiers) {
|
||||
if (nejlika::IsNormalDamageType(modifier.GetType()) || nejlika::IsOverTimeType(modifier.GetType()) || nejlika::IsDurationType(modifier.GetType())) {
|
||||
if (modifier.GetOperator() == ModifierOperator::Additive && modifier.GetUpgradeName().empty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
activeModifiers.push_back(modifier);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& upgradeItem : upgradeItems) {
|
||||
auto* item = inventoryComponent->FindItemById(upgradeItem);
|
||||
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG("Applying upgrade item %i", item->GetLot());
|
||||
|
||||
const auto itemDataOpt = NejlikaData::GetUpgradeTemplate(item->GetLot());
|
||||
|
||||
if (!itemDataOpt.has_value()) {
|
||||
LOG("Upgrade item %i has no data", item->GetLot());
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& itemData = *itemDataOpt.value();
|
||||
|
||||
const auto& itemModifiers = itemData.GenerateModifiers(item->GetCount());
|
||||
|
||||
LOG("Upgrade item %i has %i modifiers with level %i", item->GetLot(), itemModifiers.size(), item->GetCount());
|
||||
|
||||
activeModifiers.insert(activeModifiers.end(), itemModifiers.begin(), itemModifiers.end());
|
||||
}
|
||||
}
|
||||
|
||||
destroyable->SetMaxHealth(static_cast<int32_t>(CalculateFinalModifier(ModifierType::Health, {}, level)));
|
||||
destroyable->SetMaxArmor(static_cast<int32_t>(CalculateFinalModifier(ModifierType::Armor, {}, level)));
|
||||
//if (!entity->IsPlayer()) {
|
||||
destroyable->SetMaxImagination(static_cast<int32_t>(CalculateFinalModifier(ModifierType::Imagination, {}, level)));
|
||||
//}
|
||||
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
destroyable->SetHealth(destroyable->GetMaxHealth());
|
||||
destroyable->SetArmor(destroyable->GetMaxArmor());
|
||||
|
||||
//if (!entity->IsPlayer()) {
|
||||
destroyable->SetImagination(destroyable->GetMaxImagination());
|
||||
//}
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
auto* controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
|
||||
if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(CalculateMultiplier(ModifierType::Speed) / 100.0f);
|
||||
}
|
||||
|
||||
TriggerPassiveRegeneration();
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::CheckForRescale(AdditionalEntityData* other) {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* baseCombat = entity->GetComponent<BaseCombatAIComponent>();
|
||||
|
||||
if (baseCombat == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& threats = baseCombat->GetThreats();
|
||||
|
||||
int32_t totalThreats = 0;
|
||||
int32_t totalLevel = 0;
|
||||
|
||||
for (const auto& [threat, _] : threats) {
|
||||
const auto threatEntityOpt = NejlikaData::GetAdditionalEntityData(threat);
|
||||
|
||||
if (!threatEntityOpt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& threatEntity = *threatEntityOpt.value();
|
||||
|
||||
if (other->id == threatEntity.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
totalLevel += threatEntity.level;
|
||||
totalThreats++;
|
||||
}
|
||||
|
||||
if (other != nullptr) {
|
||||
totalLevel += other->level;
|
||||
totalThreats++;
|
||||
|
||||
auto* team = TeamManager::Instance()->GetTeam(other->id);
|
||||
|
||||
if (team != nullptr) {
|
||||
for (const auto& member : team->members) {
|
||||
const auto memberEntityOpt = NejlikaData::GetAdditionalEntityData(member);
|
||||
|
||||
if (!memberEntityOpt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& memberEntity = *memberEntityOpt.value();
|
||||
|
||||
if (other->id == memberEntity.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
totalLevel += memberEntity.level;
|
||||
totalThreats++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (totalThreats == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto averageLevel = totalLevel / totalThreats;
|
||||
|
||||
// Can't rescale to a lower level
|
||||
if (averageLevel <= level) {
|
||||
return;
|
||||
}
|
||||
|
||||
level = averageLevel;
|
||||
|
||||
auto* destroyable = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyable == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
float healthPercentage = destroyable->GetMaxHealth() == 0 ? 1 : static_cast<float>(destroyable->GetHealth()) / destroyable->GetMaxHealth();
|
||||
float armorPercentage = destroyable->GetMaxArmor() == 0 ? 1 : static_cast<float>(destroyable->GetArmor()) / destroyable->GetMaxArmor();
|
||||
float imaginationPercentage = destroyable->GetMaxImagination() == 0 ? 1 : static_cast<float>(destroyable->GetImagination()) / destroyable->GetMaxImagination();
|
||||
|
||||
RollStandardModifiers(level);
|
||||
|
||||
ApplyToEntity();
|
||||
|
||||
destroyable->SetHealth(static_cast<int32_t>(destroyable->GetMaxHealth() * healthPercentage));
|
||||
destroyable->SetArmor(static_cast<int32_t>(destroyable->GetMaxArmor() * armorPercentage));
|
||||
destroyable->SetImagination(static_cast<int32_t>(destroyable->GetMaxImagination() * imaginationPercentage));
|
||||
|
||||
LOG("Rescaled entity %i to level %d", entity->GetLOT(), level);
|
||||
}
|
||||
109
dGame/AdditionalEntityData.h
Normal file
109
dGame/AdditionalEntityData.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
#include "ModifierInstance.h"
|
||||
#include "EntityTemplate.h"
|
||||
#include "UpgradeTriggerType.h"
|
||||
#include "TriggerParameters.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class AdditionalEntityData
|
||||
{
|
||||
public:
|
||||
AdditionalEntityData() = default;
|
||||
|
||||
AdditionalEntityData(LWOOBJID id, LOT lot) : id(id), lot(lot) {}
|
||||
|
||||
float CalculateModifier(ModifierType type, ModifierOperator op, bool resistance) const;
|
||||
|
||||
float CalculateModifier(ModifierType type, const std::vector<ModifierInstance>& additionalModifiers, ModifierOperator op, bool resistance) const;
|
||||
|
||||
float CalculateModifier(ModifierType type, int32_t level) const;
|
||||
|
||||
float CalculateModifier(ModifierType type) const;
|
||||
|
||||
float CalculateFinalModifier(ModifierType type, const std::vector<ModifierInstance>& additionalModifiers, int32_t level) const;
|
||||
|
||||
float CalculateResistance(ModifierType type) const;
|
||||
|
||||
/**
|
||||
* @brief Calculate the multiplier for a given modifier type. With a base value of 100 (%).
|
||||
*
|
||||
* @param type The modifier type.
|
||||
* @return The multiplier.
|
||||
*/
|
||||
float CalculateMultiplier(ModifierType type) const;
|
||||
|
||||
/**
|
||||
* @brief Calculate the multiplier for a given modifier type. With a base value of 100 (%).
|
||||
*
|
||||
* @param type The modifier type.
|
||||
* @param additionalModifiers Additional modifiers to apply.
|
||||
* @return The multiplier.
|
||||
*/
|
||||
float CalculateMultiplier(ModifierType type, const std::vector<ModifierInstance>& additionalModifiers) const;
|
||||
|
||||
/**
|
||||
* @brief Calculate damage conversation mapping.
|
||||
*
|
||||
* @param additionalModifiers Additional modifiers to apply.
|
||||
*
|
||||
* @return The damage conversion mapping.
|
||||
*/
|
||||
std::unordered_map<ModifierType, std::unordered_map<ModifierType, float>> CalculateDamageConversion(std::vector<ModifierInstance>& additionalModifiers) const;
|
||||
|
||||
void ApplyToEntity();
|
||||
|
||||
void CheckForRescale(AdditionalEntityData* other);
|
||||
|
||||
int32_t GetLevel() const { return level; }
|
||||
|
||||
LWOOBJID GetID() const { return id; }
|
||||
|
||||
LOT GetLOT() const { return lot; }
|
||||
|
||||
const std::unordered_set<LWOOBJID>& GetUpgradeItems() const { return upgradeItems; }
|
||||
|
||||
void AddUpgradeItem(LWOOBJID id) { upgradeItems.insert(id); }
|
||||
|
||||
void RemoveUpgradeItem(LWOOBJID id) { upgradeItems.erase(id); }
|
||||
|
||||
std::vector<ModifierInstance> TriggerUpgradeItems(UpgradeTriggerType triggerType, const TriggerParameters& params);
|
||||
|
||||
std::vector<ModifierInstance> TriggerUpgradeItems(UpgradeTriggerType triggerType);
|
||||
|
||||
void InitializeSkills();
|
||||
|
||||
void AddSkills(LWOOBJID item);
|
||||
void RemoveSkills(LOT lot);
|
||||
|
||||
const std::vector<ModifierInstance>& GetActiveModifiers() const { return activeModifiers; }
|
||||
|
||||
std::vector<ModifierInstance> CalculateMainWeaponDamage();
|
||||
|
||||
private:
|
||||
void RollStandardModifiers(int32_t level);
|
||||
|
||||
void TriggerPassiveRegeneration();
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
std::vector<ModifierInstance> standardModifiers;
|
||||
std::vector<ModifierInstance> activeModifiers;
|
||||
std::unordered_set<LWOOBJID> upgradeItems;
|
||||
|
||||
LWOOBJID id;
|
||||
LOT lot;
|
||||
|
||||
int32_t level = 1;
|
||||
};
|
||||
|
||||
}
|
||||
240
dGame/AdditionalItemData.cpp
Normal file
240
dGame/AdditionalItemData.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "AdditionalItemData.h"
|
||||
|
||||
#include "Item.h"
|
||||
#include "eItemType.h"
|
||||
#include "NejlikaData.h"
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
nejlika::AdditionalItemData::AdditionalItemData(Item* item)
|
||||
{
|
||||
const auto& config = item->GetConfig();
|
||||
|
||||
for (const auto& entry : config)
|
||||
{
|
||||
if (entry->GetKey() != u"modifiers")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto str = entry->GetValueAsString();
|
||||
|
||||
if (str.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const auto json = nlohmann::json::parse(str);
|
||||
|
||||
Load(json);
|
||||
}
|
||||
catch (const nlohmann::json::exception& e)
|
||||
{
|
||||
std::cout << "Failed to parse additional item data: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nejlika::AdditionalItemData::AdditionalItemData(const nlohmann::json& json) {
|
||||
Load(json);
|
||||
}
|
||||
|
||||
void nejlika::AdditionalItemData::Load(const nlohmann::json& json) {
|
||||
if (json.contains("names"))
|
||||
{
|
||||
for (const auto& name : json["names"])
|
||||
{
|
||||
modifierNames.emplace_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("instances"))
|
||||
{
|
||||
for (const auto& instance : json["instances"])
|
||||
{
|
||||
modifierInstances.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::AdditionalItemData::ToJson() const {
|
||||
nlohmann::json json;
|
||||
|
||||
json["names"] = nlohmann::json::array();
|
||||
|
||||
for (const auto& name : modifierNames)
|
||||
{
|
||||
json["names"].push_back(name.ToJson());
|
||||
}
|
||||
|
||||
json["instances"] = nlohmann::json::array();
|
||||
|
||||
for (const auto& instance : modifierInstances)
|
||||
{
|
||||
json["instances"].push_back(instance.ToJson());
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void nejlika::AdditionalItemData::Save(Item* item) {
|
||||
auto& config = item->GetConfig();
|
||||
|
||||
// Remove the old data
|
||||
for (size_t i = 0; i < config.size(); i++)
|
||||
{
|
||||
if (config[i]->GetKey() == u"modifiers")
|
||||
{
|
||||
config.erase(config.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << ToJson().dump();
|
||||
|
||||
std::cout << ss.str() << std::endl;
|
||||
|
||||
config.push_back(new LDFData<std::string>(u"modifiers", ToJson().dump()));
|
||||
}
|
||||
|
||||
void nejlika::AdditionalItemData::RollModifiers(Item* item, int32_t level) {
|
||||
modifierNames.clear();
|
||||
modifierInstances.clear();
|
||||
|
||||
const auto& info = item->GetInfo();
|
||||
|
||||
const auto itemType = static_cast<eItemType>(info.itemType);
|
||||
const auto itemRarity = info.rarity == 0 ? 1 : info.rarity;
|
||||
|
||||
uint32_t rarityRollPrefix = 0;
|
||||
uint32_t rarityRollSuffix = 0;
|
||||
|
||||
// Generate (itemRarity) amout of names and modifiers rolls, take the highest rarity. 0-1000
|
||||
for (int i = 0; i < itemRarity; i++) {
|
||||
auto roll = GeneralUtils::GenerateRandomNumber<uint32_t>() % 1000;
|
||||
|
||||
if (roll > rarityRollPrefix) {
|
||||
rarityRollPrefix = roll;
|
||||
}
|
||||
|
||||
roll = GeneralUtils::GenerateRandomNumber<uint32_t>() % 1000;
|
||||
|
||||
if (roll > rarityRollSuffix) {
|
||||
rarityRollSuffix = roll;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& templates = NejlikaData::GetModifierNameTemplates();
|
||||
|
||||
std::vector<ModifierNameTemplate> availablePrefixes;
|
||||
std::vector<ModifierNameTemplate> availableSuffixes;
|
||||
|
||||
for (const auto& [type, nameTemplates] : templates) {
|
||||
for (const auto& nameTemplate : nameTemplates) {
|
||||
if (type != ModifierNameType::Prefix && type != ModifierNameType::Suffix) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nameTemplate.GetMinLevel() > level || nameTemplate.GetMaxLevel() < level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto rarity = nameTemplate.GetRarity();
|
||||
|
||||
if (rarity == ModifierRarity::Common) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& itemTypes = nameTemplate.GetItemTypes();
|
||||
|
||||
if (std::find(itemTypes.begin(), itemTypes.end(), itemType) == itemTypes.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
Uncommon: rarityRoll > 500,
|
||||
Rare: rarityRoll > 900,
|
||||
Epic: rarityRoll > 990,
|
||||
Legendary: rarityRoll = 999
|
||||
*/
|
||||
const auto roll = type == ModifierNameType::Prefix ? rarityRollPrefix : rarityRollSuffix;
|
||||
|
||||
if (rarity == ModifierRarity::Uncommon && roll > 900) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rarity == ModifierRarity::Rare && (roll <= 900 || roll > 990)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rarity == ModifierRarity::Epic && (roll <= 990 || roll > 998)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rarity == ModifierRarity::Legendary && roll != 999) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == ModifierNameType::Prefix) {
|
||||
availablePrefixes.push_back(nameTemplate);
|
||||
}
|
||||
else {
|
||||
availableSuffixes.push_back(nameTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!availablePrefixes.empty()) {
|
||||
const auto& prefix = availablePrefixes[GeneralUtils::GenerateRandomNumber<uint32_t>() % availablePrefixes.size()];
|
||||
|
||||
modifierNames.push_back(ModifierName(prefix));
|
||||
|
||||
const auto modifiers = prefix.GenerateModifiers(level);
|
||||
|
||||
modifierInstances.insert(modifierInstances.end(), modifiers.begin(), modifiers.end());
|
||||
}
|
||||
|
||||
if (!availableSuffixes.empty()) {
|
||||
const auto& suffix = availableSuffixes[GeneralUtils::GenerateRandomNumber<uint32_t>() % availableSuffixes.size()];
|
||||
|
||||
modifierNames.push_back(ModifierName(suffix));
|
||||
|
||||
const auto modifiers = suffix.GenerateModifiers(level);
|
||||
|
||||
modifierInstances.insert(modifierInstances.end(), modifiers.begin(), modifiers.end());
|
||||
}
|
||||
|
||||
const auto& itemTemplateVec = NejlikaData::GetModifierNameTemplates(ModifierNameType::Object);
|
||||
|
||||
std::vector<const ModifierNameTemplate*> availableObjects;
|
||||
|
||||
for (const auto& itemTemplate : itemTemplateVec) {
|
||||
if (itemTemplate.GetMinLevel() > level || itemTemplate.GetMaxLevel() < level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (itemTemplate.GetLOT() != static_cast<int32_t>(item->GetLot())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
availableObjects.push_back(&itemTemplate);
|
||||
}
|
||||
|
||||
if (availableObjects.empty()) {
|
||||
Save(item);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& itemTemplate = *availableObjects[GeneralUtils::GenerateRandomNumber<uint32_t>() % availableObjects.size()];
|
||||
|
||||
const auto itemModifiers = itemTemplate.GenerateModifiers(level);
|
||||
|
||||
modifierInstances.insert(modifierInstances.end(), itemModifiers.begin(), itemModifiers.end());
|
||||
|
||||
Save(item);
|
||||
}
|
||||
|
||||
41
dGame/AdditionalItemData.h
Normal file
41
dGame/AdditionalItemData.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "ModifierName.h"
|
||||
#include "ModifierInstance.h"
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
class Item;
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class AdditionalItemData
|
||||
{
|
||||
public:
|
||||
AdditionalItemData() = default;
|
||||
|
||||
AdditionalItemData(Item* item);
|
||||
|
||||
AdditionalItemData(const nlohmann::json& json);
|
||||
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
void Load(const nlohmann::json& json);
|
||||
|
||||
void Save(Item* item);
|
||||
|
||||
void RollModifiers(Item* item, int32_t level);
|
||||
|
||||
const std::vector<ModifierName>& GetModifierNames() const { return modifierNames; }
|
||||
const std::vector<ModifierInstance>& GetModifierInstances() const { return modifierInstances; }
|
||||
|
||||
private:
|
||||
std::vector<ModifierName> modifierNames;
|
||||
std::vector<ModifierInstance> modifierInstances;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -6,18 +6,49 @@ set(DGAME_SOURCES "Character.cpp"
|
||||
"TeamManager.cpp"
|
||||
"TradingManager.cpp"
|
||||
"User.cpp"
|
||||
"UserManager.cpp")
|
||||
"UserManager.cpp"
|
||||
"ModifierTemplate.cpp"
|
||||
"ModifierInstance.cpp"
|
||||
"ModifierRarity.cpp"
|
||||
"ModifierType.cpp"
|
||||
"ModifierScale.cpp"
|
||||
"ModifierName.cpp"
|
||||
"ModifierNameTemplate.cpp"
|
||||
"NejlikaData.cpp"
|
||||
"AdditionalItemData.cpp"
|
||||
"EntityTemplate.cpp"
|
||||
"AdditionalEntityData.cpp"
|
||||
"NejlikaHooks.cpp"
|
||||
"UpgradeTemplate.cpp"
|
||||
"UpgradeEffect.cpp"
|
||||
"Lookup.cpp"
|
||||
"NejlikaHelpers.cpp"
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/dScripts
|
||||
${PROJECT_SOURCE_DIR}/dGame
|
||||
)
|
||||
|
||||
add_library(dGameBase ${DGAME_SOURCES})
|
||||
add_library(dGameBase OBJECT ${DGAME_SOURCES})
|
||||
target_precompile_headers(dGameBase PRIVATE ${HEADERS_DGAME})
|
||||
target_link_libraries(dGameBase
|
||||
PUBLIC dDatabase dPhysics
|
||||
INTERFACE dComponents dEntity)
|
||||
target_include_directories(dGameBase PUBLIC "." "dEntity"
|
||||
PRIVATE "dComponents" "dGameMessages" "dBehaviors" "dMission" "dUtilities" "dInventory"
|
||||
$<TARGET_PROPERTY:dPropertyBehaviors,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
"${PROJECT_SOURCE_DIR}/dCommon"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon/dClient"
|
||||
# dDatabase
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
|
||||
# dPhysics
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include"
|
||||
"${PROJECT_SOURCE_DIR}/dZoneManager"
|
||||
)
|
||||
|
||||
add_subdirectory(dBehaviors)
|
||||
add_subdirectory(dComponents)
|
||||
@@ -28,7 +59,26 @@ add_subdirectory(dMission)
|
||||
add_subdirectory(dPropertyBehaviors)
|
||||
add_subdirectory(dUtilities)
|
||||
|
||||
add_library(dGame INTERFACE)
|
||||
target_link_libraries(dGame INTERFACE
|
||||
dGameBase dBehaviors dComponents dEntity dGameMessages dInventory dMission dPropertyBehaviors dUtilities dScripts
|
||||
add_library(dGame STATIC
|
||||
$<TARGET_OBJECTS:dGameBase>
|
||||
$<TARGET_OBJECTS:dBehaviors>
|
||||
$<TARGET_OBJECTS:dComponents>
|
||||
$<TARGET_OBJECTS:dEntity>
|
||||
$<TARGET_OBJECTS:dGameMessages>
|
||||
$<TARGET_OBJECTS:dInventory>
|
||||
$<TARGET_OBJECTS:dMission>
|
||||
$<TARGET_OBJECTS:dPropertyBehaviors>
|
||||
$<TARGET_OBJECTS:dUtilities>
|
||||
)
|
||||
target_link_libraries(dGame INTERFACE dNet)
|
||||
target_include_directories(dGame INTERFACE
|
||||
$<TARGET_PROPERTY:dGameBase,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dBehaviors,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dComponents,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dEntity,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dGameMessages,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dInventory,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dMission,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dPropertyBehaviors,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:dUtilities,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
)
|
||||
|
||||
@@ -27,12 +27,9 @@ Character::Character(uint32_t id, User* parentUser) {
|
||||
m_ID = id;
|
||||
m_ParentUser = parentUser;
|
||||
m_OurEntity = nullptr;
|
||||
m_Doc = nullptr;
|
||||
}
|
||||
|
||||
Character::~Character() {
|
||||
if (m_Doc) delete m_Doc;
|
||||
m_Doc = nullptr;
|
||||
m_OurEntity = nullptr;
|
||||
m_ParentUser = nullptr;
|
||||
}
|
||||
@@ -55,8 +52,6 @@ void Character::UpdateInfoFromDatabase() {
|
||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||
m_ZoneCloneID = 0;
|
||||
|
||||
m_Doc = nullptr;
|
||||
|
||||
//Quickly and dirtly parse the xmlData to get the info we need:
|
||||
DoQuickXMLDataParse();
|
||||
|
||||
@@ -70,18 +65,13 @@ void Character::UpdateInfoFromDatabase() {
|
||||
}
|
||||
|
||||
void Character::UpdateFromDatabase() {
|
||||
if (m_Doc) delete m_Doc;
|
||||
UpdateInfoFromDatabase();
|
||||
}
|
||||
|
||||
void Character::DoQuickXMLDataParse() {
|
||||
if (m_XMLData.size() == 0) return;
|
||||
|
||||
delete m_Doc;
|
||||
m_Doc = new tinyxml2::XMLDocument();
|
||||
if (!m_Doc) return;
|
||||
|
||||
if (m_Doc->Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
|
||||
if (m_Doc.Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
|
||||
LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
|
||||
} else {
|
||||
LOG("Failed to load xmlData!");
|
||||
@@ -89,7 +79,7 @@ void Character::DoQuickXMLDataParse() {
|
||||
return;
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement* mf = m_Doc->FirstChildElement("obj")->FirstChildElement("mf");
|
||||
tinyxml2::XMLElement* mf = m_Doc.FirstChildElement("obj")->FirstChildElement("mf");
|
||||
if (!mf) {
|
||||
LOG("Failed to find mf tag!");
|
||||
return;
|
||||
@@ -108,7 +98,7 @@ void Character::DoQuickXMLDataParse() {
|
||||
mf->QueryAttribute("ess", &m_Eyes);
|
||||
mf->QueryAttribute("ms", &m_Mouth);
|
||||
|
||||
tinyxml2::XMLElement* inv = m_Doc->FirstChildElement("obj")->FirstChildElement("inv");
|
||||
tinyxml2::XMLElement* inv = m_Doc.FirstChildElement("obj")->FirstChildElement("inv");
|
||||
if (!inv) {
|
||||
LOG("Char has no inv!");
|
||||
return;
|
||||
@@ -141,7 +131,7 @@ void Character::DoQuickXMLDataParse() {
|
||||
}
|
||||
|
||||
|
||||
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (character) {
|
||||
character->QueryAttribute("cc", &m_Coins);
|
||||
int32_t gm_level = 0;
|
||||
@@ -205,7 +195,7 @@ void Character::DoQuickXMLDataParse() {
|
||||
character->QueryAttribute("lzrw", &m_OriginalRotation.w);
|
||||
}
|
||||
|
||||
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
|
||||
auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
|
||||
if (flags) {
|
||||
auto* currentChild = flags->FirstChildElement();
|
||||
while (currentChild) {
|
||||
@@ -239,12 +229,10 @@ void Character::SetBuildMode(bool buildMode) {
|
||||
}
|
||||
|
||||
void Character::SaveXMLToDatabase() {
|
||||
if (!m_Doc) return;
|
||||
|
||||
//For metrics, we'll record the time it took to save:
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char");
|
||||
tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
|
||||
if (character) {
|
||||
character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel));
|
||||
character->SetAttribute("cc", m_Coins);
|
||||
@@ -266,11 +254,11 @@ void Character::SaveXMLToDatabase() {
|
||||
}
|
||||
|
||||
auto emotes = character->FirstChildElement("ue");
|
||||
if (!emotes) emotes = m_Doc->NewElement("ue");
|
||||
if (!emotes) emotes = m_Doc.NewElement("ue");
|
||||
|
||||
emotes->DeleteChildren();
|
||||
for (int emoteID : m_UnlockedEmotes) {
|
||||
auto emote = m_Doc->NewElement("e");
|
||||
auto emote = m_Doc.NewElement("e");
|
||||
emote->SetAttribute("id", emoteID);
|
||||
|
||||
emotes->LinkEndChild(emote);
|
||||
@@ -280,15 +268,15 @@ void Character::SaveXMLToDatabase() {
|
||||
}
|
||||
|
||||
//Export our flags:
|
||||
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
|
||||
auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
|
||||
if (!flags) {
|
||||
flags = m_Doc->NewElement("flag"); //Create a flags tag if we don't have one
|
||||
m_Doc->FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
|
||||
flags = m_Doc.NewElement("flag"); //Create a flags tag if we don't have one
|
||||
m_Doc.FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
|
||||
}
|
||||
|
||||
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
|
||||
for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) {
|
||||
auto* f = m_Doc->NewElement("f");
|
||||
auto* f = m_Doc.NewElement("f");
|
||||
f->SetAttribute("id", flag.first);
|
||||
|
||||
//Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute.
|
||||
@@ -301,7 +289,7 @@ void Character::SaveXMLToDatabase() {
|
||||
|
||||
// Prevents the news feed from showing up on world transfers
|
||||
if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) {
|
||||
auto* s = m_Doc->NewElement("s");
|
||||
auto* s = m_Doc.NewElement("s");
|
||||
s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE);
|
||||
flags->LinkEndChild(s);
|
||||
}
|
||||
@@ -326,7 +314,7 @@ void Character::SaveXMLToDatabase() {
|
||||
|
||||
void Character::SetIsNewLogin() {
|
||||
// If we dont have a flag element, then we cannot have a s element as a child of flag.
|
||||
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
|
||||
auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
|
||||
if (!flags) return;
|
||||
|
||||
auto* currentChild = flags->FirstChildElement();
|
||||
@@ -344,7 +332,7 @@ void Character::SetIsNewLogin() {
|
||||
void Character::WriteToDatabase() {
|
||||
//Dump our xml into m_XMLData:
|
||||
tinyxml2::XMLPrinter printer(0, true, 0);
|
||||
m_Doc->Print(&printer);
|
||||
m_Doc.Print(&printer);
|
||||
|
||||
//Finally, save to db:
|
||||
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
|
||||
@@ -421,15 +409,15 @@ void Character::SetRetroactiveFlags() {
|
||||
|
||||
void Character::SaveXmlRespawnCheckpoints() {
|
||||
//Export our respawn points:
|
||||
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res");
|
||||
auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
|
||||
if (!points) {
|
||||
points = m_Doc->NewElement("res");
|
||||
m_Doc->FirstChildElement("obj")->LinkEndChild(points);
|
||||
points = m_Doc.NewElement("res");
|
||||
m_Doc.FirstChildElement("obj")->LinkEndChild(points);
|
||||
}
|
||||
|
||||
points->DeleteChildren();
|
||||
for (const auto& point : m_WorldRespawnCheckpoints) {
|
||||
auto* r = m_Doc->NewElement("r");
|
||||
auto* r = m_Doc.NewElement("r");
|
||||
r->SetAttribute("w", point.first);
|
||||
|
||||
r->SetAttribute("x", point.second.x);
|
||||
@@ -443,7 +431,7 @@ void Character::SaveXmlRespawnCheckpoints() {
|
||||
void Character::LoadXmlRespawnCheckpoints() {
|
||||
m_WorldRespawnCheckpoints.clear();
|
||||
|
||||
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res");
|
||||
auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
|
||||
if (!points) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
void LoadXmlRespawnCheckpoints();
|
||||
|
||||
const std::string& GetXMLData() const { return m_XMLData; }
|
||||
tinyxml2::XMLDocument* GetXMLDoc() const { return m_Doc; }
|
||||
const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
|
||||
|
||||
/**
|
||||
* Out of abundance of safety and clarity of what this saves, this is its own function.
|
||||
@@ -623,7 +623,7 @@ private:
|
||||
/**
|
||||
* The character XML belonging to this character
|
||||
*/
|
||||
tinyxml2::XMLDocument* m_Doc;
|
||||
tinyxml2::XMLDocument m_Doc;
|
||||
|
||||
/**
|
||||
* Title of an announcement this character made (reserved for GMs)
|
||||
|
||||
302
dGame/Entity.cpp
302
dGame/Entity.cpp
@@ -96,6 +96,9 @@
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
|
||||
Observable<Entity*, const PositionUpdate&> Entity::OnPlayerPositionUpdate;
|
||||
Observable<Entity*> Entity::OnReadyForUpdates;
|
||||
|
||||
Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) {
|
||||
m_ObjectID = objectID;
|
||||
m_TemplateID = info.lot;
|
||||
@@ -146,17 +149,15 @@ Entity::~Entity() {
|
||||
return;
|
||||
}
|
||||
|
||||
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerExit(zoneControl, this);
|
||||
auto* zoneControl = Game::entityManager->GetZoneControlEntity();
|
||||
if (zoneControl) {
|
||||
zoneControl->GetScript()->OnPlayerExit(zoneControl, this);
|
||||
}
|
||||
|
||||
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
scriptEntity->GetScript()->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,8 +479,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
|
||||
auto* xmlDoc = m_Character ? m_Character->GetXMLDoc() : nullptr;
|
||||
AddComponent<InventoryComponent>(xmlDoc);
|
||||
AddComponent<InventoryComponent>();
|
||||
}
|
||||
// if this component exists, then we initialize it. it's value is always 0
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
|
||||
@@ -733,15 +733,21 @@ void Entity::Initialize() {
|
||||
// if we have a moving platform path, then we need a moving platform component
|
||||
if (path->pathType == PathType::MovingPlatform) {
|
||||
AddComponent<MovingPlatformComponent>(pathName);
|
||||
// else if we are a movement path
|
||||
} /*else if (path->pathType == PathType::Movement) {
|
||||
auto movementAIcomp = GetComponent<MovementAIComponent>();
|
||||
if (movementAIcomp){
|
||||
// TODO: set path in existing movementAIComp
|
||||
} else if (path->pathType == PathType::Movement) {
|
||||
auto movementAIcomponent = GetComponent<MovementAIComponent>();
|
||||
if (movementAIcomponent && combatAiId == 0) {
|
||||
movementAIcomponent->SetPath(pathName);
|
||||
} else {
|
||||
// TODO: create movementAIcomp and set path
|
||||
MovementAIInfo moveInfo = MovementAIInfo();
|
||||
moveInfo.movementType = "";
|
||||
moveInfo.wanderChance = 0;
|
||||
moveInfo.wanderRadius = 16;
|
||||
moveInfo.wanderSpeed = 2.5f;
|
||||
moveInfo.wanderDelayMax = 5;
|
||||
moveInfo.wanderDelayMin = 2;
|
||||
AddComponent<MovementAIComponent>(moveInfo);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
} else {
|
||||
// else we still need to setup moving platform if it has a moving platform comp but no path
|
||||
int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
|
||||
@@ -762,9 +768,7 @@ void Entity::Initialize() {
|
||||
|
||||
// Hacky way to trigger these when the object has had a chance to get constructed
|
||||
AddCallbackTimer(0, [this]() {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnStartup(this);
|
||||
}
|
||||
this->GetScript()->OnStartup(this);
|
||||
});
|
||||
|
||||
if (!m_Character && Game::entityManager->GetGhostingEnabled()) {
|
||||
@@ -839,17 +843,6 @@ bool Entity::HasComponent(const eReplicaComponentType componentId) const {
|
||||
return m_Components.find(componentId) != m_Components.end();
|
||||
}
|
||||
|
||||
std::vector<ScriptComponent*> Entity::GetScriptComponents() {
|
||||
std::vector<ScriptComponent*> comps;
|
||||
for (std::pair<eReplicaComponentType, void*> p : m_Components) {
|
||||
if (p.first == eReplicaComponentType::SCRIPT) {
|
||||
comps.push_back(static_cast<ScriptComponent*>(p.second));
|
||||
}
|
||||
}
|
||||
|
||||
return comps;
|
||||
}
|
||||
|
||||
void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName) {
|
||||
if (notificationName == "HitOrHealResult" || notificationName == "Hit") {
|
||||
auto* destroyableComponent = GetComponent<DestroyableComponent>();
|
||||
@@ -899,34 +892,34 @@ void Entity::SetGMLevel(eGameMasterLevel value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType) {
|
||||
void Entity::WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) {
|
||||
if (packetType == eReplicaPacketType::CONSTRUCTION) {
|
||||
outBitStream->Write(m_ObjectID);
|
||||
outBitStream->Write(m_TemplateID);
|
||||
outBitStream.Write(m_ObjectID);
|
||||
outBitStream.Write(m_TemplateID);
|
||||
|
||||
if (IsPlayer()) {
|
||||
std::string name = m_Character != nullptr ? m_Character->GetName() : "Invalid";
|
||||
outBitStream->Write<uint8_t>(uint8_t(name.size()));
|
||||
outBitStream.Write<uint8_t>(uint8_t(name.size()));
|
||||
|
||||
for (size_t i = 0; i < name.size(); ++i) {
|
||||
outBitStream->Write<uint16_t>(name[i]);
|
||||
outBitStream.Write<uint16_t>(name[i]);
|
||||
}
|
||||
} else {
|
||||
const auto& name = GetVar<std::string>(u"npcName");
|
||||
outBitStream->Write<uint8_t>(uint8_t(name.size()));
|
||||
outBitStream.Write<uint8_t>(uint8_t(name.size()));
|
||||
|
||||
for (size_t i = 0; i < name.size(); ++i) {
|
||||
outBitStream->Write<uint16_t>(name[i]);
|
||||
outBitStream.Write<uint16_t>(name[i]);
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write<uint32_t>(0); //Time since created on server
|
||||
outBitStream.Write<uint32_t>(0); //Time since created on server
|
||||
|
||||
const auto& syncLDF = GetVar<std::vector<std::u16string>>(u"syncLDF");
|
||||
|
||||
// Only sync for models.
|
||||
if (m_Settings.size() > 0 && (GetComponent<ModelComponent>() && !GetComponent<PetComponent>())) {
|
||||
outBitStream->Write1(); //ldf data
|
||||
outBitStream.Write1(); //ldf data
|
||||
|
||||
RakNet::BitStream settingStream;
|
||||
int32_t numberOfValidKeys = m_Settings.size();
|
||||
@@ -943,13 +936,13 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
|
||||
|
||||
for (LDFBaseData* data : m_Settings) {
|
||||
if (data && data->GetValueType() != eLDFType::LDF_TYPE_UNKNOWN) {
|
||||
data->WriteToPacket(&settingStream);
|
||||
data->WriteToPacket(settingStream);
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write(settingStream.GetNumberOfBytesUsed() + 1);
|
||||
outBitStream->Write<uint8_t>(0); //no compression used
|
||||
outBitStream->Write(settingStream);
|
||||
outBitStream.Write(settingStream.GetNumberOfBytesUsed() + 1);
|
||||
outBitStream.Write<uint8_t>(0); //no compression used
|
||||
outBitStream.Write(settingStream);
|
||||
} else if (!syncLDF.empty()) {
|
||||
std::vector<LDFBaseData*> ldfData;
|
||||
|
||||
@@ -957,79 +950,79 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
|
||||
ldfData.push_back(GetVarData(data));
|
||||
}
|
||||
|
||||
outBitStream->Write1(); //ldf data
|
||||
outBitStream.Write1(); //ldf data
|
||||
|
||||
RakNet::BitStream settingStream;
|
||||
settingStream.Write<uint32_t>(ldfData.size());
|
||||
for (LDFBaseData* data : ldfData) {
|
||||
if (data) {
|
||||
data->WriteToPacket(&settingStream);
|
||||
data->WriteToPacket(settingStream);
|
||||
}
|
||||
}
|
||||
|
||||
outBitStream->Write(settingStream.GetNumberOfBytesUsed() + 1);
|
||||
outBitStream->Write<uint8_t>(0); //no compression used
|
||||
outBitStream->Write(settingStream);
|
||||
outBitStream.Write(settingStream.GetNumberOfBytesUsed() + 1);
|
||||
outBitStream.Write<uint8_t>(0); //no compression used
|
||||
outBitStream.Write(settingStream);
|
||||
} else {
|
||||
outBitStream->Write0(); //No ldf data
|
||||
outBitStream.Write0(); //No ldf data
|
||||
}
|
||||
|
||||
TriggerComponent* triggerComponent;
|
||||
if (TryGetComponent(eReplicaComponentType::TRIGGER, triggerComponent)) {
|
||||
// has trigger component, check to see if we have events to handle
|
||||
auto* trigger = triggerComponent->GetTrigger();
|
||||
outBitStream->Write<bool>(trigger && trigger->events.size() > 0);
|
||||
outBitStream.Write<bool>(trigger && trigger->events.size() > 0);
|
||||
} else { // no trigger componenet, so definitely no triggers
|
||||
outBitStream->Write0();
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
|
||||
if (m_ParentEntity != nullptr || m_SpawnerID != 0) {
|
||||
outBitStream->Write1();
|
||||
if (m_ParentEntity != nullptr) outBitStream->Write(GeneralUtils::SetBit(m_ParentEntity->GetObjectID(), static_cast<uint32_t>(eObjectBits::CLIENT)));
|
||||
else if (m_Spawner != nullptr && m_Spawner->m_Info.isNetwork) outBitStream->Write(m_SpawnerID);
|
||||
else outBitStream->Write(GeneralUtils::SetBit(m_SpawnerID, static_cast<uint32_t>(eObjectBits::CLIENT)));
|
||||
} else outBitStream->Write0();
|
||||
outBitStream.Write1();
|
||||
if (m_ParentEntity != nullptr) outBitStream.Write(GeneralUtils::SetBit(m_ParentEntity->GetObjectID(), static_cast<uint32_t>(eObjectBits::CLIENT)));
|
||||
else if (m_Spawner != nullptr && m_Spawner->m_Info.isNetwork) outBitStream.Write(m_SpawnerID);
|
||||
else outBitStream.Write(GeneralUtils::SetBit(m_SpawnerID, static_cast<uint32_t>(eObjectBits::CLIENT)));
|
||||
} else outBitStream.Write0();
|
||||
|
||||
outBitStream->Write(m_HasSpawnerNodeID);
|
||||
if (m_HasSpawnerNodeID) outBitStream->Write(m_SpawnerNodeID);
|
||||
outBitStream.Write(m_HasSpawnerNodeID);
|
||||
if (m_HasSpawnerNodeID) outBitStream.Write(m_SpawnerNodeID);
|
||||
|
||||
//outBitStream->Write0(); //Spawner node id
|
||||
//outBitStream.Write0(); //Spawner node id
|
||||
|
||||
if (m_Scale == 1.0f || m_Scale == 0.0f) outBitStream->Write0();
|
||||
if (m_Scale == 1.0f || m_Scale == 0.0f) outBitStream.Write0();
|
||||
else {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_Scale);
|
||||
outBitStream.Write1();
|
||||
outBitStream.Write(m_Scale);
|
||||
}
|
||||
|
||||
outBitStream->Write0(); //ObjectWorldState
|
||||
outBitStream.Write0(); //ObjectWorldState
|
||||
|
||||
if (m_GMLevel != eGameMasterLevel::CIVILIAN) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_GMLevel);
|
||||
} else outBitStream->Write0(); //No GM Level
|
||||
outBitStream.Write1();
|
||||
outBitStream.Write(m_GMLevel);
|
||||
} else outBitStream.Write0(); //No GM Level
|
||||
}
|
||||
|
||||
// Only serialize parent / child info should the info be dirty (changed) or if this is the construction of the entity.
|
||||
outBitStream->Write(m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION);
|
||||
outBitStream.Write(m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION);
|
||||
if (m_IsParentChildDirty || packetType == eReplicaPacketType::CONSTRUCTION) {
|
||||
m_IsParentChildDirty = false;
|
||||
outBitStream->Write(m_ParentEntity != nullptr);
|
||||
outBitStream.Write(m_ParentEntity != nullptr);
|
||||
if (m_ParentEntity) {
|
||||
outBitStream->Write(m_ParentEntity->GetObjectID());
|
||||
outBitStream->Write0();
|
||||
outBitStream.Write(m_ParentEntity->GetObjectID());
|
||||
outBitStream.Write0();
|
||||
}
|
||||
outBitStream->Write(m_ChildEntities.size() > 0);
|
||||
outBitStream.Write(m_ChildEntities.size() > 0);
|
||||
if (m_ChildEntities.size() > 0) {
|
||||
outBitStream->Write<uint16_t>(m_ChildEntities.size());
|
||||
outBitStream.Write<uint16_t>(m_ChildEntities.size());
|
||||
for (Entity* child : m_ChildEntities) {
|
||||
outBitStream->Write<uint64_t>(child->GetObjectID());
|
||||
outBitStream.Write<uint64_t>(child->GetObjectID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType) {
|
||||
void Entity::WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType) {
|
||||
|
||||
/**
|
||||
* This has to be done in a specific order.
|
||||
@@ -1117,7 +1110,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
possessorComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||
} else {
|
||||
// Should never happen, but just to be safe
|
||||
outBitStream->Write0();
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
LevelProgressionComponent* levelProgressionComponent;
|
||||
@@ -1125,7 +1118,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||
} else {
|
||||
// Should never happen, but just to be safe
|
||||
outBitStream->Write0();
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
PlayerForcedMovementComponent* playerForcedMovementComponent;
|
||||
@@ -1133,7 +1126,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||
} else {
|
||||
// Should never happen, but just to be safe
|
||||
outBitStream->Write0();
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
characterComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||
@@ -1250,10 +1243,10 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
// BBB Component, unused currently
|
||||
// Need to to write0 so that is serialized correctly
|
||||
// TODO: Implement BBB Component
|
||||
outBitStream->Write0();
|
||||
outBitStream.Write0();
|
||||
}
|
||||
|
||||
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) {
|
||||
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument& doc) {
|
||||
//This function should only ever be called from within Character, meaning doc should always exist when this is called.
|
||||
//Naturally, we don't include any non-player components in this update function.
|
||||
|
||||
@@ -1278,9 +1271,7 @@ void Entity::Update(const float deltaTime) {
|
||||
// Remove the timer from the list of timers first so that scripts and events can remove timers without causing iterator invalidation
|
||||
auto timerName = timer.GetName();
|
||||
m_Timers.erase(m_Timers.begin() + timerPosition);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnTimerDone(this, timerName);
|
||||
}
|
||||
GetScript()->OnTimerDone(this, timerName);
|
||||
|
||||
TriggerEvent(eTriggerEventType::TIMER_DONE, this);
|
||||
} else {
|
||||
@@ -1326,9 +1317,7 @@ void Entity::Update(const float deltaTime) {
|
||||
Wake();
|
||||
}
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnUpdate(this);
|
||||
}
|
||||
GetScript()->OnUpdate(this);
|
||||
|
||||
for (const auto& pair : m_Components) {
|
||||
if (pair.second == nullptr) continue;
|
||||
@@ -1345,9 +1334,7 @@ void Entity::OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxN
|
||||
Entity* other = Game::entityManager->GetEntity(otherEntity);
|
||||
if (!other) return;
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnProximityUpdate(this, other, proxName, status);
|
||||
}
|
||||
GetScript()->OnProximityUpdate(this, other, proxName, status);
|
||||
|
||||
RocketLaunchpadControlComponent* rocketComp = GetComponent<RocketLaunchpadControlComponent>();
|
||||
if (!rocketComp) return;
|
||||
@@ -1359,9 +1346,7 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) {
|
||||
auto* other = Game::entityManager->GetEntity(otherEntity);
|
||||
if (!other) return;
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnCollisionPhantom(this, other);
|
||||
}
|
||||
GetScript()->OnCollisionPhantom(this, other);
|
||||
|
||||
for (const auto& callback : m_PhantomCollisionCallbacks) {
|
||||
callback(other);
|
||||
@@ -1400,9 +1385,7 @@ void Entity::OnCollisionLeavePhantom(const LWOOBJID otherEntity) {
|
||||
auto* other = Game::entityManager->GetEntity(otherEntity);
|
||||
if (!other) return;
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnOffCollisionPhantom(this, other);
|
||||
}
|
||||
GetScript()->OnOffCollisionPhantom(this, other);
|
||||
|
||||
TriggerEvent(eTriggerEventType::EXIT, other);
|
||||
|
||||
@@ -1419,46 +1402,32 @@ void Entity::OnCollisionLeavePhantom(const LWOOBJID otherEntity) {
|
||||
}
|
||||
|
||||
void Entity::OnFireEventServerSide(Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnFireEventServerSide(this, sender, args, param1, param2, param3);
|
||||
}
|
||||
GetScript()->OnFireEventServerSide(this, sender, args, param1, param2, param3);
|
||||
}
|
||||
|
||||
void Entity::OnActivityStateChangeRequest(LWOOBJID senderID, int32_t value1, int32_t value2, const std::u16string& stringValue) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnActivityStateChangeRequest(this, senderID, value1, value2, stringValue);
|
||||
}
|
||||
GetScript()->OnActivityStateChangeRequest(this, senderID, value1, value2, stringValue);
|
||||
}
|
||||
|
||||
void Entity::OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName,
|
||||
float_t pathTime, float_t totalTime, int32_t waypoint) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnCinematicUpdate(self, sender, event, pathName, pathTime, totalTime, waypoint);
|
||||
}
|
||||
GetScript()->OnCinematicUpdate(self, sender, event, pathName, pathTime, totalTime, waypoint);
|
||||
}
|
||||
|
||||
void Entity::NotifyObject(Entity* sender, const std::string& name, int32_t param1, int32_t param2) {
|
||||
GameMessages::SendNotifyObject(GetObjectID(), sender->GetObjectID(), GeneralUtils::ASCIIToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnNotifyObject(this, sender, name, param1, param2);
|
||||
}
|
||||
GetScript()->OnNotifyObject(this, sender, name, param1, param2);
|
||||
}
|
||||
|
||||
void Entity::OnEmoteReceived(const int32_t emote, Entity* target) {
|
||||
for (auto* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnEmoteReceived(this, emote, target);
|
||||
}
|
||||
GetScript()->OnEmoteReceived(this, emote, target);
|
||||
}
|
||||
|
||||
void Entity::OnUse(Entity* originator) {
|
||||
TriggerEvent(eTriggerEventType::INTERACT, originator);
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnUse(this, originator);
|
||||
}
|
||||
|
||||
// component base class when
|
||||
GetScript()->OnUse(this, originator);
|
||||
|
||||
for (const auto& pair : m_Components) {
|
||||
if (pair.second == nullptr) continue;
|
||||
@@ -1468,82 +1437,63 @@ void Entity::OnUse(Entity* originator) {
|
||||
}
|
||||
|
||||
void Entity::OnHitOrHealResult(Entity* attacker, int32_t damage) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnHitOrHealResult(this, attacker, damage);
|
||||
}
|
||||
GetScript()->OnHitOrHealResult(this, attacker, damage);
|
||||
}
|
||||
|
||||
void Entity::OnHit(Entity* attacker) {
|
||||
TriggerEvent(eTriggerEventType::HIT, attacker);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnHit(this, attacker);
|
||||
}
|
||||
GetScript()->OnHit(this, attacker);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyEditBegin() {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyEditBegin(this);
|
||||
}
|
||||
GetScript()->OnZonePropertyEditBegin(this);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyEditEnd() {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyEditEnd(this);
|
||||
}
|
||||
GetScript()->OnZonePropertyEditEnd(this);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyModelEquipped() {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyModelEquipped(this);
|
||||
}
|
||||
GetScript()->OnZonePropertyModelEquipped(this);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyModelPlaced(Entity* player) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyModelPlaced(this, player);
|
||||
}
|
||||
GetScript()->OnZonePropertyModelPlaced(this, player);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyModelPickedUp(Entity* player) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyModelPickedUp(this, player);
|
||||
}
|
||||
GetScript()->OnZonePropertyModelPickedUp(this, player);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyModelRemoved(Entity* player) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyModelRemoved(this, player);
|
||||
}
|
||||
GetScript()->OnZonePropertyModelRemoved(this, player);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyModelRemovedWhileEquipped(Entity* player) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyModelRemovedWhileEquipped(this, player);
|
||||
}
|
||||
GetScript()->OnZonePropertyModelRemovedWhileEquipped(this, player);
|
||||
}
|
||||
|
||||
void Entity::OnZonePropertyModelRotated(Entity* player) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnZonePropertyModelRotated(this, player);
|
||||
}
|
||||
GetScript()->OnZonePropertyModelRotated(this, player);
|
||||
}
|
||||
|
||||
void Entity::OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnMessageBoxResponse(this, sender, button, identifier, userData);
|
||||
}
|
||||
GetScript()->OnMessageBoxResponse(this, sender, button, identifier, userData);
|
||||
}
|
||||
|
||||
void Entity::OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnChoiceBoxResponse(this, sender, button, buttonIdentifier, identifier);
|
||||
}
|
||||
GetScript()->OnChoiceBoxResponse(this, sender, button, buttonIdentifier, identifier);
|
||||
}
|
||||
|
||||
void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnRequestActivityExit(sender, player, canceled);
|
||||
}
|
||||
GetScript()->OnRequestActivityExit(sender, player, canceled);
|
||||
}
|
||||
|
||||
CppScripts::Script* const Entity::GetScript() {
|
||||
auto* scriptComponent = GetComponent<ScriptComponent>();
|
||||
auto* script = scriptComponent ? scriptComponent->GetScript() : CppScripts::GetInvalidScript();
|
||||
DluAssert(script != nullptr);
|
||||
return script;
|
||||
}
|
||||
|
||||
void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType) {
|
||||
@@ -1576,9 +1526,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
||||
|
||||
//OMAI WA MOU, SHINDERIU
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnDie(this, murderer);
|
||||
}
|
||||
GetScript()->OnDie(this, murderer);
|
||||
|
||||
if (m_Spawner != nullptr) {
|
||||
m_Spawner->NotifyOfEntityDeath(m_ObjectID);
|
||||
@@ -1589,7 +1537,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
||||
bool waitForDeathAnimation = false;
|
||||
|
||||
if (destroyableComponent) {
|
||||
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
|
||||
waitForDeathAnimation = !destroyableComponent->GetIsSmashable() && destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
|
||||
}
|
||||
|
||||
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
|
||||
@@ -1689,10 +1637,8 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
|
||||
CDObjectSkillsTable* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
|
||||
std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); });
|
||||
for (CDObjectSkills skill : skills) {
|
||||
CDSkillBehaviorTable* skillBehTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
|
||||
|
||||
auto* skillComponent = GetComponent<SkillComponent>();
|
||||
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID());
|
||||
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID(), skill.castOnType, NiQuaternion(0, 0, 0, 0));
|
||||
|
||||
auto* missionComponent = GetComponent<MissionComponent>();
|
||||
|
||||
@@ -1835,6 +1781,12 @@ void Entity::SetOwnerOverride(const LWOOBJID value) {
|
||||
m_OwnerOverride = value;
|
||||
}
|
||||
|
||||
void Entity::SetPlayerReadyForUpdates() {
|
||||
m_PlayerIsReadyForUpdates = true;
|
||||
|
||||
OnReadyForUpdates(this);
|
||||
}
|
||||
|
||||
bool Entity::GetIsGhostingCandidate() const {
|
||||
return m_IsGhostingCandidate;
|
||||
}
|
||||
@@ -1897,6 +1849,12 @@ const NiPoint3& Entity::GetPosition() const {
|
||||
return vehicel->GetPosition();
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
return rigidBodyPhantomPhysicsComponent->GetPosition();
|
||||
}
|
||||
|
||||
return NiPoint3Constant::ZERO;
|
||||
}
|
||||
|
||||
@@ -1925,6 +1883,12 @@ const NiQuaternion& Entity::GetRotation() const {
|
||||
return vehicel->GetRotation();
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
return rigidBodyPhantomPhysicsComponent->GetRotation();
|
||||
}
|
||||
|
||||
return NiQuaternionConstant::IDENTITY;
|
||||
}
|
||||
|
||||
@@ -1953,6 +1917,12 @@ void Entity::SetPosition(const NiPoint3& position) {
|
||||
vehicel->SetPosition(position);
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
rigidBodyPhantomPhysicsComponent->SetPosition(position);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(this);
|
||||
}
|
||||
|
||||
@@ -1981,6 +1951,12 @@ void Entity::SetRotation(const NiQuaternion& rotation) {
|
||||
vehicel->SetRotation(rotation);
|
||||
}
|
||||
|
||||
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
|
||||
if (rigidBodyPhantomPhysicsComponent != nullptr) {
|
||||
rigidBodyPhantomPhysicsComponent->SetRotation(rotation);
|
||||
}
|
||||
|
||||
Game::entityManager->SerializeEntity(this);
|
||||
}
|
||||
|
||||
@@ -2126,9 +2102,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
|
||||
havokVehiclePhysicsComponent->SetIsOnGround(update.onGround);
|
||||
havokVehiclePhysicsComponent->SetIsOnRail(update.onRail);
|
||||
havokVehiclePhysicsComponent->SetVelocity(update.velocity);
|
||||
havokVehiclePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO);
|
||||
havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity);
|
||||
havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO);
|
||||
havokVehiclePhysicsComponent->SetRemoteInputInfo(update.remoteInputInfo);
|
||||
} else {
|
||||
// Need to get the mount's controllable physics
|
||||
@@ -2139,9 +2113,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
|
||||
possessedControllablePhysicsComponent->SetIsOnGround(update.onGround);
|
||||
possessedControllablePhysicsComponent->SetIsOnRail(update.onRail);
|
||||
possessedControllablePhysicsComponent->SetVelocity(update.velocity);
|
||||
possessedControllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO);
|
||||
possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity);
|
||||
possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO);
|
||||
}
|
||||
Game::entityManager->SerializeEntity(possassableEntity);
|
||||
}
|
||||
@@ -2163,15 +2135,15 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
|
||||
controllablePhysicsComponent->SetIsOnGround(update.onGround);
|
||||
controllablePhysicsComponent->SetIsOnRail(update.onRail);
|
||||
controllablePhysicsComponent->SetVelocity(update.velocity);
|
||||
controllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO);
|
||||
controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity);
|
||||
controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO);
|
||||
|
||||
auto* ghostComponent = GetComponent<GhostComponent>();
|
||||
if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position);
|
||||
Game::entityManager->QueueGhostUpdate(GetObjectID());
|
||||
|
||||
if (updateChar) Game::entityManager->SerializeEntity(this);
|
||||
|
||||
OnPlayerPositionUpdate.Notify(this, update);
|
||||
}
|
||||
|
||||
const SystemAddress& Entity::GetSystemAddress() const {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "NiQuaternion.h"
|
||||
#include "LDFFormat.h"
|
||||
#include "eKillType.h"
|
||||
#include "Observable.h"
|
||||
|
||||
namespace Loot {
|
||||
class Info;
|
||||
@@ -116,7 +117,7 @@ public:
|
||||
|
||||
void SetOwnerOverride(LWOOBJID value);
|
||||
|
||||
void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; }
|
||||
void SetPlayerReadyForUpdates();
|
||||
|
||||
void SetObservers(int8_t value);
|
||||
|
||||
@@ -146,7 +147,8 @@ public:
|
||||
|
||||
void AddComponent(eReplicaComponentType componentId, Component* component);
|
||||
|
||||
std::vector<ScriptComponent*> GetScriptComponents();
|
||||
// This is expceted to never return nullptr, an assert checks this.
|
||||
CppScripts::Script* const GetScript();
|
||||
|
||||
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
|
||||
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
|
||||
@@ -171,9 +173,9 @@ public:
|
||||
|
||||
std::unordered_map<eReplicaComponentType, Component*>& GetComponents() { return m_Components; } // TODO: Remove
|
||||
|
||||
void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
|
||||
void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
|
||||
void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
|
||||
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
|
||||
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
|
||||
void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
|
||||
void Update(float deltaTime);
|
||||
|
||||
// Events
|
||||
@@ -295,6 +297,16 @@ public:
|
||||
|
||||
void ProcessPositionUpdate(PositionUpdate& update);
|
||||
|
||||
// Scale will only be communicated to the client when the construction packet is sent
|
||||
void SetScale(const float scale) { m_Scale = scale; };
|
||||
|
||||
/**
|
||||
* @brief The observable for player entity position updates.
|
||||
*/
|
||||
static Observable<Entity*, const PositionUpdate&> OnPlayerPositionUpdate;
|
||||
|
||||
static Observable<Entity*> OnReadyForUpdates;
|
||||
|
||||
protected:
|
||||
LWOOBJID m_ObjectID;
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#include "GhostComponent.h"
|
||||
#include <ranges>
|
||||
|
||||
Observable<Entity*> EntityManager::OnEntityCreated;
|
||||
Observable<Entity*> EntityManager::OnEntityDestroyed;
|
||||
|
||||
// Configure which zones have ghosting disabled, mostly small worlds.
|
||||
std::vector<LWOMAPID> EntityManager::m_GhostingExcludedZones = {
|
||||
// Small zones
|
||||
@@ -138,6 +141,9 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
|
||||
m_SpawnPoints.insert_or_assign(GeneralUtils::UTF16ToWTF8(spawnName), entity->GetObjectID());
|
||||
}
|
||||
|
||||
// Notify observers that a new entity has been created
|
||||
OnEntityCreated(entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -161,6 +167,9 @@ void EntityManager::DestroyEntity(Entity* entity) {
|
||||
DestructEntity(entity);
|
||||
}
|
||||
|
||||
// Notify observers that an entity is about to be destroyed
|
||||
OnEntityDestroyed(entity);
|
||||
|
||||
// Delete this entity at the end of the frame
|
||||
ScheduleForDeletion(id);
|
||||
}
|
||||
@@ -178,18 +187,18 @@ void EntityManager::SerializeEntities() {
|
||||
stream.Write<char>(ID_REPLICA_MANAGER_SERIALIZE);
|
||||
stream.Write<unsigned short>(entity->GetNetworkId());
|
||||
|
||||
entity->WriteBaseReplicaData(&stream, eReplicaPacketType::SERIALIZATION);
|
||||
entity->WriteComponents(&stream, eReplicaPacketType::SERIALIZATION);
|
||||
entity->WriteBaseReplicaData(stream, eReplicaPacketType::SERIALIZATION);
|
||||
entity->WriteComponents(stream, eReplicaPacketType::SERIALIZATION);
|
||||
|
||||
if (entity->GetIsGhostingCandidate()) {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent && ghostComponent->IsObserved(toSerialize)) {
|
||||
Game::server->Send(&stream, player->GetSystemAddress(), false);
|
||||
Game::server->Send(stream, player->GetSystemAddress(), false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Game::server->Send(&stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
Game::server->Send(stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
}
|
||||
}
|
||||
m_EntitiesToSerialize.clear();
|
||||
@@ -359,16 +368,16 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
stream.Write(true);
|
||||
stream.Write<uint16_t>(entity->GetNetworkId());
|
||||
|
||||
entity->WriteBaseReplicaData(&stream, eReplicaPacketType::CONSTRUCTION);
|
||||
entity->WriteComponents(&stream, eReplicaPacketType::CONSTRUCTION);
|
||||
entity->WriteBaseReplicaData(stream, eReplicaPacketType::CONSTRUCTION);
|
||||
entity->WriteComponents(stream, eReplicaPacketType::CONSTRUCTION);
|
||||
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||
if (skipChecks) {
|
||||
Game::server->Send(&stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
Game::server->Send(stream, UNASSIGNED_SYSTEM_ADDRESS, true);
|
||||
} else {
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
if (player->GetPlayerReadyForUpdates()) {
|
||||
Game::server->Send(&stream, player->GetSystemAddress(), false);
|
||||
Game::server->Send(stream, player->GetSystemAddress(), false);
|
||||
} else {
|
||||
auto* ghostComponent = player->GetComponent<GhostComponent>();
|
||||
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
|
||||
@@ -376,7 +385,7 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Game::server->Send(&stream, sysAddr, false);
|
||||
Game::server->Send(stream, sysAddr, false);
|
||||
}
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
@@ -407,7 +416,7 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
|
||||
stream.Write<uint8_t>(ID_REPLICA_MANAGER_DESTRUCTION);
|
||||
stream.Write<uint16_t>(entity->GetNetworkId());
|
||||
|
||||
Game::server->Send(&stream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
||||
Game::server->Send(stream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
for (auto* player : PlayerManager::GetAllPlayers()) {
|
||||
if (!player->GetPlayerReadyForUpdates()) {
|
||||
@@ -418,10 +427,16 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
|
||||
}
|
||||
|
||||
void EntityManager::SerializeEntity(Entity* entity) {
|
||||
if (!entity || entity->GetNetworkId() == 0) return;
|
||||
if (!entity) return;
|
||||
|
||||
EntityManager::SerializeEntity(*entity);
|
||||
}
|
||||
|
||||
if (std::find(m_EntitiesToSerialize.begin(), m_EntitiesToSerialize.end(), entity->GetObjectID()) == m_EntitiesToSerialize.end()) {
|
||||
m_EntitiesToSerialize.push_back(entity->GetObjectID());
|
||||
void EntityManager::SerializeEntity(const Entity& entity) {
|
||||
if (entity.GetNetworkId() == 0) return;
|
||||
|
||||
if (std::find(m_EntitiesToSerialize.cbegin(), m_EntitiesToSerialize.cend(), entity.GetObjectID()) == m_EntitiesToSerialize.cend()) {
|
||||
m_EntitiesToSerialize.push_back(entity.GetObjectID());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Observable.h"
|
||||
|
||||
class Entity;
|
||||
class EntityInfo;
|
||||
@@ -45,6 +46,7 @@ public:
|
||||
void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, bool skipChecks = false);
|
||||
void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
void SerializeEntity(Entity* entity);
|
||||
void SerializeEntity(const Entity& entity);
|
||||
|
||||
void ConstructAllEntities(const SystemAddress& sysAddr);
|
||||
void DestructAllEntities(const SystemAddress& sysAddr);
|
||||
@@ -71,6 +73,9 @@ public:
|
||||
const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; };
|
||||
const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; };
|
||||
|
||||
static Observable<Entity*> OnEntityCreated;
|
||||
static Observable<Entity*> OnEntityDestroyed;
|
||||
|
||||
private:
|
||||
void SerializeEntities();
|
||||
void KillEntities();
|
||||
|
||||
90
dGame/EntityTemplate.cpp
Normal file
90
dGame/EntityTemplate.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "EntityTemplate.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
nejlika::EntityTemplate::EntityTemplate(const nlohmann::json& json) {
|
||||
lot = json["lot"].get<LOT>();
|
||||
minLevel = json.contains("min-level") ? json["min-level"].get<int32_t>() : 1;
|
||||
|
||||
for (const auto& scaler : json["scaling"])
|
||||
{
|
||||
EntityTemplateScaler s;
|
||||
|
||||
s.type = magic_enum::enum_cast<ModifierType>(scaler["type"].get<std::string>()).value();
|
||||
s.isResistance = scaler.contains("resistance") && scaler["resistance"].get<bool>();
|
||||
s.polynomial = scaler["polynomial"].get<std::vector<float>>();
|
||||
|
||||
scalers.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::EntityTemplate::ToJson() const {
|
||||
nlohmann::json json;
|
||||
|
||||
json["lot"] = lot;
|
||||
json["min-level"] = minLevel;
|
||||
|
||||
nlohmann::json scalersJson;
|
||||
|
||||
for (const auto& scaler : scalers)
|
||||
{
|
||||
nlohmann::json s;
|
||||
|
||||
s["type"] = magic_enum::enum_name(scaler.type);
|
||||
s["resistance"] = scaler.isResistance;
|
||||
s["polynomial"] = scaler.polynomial;
|
||||
|
||||
scalersJson.push_back(s);
|
||||
}
|
||||
|
||||
json["scaling"] = scalersJson;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
float nejlika::EntityTemplate::GetScaler(ModifierType type, bool isResistance, int32_t level) const {
|
||||
for (const auto& scaler : scalers)
|
||||
{
|
||||
if (scaler.type == type && scaler.isResistance == isResistance)
|
||||
{
|
||||
return CalculateScaler(scaler, level);
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::vector<nejlika::ModifierInstance> nejlika::EntityTemplate::GenerateModifiers(int32_t level) const {
|
||||
std::vector<ModifierInstance> modifiers;
|
||||
|
||||
for (const auto& scaler : scalers)
|
||||
{
|
||||
ModifierInstance modifier(
|
||||
scaler.type,
|
||||
CalculateScaler(scaler, level),
|
||||
ModifierOperator::Additive,
|
||||
scaler.isResistance,
|
||||
ModifierCategory::Player,
|
||||
0,
|
||||
"",
|
||||
ModifierType::Invalid,
|
||||
""
|
||||
);
|
||||
|
||||
modifiers.push_back(modifier);
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
float nejlika::EntityTemplate::CalculateScaler(const EntityTemplateScaler& scaler, int32_t level) const {
|
||||
float result = 0.0f;
|
||||
|
||||
for (size_t i = 0; i < scaler.polynomial.size(); ++i)
|
||||
{
|
||||
result += scaler.polynomial[i] * std::pow(level, i);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
47
dGame/EntityTemplate.h
Normal file
47
dGame/EntityTemplate.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "Entity.h"
|
||||
#include "ModifierType.h"
|
||||
#include "ModifierInstance.h"
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class EntityTemplate
|
||||
{
|
||||
public:
|
||||
EntityTemplate() = default;
|
||||
|
||||
EntityTemplate(const nlohmann::json& json);
|
||||
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
LOT GetLOT() const { return lot; }
|
||||
|
||||
int32_t GetMinLevel() const { return minLevel; }
|
||||
|
||||
float GetScaler(ModifierType type, bool isResistance, int32_t level) const;
|
||||
|
||||
std::vector<ModifierInstance> GenerateModifiers(int32_t level) const;
|
||||
|
||||
private:
|
||||
struct EntityTemplateScaler
|
||||
{
|
||||
ModifierType type;
|
||||
bool isResistance;
|
||||
std::vector<float> polynomial;
|
||||
};
|
||||
|
||||
float CalculateScaler(const EntityTemplateScaler& scaler, int32_t level) const;
|
||||
|
||||
LOT lot;
|
||||
std::vector<EntityTemplateScaler> scalers;
|
||||
int32_t minLevel;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -43,9 +43,9 @@ inline void WriteLeaderboardRow(std::ostringstream& leaderboard, const uint32_t&
|
||||
leaderboard << "\nResult[0].Row[" << index << "]." << data->GetString();
|
||||
}
|
||||
|
||||
void Leaderboard::Serialize(RakNet::BitStream* bitStream) const {
|
||||
bitStream->Write(gameID);
|
||||
bitStream->Write(infoType);
|
||||
void Leaderboard::Serialize(RakNet::BitStream& bitStream) const {
|
||||
bitStream.Write(gameID);
|
||||
bitStream.Write(infoType);
|
||||
|
||||
std::ostringstream leaderboard;
|
||||
|
||||
@@ -64,12 +64,12 @@ void Leaderboard::Serialize(RakNet::BitStream* bitStream) const {
|
||||
|
||||
// Serialize the thing to a BitStream
|
||||
uint32_t leaderboardSize = leaderboard.tellp();
|
||||
bitStream->Write<uint32_t>(leaderboardSize);
|
||||
bitStream.Write<uint32_t>(leaderboardSize);
|
||||
// Doing this all in 1 call so there is no possbility of a dangling pointer.
|
||||
bitStream->WriteAlignedBytes(reinterpret_cast<const unsigned char*>(GeneralUtils::ASCIIToUTF16(leaderboard.str()).c_str()), leaderboardSize * sizeof(char16_t));
|
||||
if (leaderboardSize > 0) bitStream->Write<uint16_t>(0);
|
||||
bitStream->Write0();
|
||||
bitStream->Write0();
|
||||
bitStream.WriteAlignedBytes(reinterpret_cast<const unsigned char*>(GeneralUtils::ASCIIToUTF16(leaderboard.str()).c_str()), leaderboardSize * sizeof(char16_t));
|
||||
if (leaderboardSize > 0) bitStream.Write<uint16_t>(0);
|
||||
bitStream.Write0();
|
||||
bitStream.Write0();
|
||||
}
|
||||
|
||||
void Leaderboard::QueryToLdf(std::unique_ptr<sql::ResultSet>& rows) {
|
||||
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
*
|
||||
* Expensive! Leaderboards are very string intensive so be wary of performatnce calling this method.
|
||||
*/
|
||||
void Serialize(RakNet::BitStream* bitStream) const;
|
||||
void Serialize(RakNet::BitStream& bitStream) const;
|
||||
|
||||
/**
|
||||
* Builds the leaderboard from the database based on the associated gameID
|
||||
|
||||
181
dGame/Lookup.cpp
Normal file
181
dGame/Lookup.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "Lookup.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
Lookup::Lookup(const std::filesystem::path& lookup)
|
||||
{
|
||||
m_Lookup = lookup;
|
||||
|
||||
// Check if the file exists.
|
||||
if (!std::filesystem::exists(lookup))
|
||||
{
|
||||
// Empty lookup
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the json file
|
||||
std::ifstream file(lookup);
|
||||
|
||||
std::string json((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
// Parse the json
|
||||
auto doc = nlohmann::json::parse(json);
|
||||
|
||||
|
||||
// The document is a map, so we can iterate over it.
|
||||
for (const auto& [key, v] : doc.items()) {
|
||||
if (v.is_number_integer())
|
||||
{
|
||||
m_LookupMap[key] = v.get<int64_t>();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!v.is_object()) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Invalid value for key \"" << key << "\" in lookup.";
|
||||
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
// Get the data
|
||||
auto data = v.get<nlohmann::json>();
|
||||
|
||||
// Get the id
|
||||
auto id = data["id"].get<int64_t>();
|
||||
|
||||
m_LookupMap[key] = id;
|
||||
|
||||
// Get the metadata
|
||||
auto metadata = data["metadata"].get<std::string>();
|
||||
|
||||
m_Metadata[key] = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
nejlika::Lookup::Lookup(const Lookup &other)
|
||||
{
|
||||
m_Lookup = other.m_Lookup;
|
||||
m_LookupMap = other.m_LookupMap;
|
||||
m_Metadata = other.m_Metadata;
|
||||
m_CoreSymbols = other.m_CoreSymbols;
|
||||
}
|
||||
|
||||
id Lookup::GetValue(const name& symbol) const
|
||||
{
|
||||
id value;
|
||||
|
||||
if (IsCoreSymbol(symbol, value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const auto& it = m_LookupMap.find(symbol);
|
||||
|
||||
if (it == m_LookupMap.end())
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Symbol \"" << symbol << "\" does not exist in the lookup.";
|
||||
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::Exists(const name &symbol) const
|
||||
{
|
||||
return IsCoreSymbol(symbol) || (m_CoreSymbols.find(symbol) != m_CoreSymbols.end()) || (m_LookupMap.find(symbol) != m_LookupMap.end());
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::Exists(id value) const
|
||||
{
|
||||
for (const auto& [k, v] : m_LookupMap)
|
||||
{
|
||||
if (v == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& Lookup::GetMetadata(const name& symbol) const
|
||||
{
|
||||
const auto& it = m_Metadata.find(symbol);
|
||||
|
||||
if (it == m_Metadata.end())
|
||||
{
|
||||
static std::string empty;
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const std::filesystem::path& Lookup::GetLookup() const
|
||||
{
|
||||
return m_Lookup;
|
||||
}
|
||||
|
||||
const std::unordered_map<name, id> &nejlika::Lookup::GetMap() const
|
||||
{
|
||||
return m_LookupMap;
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::IsCoreSymbol(const name &symbol, id &value) const
|
||||
{
|
||||
try {
|
||||
value = std::stoi(symbol);
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
// cont...
|
||||
}
|
||||
|
||||
if (!symbol.starts_with(core_prefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check in the core symbols
|
||||
const auto& it = m_CoreSymbols.find(symbol);
|
||||
|
||||
if (it != m_CoreSymbols.end())
|
||||
{
|
||||
value = it->second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// In the format "core:<value>"
|
||||
try {
|
||||
value = std::stoi(symbol.substr(core_prefix.size() + 1));
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::IsCoreSymbol(const name &symbol)
|
||||
{
|
||||
// Check if it can be converted to an integer
|
||||
try {
|
||||
[[maybe_unused]] auto value = std::stoi(symbol);
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
// cont...
|
||||
}
|
||||
|
||||
return symbol.starts_with(core_prefix);
|
||||
}
|
||||
117
dGame/Lookup.h
Normal file
117
dGame/Lookup.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nejlika {
|
||||
|
||||
typedef std::string name;
|
||||
typedef int id;
|
||||
|
||||
/**
|
||||
* @brief A one-way mapping between symbols (names) and their corresponding numerical values.
|
||||
*
|
||||
* This is an active lookup, meaning that it is possible to add new symbols and their values to the lookup.
|
||||
*
|
||||
* A method is provided to wait for a symbol to be added to the lookup.
|
||||
*/
|
||||
class Lookup
|
||||
{
|
||||
public:
|
||||
inline static const name core_prefix = "lego-universe";
|
||||
|
||||
/**
|
||||
* @brief Constructs a Lookup object with the specified lookup file path.
|
||||
*
|
||||
* @param lookup The path to the lookup file.
|
||||
* @throw If the lookup file could not be parsed.
|
||||
*/
|
||||
Lookup(const std::filesystem::path& lookup);
|
||||
|
||||
Lookup(const Lookup& other);
|
||||
|
||||
Lookup() = default;
|
||||
|
||||
/**
|
||||
* @brief Gets the value of the specified symbol.
|
||||
*
|
||||
* @param symbol The symbol to get the value of.
|
||||
* @return The value of the specified symbol.
|
||||
* @throw If the specified symbol does not exist.
|
||||
*/
|
||||
id GetValue(const name& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the specified symbol exists.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @return Whether the specified symbol exists.
|
||||
*/
|
||||
bool Exists(const name& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether any symbol has the specified value.
|
||||
*
|
||||
* @param value The value to check.
|
||||
* @return Whether any symbol has the specified value.
|
||||
*/
|
||||
bool Exists(id value) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the metadata of a specified symbol.
|
||||
*
|
||||
* @param symbol The symbol to get metadata of.
|
||||
* @return The metadata of the specified symbol or an empty string if the symbol does not exist.
|
||||
*/
|
||||
const std::string& GetMetadata(const name& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the path to the lookup file.
|
||||
*
|
||||
* @return The path to the lookup file.
|
||||
*/
|
||||
const std::filesystem::path& GetLookup() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the map of all symbols and their values.
|
||||
*
|
||||
* @return The map of all symbols and their values.
|
||||
*/
|
||||
const std::unordered_map<name, id>& GetMap() const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the specified symbol is a core symbol.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @param value The value of the core symbol.
|
||||
* @return Whether the specified symbol is a core symbol.
|
||||
*/
|
||||
bool IsCoreSymbol(const name& symbol, id& value) const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the specified symbol is a core symbol.
|
||||
*
|
||||
* A symbol is considered a core symbol if it is either:
|
||||
* a number;
|
||||
* or a string starting with the core_prefix.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @return Whether the specified symbol is a core symbol.
|
||||
*/
|
||||
static bool IsCoreSymbol(const name& symbol);
|
||||
|
||||
private:
|
||||
|
||||
std::filesystem::path m_Lookup;
|
||||
|
||||
std::unordered_map<name, id> m_LookupMap;
|
||||
|
||||
std::unordered_map<name, std::string> m_Metadata;
|
||||
|
||||
std::unordered_map<name, id> m_CoreSymbols;
|
||||
};
|
||||
|
||||
} // namespace nejlika
|
||||
14
dGame/ModifierCategory.h
Normal file
14
dGame/ModifierCategory.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
enum class ModifierCategory : uint8_t
|
||||
{
|
||||
Player = 0 << 0,
|
||||
Pet = 1 << 0
|
||||
};
|
||||
|
||||
}
|
||||
170
dGame/ModifierInstance.cpp
Normal file
170
dGame/ModifierInstance.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "ModifierInstance.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
nejlika::ModifierInstance::ModifierInstance(const nlohmann::json& config) {
|
||||
type = magic_enum::enum_cast<ModifierType>(config["type"].get<std::string>()).value_or(ModifierType::Invalid);
|
||||
convertTo = magic_enum::enum_cast<ModifierType>(config["convert-to"].get<std::string>()).value_or(ModifierType::Invalid);
|
||||
value = config["value"].get<float>();
|
||||
|
||||
if (config.contains("op")) {
|
||||
op = magic_enum::enum_cast<ModifierOperator>(config["op"].get<std::string>()).value_or(ModifierOperator::Additive);
|
||||
}
|
||||
else {
|
||||
op = ModifierOperator::Additive;
|
||||
}
|
||||
|
||||
isResistance = config.contains("resistance") ? config["resistance"].get<bool>() : false;
|
||||
|
||||
if (config.contains("category")) {
|
||||
category = magic_enum::enum_cast<ModifierCategory>(config["category"].get<std::string>()).value_or(ModifierCategory::Player);
|
||||
}
|
||||
else {
|
||||
category = ModifierCategory::Player;
|
||||
}
|
||||
|
||||
effectID = config.contains("effect-id") ? config["effect-id"].get<uint32_t>() : 0;
|
||||
effectType = config.contains("effect-type") ? config["effect-type"].get<std::string>() : "";
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::ModifierInstance::ToJson() const
|
||||
{
|
||||
nlohmann::json config;
|
||||
|
||||
config["type"] = magic_enum::enum_name(type);
|
||||
config["convert-to"] = magic_enum::enum_name(convertTo);
|
||||
config["value"] = value;
|
||||
config["op"] = magic_enum::enum_name(op);
|
||||
config["resistance"] = isResistance;
|
||||
config["category"] = magic_enum::enum_name(category);
|
||||
config["effect-id"] = effectID;
|
||||
config["effect-type"] = effectType;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
std::string nejlika::ModifierInstance::GenerateHtmlString(const std::vector<ModifierInstance>& modifiers)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
// target -> resistance -> op -> type -> value
|
||||
std::unordered_map<ModifierCategory, std::unordered_map<bool, std::unordered_map<ModifierOperator, std::unordered_map<ModifierType, float>>>> modifierMap;
|
||||
|
||||
bool hasConvertTo = false;
|
||||
bool hasSkillModifier = false;
|
||||
|
||||
for (const auto& modifier : modifiers) {
|
||||
if (modifier.type == ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modifier.GetConvertTo() != ModifierType::Invalid)
|
||||
{
|
||||
hasConvertTo = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modifier.GetUpgradeName().empty())
|
||||
{
|
||||
hasSkillModifier = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
modifierMap[modifier.category][modifier.isResistance][modifier.op][modifier.type] = modifier.value;
|
||||
}
|
||||
|
||||
// Resistances and addatives are not separated, pet and player are
|
||||
// Summarize the resistances and addatives
|
||||
for (const auto& target : modifierMap) {
|
||||
if (target.first == ModifierCategory::Pet) {
|
||||
ss << "\n<font color=\"#D0AB62\">Pets:</font>\n";
|
||||
}
|
||||
|
||||
for (const auto& resistance : target.second) {
|
||||
for (const auto& math : resistance.second) {
|
||||
for (const auto& modifier : math.second) {
|
||||
ss << "<font color=\"#FFFFFF\">";
|
||||
|
||||
ss << ((modifier.second > 0) ? (math.first == ModifierOperator::Multiplicative ? "+" : "") : "-");
|
||||
|
||||
ss << std::fixed << std::setprecision(1) << std::abs(modifier.second);
|
||||
|
||||
if (math.first == ModifierOperator::Multiplicative) {
|
||||
ss << "%";
|
||||
}
|
||||
|
||||
ss << "</font> <font color=\"#D0AB62\">";
|
||||
|
||||
ss << " " << nejlika::GetModifierTypeName(modifier.first);
|
||||
|
||||
if (resistance.first) {
|
||||
// If the ss now ends with 'Damage' remove it
|
||||
if (ss.str().substr(ss.str().size() - 7) == " Damage") {
|
||||
ss.seekp(-7, std::ios_base::end);
|
||||
}
|
||||
|
||||
ss << " " << "Resistance";
|
||||
}
|
||||
|
||||
ss << "</font>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSkillModifier)
|
||||
{
|
||||
for (const auto& modifier : modifiers) {
|
||||
if (modifier.type != ModifierType::SkillModifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ss << "<font color=\"" << GetModifierTypeColor(modifier.type) << "\">";
|
||||
|
||||
ss << ((modifier.value > 0) ? "+" : "-");
|
||||
|
||||
ss << std::fixed << std::setprecision(0) << std::abs(modifier.value);
|
||||
|
||||
ss << " to ";
|
||||
|
||||
ss << modifier.GetUpgradeName();
|
||||
|
||||
ss << "</font>\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConvertTo)
|
||||
{
|
||||
for (const auto& modifier : modifiers) {
|
||||
if (modifier.GetConvertTo() == ModifierType::Invalid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modifier.type == ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ss << "<font color=\"#FFFFFF\">";
|
||||
|
||||
// +xx/yy% of T1 converted to T2
|
||||
ss << ((modifier.value > 0) ? "" : "-");
|
||||
|
||||
ss << std::fixed << std::setprecision(0) << std::abs(modifier.value);
|
||||
|
||||
ss << "%</font> <font color=\"#D0AB62\">";
|
||||
|
||||
ss << " of ";
|
||||
|
||||
ss << nejlika::GetModifierTypeName(modifier.type);
|
||||
|
||||
ss << " converted to ";
|
||||
|
||||
ss << nejlika::GetModifierTypeName(modifier.GetConvertTo());
|
||||
|
||||
ss << "</font>\n";
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
97
dGame/ModifierInstance.h
Normal file
97
dGame/ModifierInstance.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include "ModifierType.h"
|
||||
#include "ModifierCategory.h"
|
||||
#include "ModifierOperator.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class ModifierInstance
|
||||
{
|
||||
public:
|
||||
ModifierInstance(
|
||||
ModifierType type, float value, ModifierOperator op, bool isResistance, ModifierCategory category, uint32_t effectID, const std::string& effectType, ModifierType convertTo,
|
||||
const std::string& upgradeName
|
||||
) : type(type), value(value), op(op), isResistance(isResistance), category(category), effectID(effectID), effectType(effectType), convertTo(convertTo),
|
||||
upgradeName(upgradeName)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Construct a new Modifier Instance object from a json configuration.
|
||||
*
|
||||
* @param config The json configuration.
|
||||
*/
|
||||
ModifierInstance(const nlohmann::json& config);
|
||||
|
||||
/**
|
||||
* @brief Convert the modifier instance to a json representation.
|
||||
*
|
||||
* @return The json representation.
|
||||
*/
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
/**
|
||||
* @brief Generate a HTML string representation of a set of modifiers.
|
||||
*
|
||||
* @param modifiers The modifiers to generate the HTML string for.
|
||||
* @return The HTML string.
|
||||
*/
|
||||
static std::string GenerateHtmlString(const std::vector<ModifierInstance>& modifiers);
|
||||
|
||||
// Getters and setters
|
||||
|
||||
ModifierType GetType() const { return type; }
|
||||
|
||||
ModifierType GetConvertTo() const { return convertTo; }
|
||||
|
||||
float GetValue() const { return value; }
|
||||
|
||||
ModifierOperator GetOperator() const { return op; }
|
||||
|
||||
bool IsResistance() const { return isResistance; }
|
||||
|
||||
ModifierCategory GetCategory() const { return category; }
|
||||
|
||||
uint32_t GetEffectID() const { return effectID; }
|
||||
|
||||
std::string GetEffectType() const { return effectType; }
|
||||
|
||||
std::string GetUpgradeName() const { return upgradeName; }
|
||||
|
||||
void SetType(ModifierType type) { this->type = type; }
|
||||
|
||||
void SetConvertTo(ModifierType convertTo) { this->convertTo = convertTo; }
|
||||
|
||||
void SetValue(float value) { this->value = value; }
|
||||
|
||||
void SetOperator(ModifierOperator op) { this->op = op; }
|
||||
|
||||
void SetIsResistance(bool isResistance) { this->isResistance = isResistance; }
|
||||
|
||||
void SetCategory(ModifierCategory category) { this->category = category; }
|
||||
|
||||
void SetEffectID(uint32_t effectID) { this->effectID = effectID; }
|
||||
|
||||
void SetEffectType(const std::string& effectType) { this->effectType = effectType; }
|
||||
|
||||
void SetUpgradeName(const std::string& upgradeName) { this->upgradeName = upgradeName; }
|
||||
|
||||
private:
|
||||
ModifierType type;
|
||||
ModifierType convertTo;
|
||||
float value;
|
||||
ModifierOperator op;
|
||||
bool isResistance;
|
||||
ModifierCategory category;
|
||||
uint32_t effectID;
|
||||
std::string effectType;
|
||||
std::string upgradeName;
|
||||
};
|
||||
|
||||
}
|
||||
83
dGame/ModifierName.cpp
Normal file
83
dGame/ModifierName.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "ModifierName.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
nejlika::ModifierName::ModifierName(const nlohmann::json & json)
|
||||
{
|
||||
name = json["name"].get<std::string>();
|
||||
|
||||
if (json.contains("type"))
|
||||
{
|
||||
type = magic_enum::enum_cast<ModifierNameType>(json["type"].get<std::string>()).value_or(ModifierNameType::Prefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = ModifierNameType::Prefix;
|
||||
}
|
||||
|
||||
if (json.contains("rarity"))
|
||||
{
|
||||
rarity = magic_enum::enum_cast<ModifierRarity>(json["rarity"].get<std::string>()).value_or(ModifierRarity::Common);
|
||||
}
|
||||
else
|
||||
{
|
||||
rarity = ModifierRarity::Common;
|
||||
}
|
||||
}
|
||||
|
||||
nejlika::ModifierName::ModifierName(const ModifierNameTemplate& templateData) {
|
||||
name = templateData.GetName();
|
||||
type = templateData.GetType();
|
||||
rarity = templateData.GetRarity();
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::ModifierName::ToJson() const
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["name"] = name;
|
||||
json["type"] = magic_enum::enum_name(type);
|
||||
json["rarity"] = magic_enum::enum_name(rarity);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
std::string nejlika::ModifierName::GenerateHtmlString() const {
|
||||
const auto& rarityColor = ModifierRarityHelper::GetModifierRarityColor(rarity);
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "<font color=\"" << rarityColor << "\">" << name << "</font>";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string nejlika::ModifierName::GenerateHtmlString(const std::vector<ModifierName>& names)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
for (const auto& name : names) {
|
||||
if (name.GetType() == ModifierNameType::Prefix && !name.name.empty()) {
|
||||
ss << name.GenerateHtmlString() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
ss << "<font color=\"#D0AB62\">NAME</font>";
|
||||
|
||||
for (const auto& name : names) {
|
||||
if (name.GetType() == ModifierNameType::Suffix && !name.name.empty()) {
|
||||
ss << "\n" << name.GenerateHtmlString();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the last newline
|
||||
auto str = ss.str();
|
||||
|
||||
if (!str.empty() && str.back() == '\n') {
|
||||
str.pop_back();
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
57
dGame/ModifierName.h
Normal file
57
dGame/ModifierName.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "ModifierNameType.h"
|
||||
#include "ModifierRarity.h"
|
||||
#include "ModifierNameTemplate.h"
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class ModifierName
|
||||
{
|
||||
public:
|
||||
ModifierName(const std::string& name, ModifierNameType type, ModifierRarity rarity) :
|
||||
name(name), type(type), rarity(rarity) {}
|
||||
|
||||
ModifierName(const nlohmann::json& json);
|
||||
|
||||
ModifierName(const ModifierNameTemplate& templateData);
|
||||
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
std::string GenerateHtmlString() const;
|
||||
|
||||
/**
|
||||
* @brief Generate a HTML string representation of a set of names.
|
||||
*
|
||||
* @param modifiers The names to generate the HTML string for.
|
||||
* @return The HTML string.
|
||||
*/
|
||||
static std::string GenerateHtmlString(const std::vector<ModifierName>& names);
|
||||
|
||||
// Getters and setters
|
||||
|
||||
const std::string& GetName() const { return name; }
|
||||
|
||||
ModifierNameType GetType() const { return type; }
|
||||
|
||||
ModifierRarity GetRarity() const { return rarity; }
|
||||
|
||||
void SetName(const std::string& name) { this->name = name; }
|
||||
|
||||
void SetType(ModifierNameType type) { this->type = type; }
|
||||
|
||||
void SetRarity(ModifierRarity rarity) { this->rarity = rarity; }
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
ModifierNameType type;
|
||||
ModifierRarity rarity;
|
||||
};
|
||||
|
||||
}
|
||||
166
dGame/ModifierNameTemplate.cpp
Normal file
166
dGame/ModifierNameTemplate.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "ModifierNameTemplate.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
nejlika::ModifierNameTemplate::ModifierNameTemplate(const nlohmann::json & json)
|
||||
{
|
||||
name = json["name"].get<std::string>();
|
||||
|
||||
if (json.contains("lot"))
|
||||
{
|
||||
lot = json["lot"].get<int32_t>();
|
||||
}
|
||||
else
|
||||
{
|
||||
lot = 0;
|
||||
}
|
||||
|
||||
if (json.contains("type"))
|
||||
{
|
||||
type = magic_enum::enum_cast<ModifierNameType>(json["type"].get<std::string>()).value_or(ModifierNameType::Prefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = ModifierNameType::Prefix;
|
||||
}
|
||||
|
||||
if (json.contains("items"))
|
||||
{
|
||||
for (const auto& itemType : json["items"])
|
||||
{
|
||||
std::string type = itemType.get<std::string>();
|
||||
|
||||
// Make uppercase
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::toupper);
|
||||
|
||||
// Replace spaces with underscores
|
||||
std::replace(type.begin(), type.end(), ' ', '_');
|
||||
|
||||
const auto itemTypeEnum = magic_enum::enum_cast<eItemType>(type);
|
||||
|
||||
if (itemTypeEnum.has_value())
|
||||
{
|
||||
itemTypes.push_back(itemTypeEnum.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Invalid item type: " << type << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("modifiers"))
|
||||
{
|
||||
for (const auto& modifier : json["modifiers"])
|
||||
{
|
||||
const auto modifierTemplate = ModifierTemplate(modifier);
|
||||
|
||||
if (modifierTemplate.GetTypes().empty() || modifierTemplate.GetTypes().at(0) == ModifierType::Invalid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
modifiers.push_back(modifierTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("levels"))
|
||||
{
|
||||
auto levels = json["levels"];
|
||||
|
||||
if (levels.contains("min"))
|
||||
{
|
||||
minLevel = levels["min"].get<int32_t>();
|
||||
|
||||
const auto rasio = 1; //45.0f / 85.0f;
|
||||
|
||||
minLevel = std::max(1, static_cast<int32_t>(minLevel * rasio));
|
||||
}
|
||||
else
|
||||
{
|
||||
minLevel = 1;
|
||||
}
|
||||
|
||||
if (levels.contains("max"))
|
||||
{
|
||||
maxLevel = levels["max"].get<int32_t>();
|
||||
}
|
||||
else
|
||||
{
|
||||
maxLevel = 45;
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("rarity"))
|
||||
{
|
||||
rarity = magic_enum::enum_cast<ModifierRarity>(json["rarity"].get<std::string>()).value_or(ModifierRarity::Common);
|
||||
}
|
||||
else
|
||||
{
|
||||
rarity = ModifierRarity::Common;
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::ModifierNameTemplate::ToJson() const {
|
||||
nlohmann::json json;
|
||||
|
||||
json["name"] = name;
|
||||
json["type"] = magic_enum::enum_name(type);
|
||||
|
||||
if (lot != 0)
|
||||
{
|
||||
json["lot"] = lot;
|
||||
}
|
||||
|
||||
if (!itemTypes.empty())
|
||||
{
|
||||
nlohmann::json items;
|
||||
|
||||
for (const auto& itemType : itemTypes)
|
||||
{
|
||||
items.push_back(magic_enum::enum_name(itemType));
|
||||
}
|
||||
|
||||
json["items"] = items;
|
||||
}
|
||||
|
||||
if (!modifiers.empty())
|
||||
{
|
||||
nlohmann::json modifierTemplates;
|
||||
|
||||
for (const auto& modifier : modifiers)
|
||||
{
|
||||
modifierTemplates.push_back(modifier.ToJson());
|
||||
}
|
||||
|
||||
json["modifiers"] = modifierTemplates;
|
||||
}
|
||||
|
||||
nlohmann::json levels;
|
||||
|
||||
levels["min"] = minLevel;
|
||||
levels["max"] = maxLevel;
|
||||
|
||||
json["levels"] = levels;
|
||||
json["rarity"] = magic_enum::enum_name(rarity);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::ModifierNameTemplate::GenerateModifiers(int32_t level) const
|
||||
{
|
||||
std::vector<ModifierInstance> result;
|
||||
|
||||
for (const auto& modifierTemplate : modifiers)
|
||||
{
|
||||
auto modifiers = modifierTemplate.GenerateModifiers(level);
|
||||
|
||||
result.insert(result.end(), modifiers.begin(), modifiers.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
55
dGame/ModifierNameTemplate.h
Normal file
55
dGame/ModifierNameTemplate.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "eItemType.h"
|
||||
#include "ModifierNameType.h"
|
||||
#include "ModifierRarity.h"
|
||||
#include "ModifierTemplate.h"
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class ModifierNameTemplate
|
||||
{
|
||||
public:
|
||||
ModifierNameTemplate(const nlohmann::json& json);
|
||||
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
std::vector<ModifierInstance> GenerateModifiers(int32_t level) const;
|
||||
|
||||
// Getters
|
||||
|
||||
const std::string& GetName() const { return name; }
|
||||
|
||||
ModifierNameType GetType() const { return type; }
|
||||
|
||||
const std::vector<ModifierTemplate>& GetModifiers() const { return modifiers; }
|
||||
|
||||
const std::vector<eItemType>& GetItemTypes() const { return itemTypes; }
|
||||
|
||||
int32_t GetMinLevel() const { return minLevel; }
|
||||
|
||||
int32_t GetMaxLevel() const { return maxLevel; }
|
||||
|
||||
ModifierRarity GetRarity() const { return rarity; }
|
||||
|
||||
int32_t GetLOT() const { return lot; }
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
int32_t lot;
|
||||
ModifierNameType type;
|
||||
std::vector<ModifierTemplate> modifiers;
|
||||
std::vector<eItemType> itemTypes;
|
||||
int32_t minLevel;
|
||||
int32_t maxLevel;
|
||||
ModifierRarity rarity;
|
||||
};
|
||||
|
||||
}
|
||||
16
dGame/ModifierNameType.h
Normal file
16
dGame/ModifierNameType.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
enum class ModifierNameType : uint8_t
|
||||
{
|
||||
Prefix,
|
||||
Suffix,
|
||||
Object,
|
||||
Skill
|
||||
};
|
||||
|
||||
}
|
||||
14
dGame/ModifierOperator.h
Normal file
14
dGame/ModifierOperator.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
enum class ModifierOperator : uint8_t
|
||||
{
|
||||
Additive,
|
||||
Multiplicative
|
||||
};
|
||||
|
||||
}
|
||||
31
dGame/ModifierRarity.cpp
Normal file
31
dGame/ModifierRarity.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "ModifierRarity.h"
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const std::unordered_map<ModifierRarity, std::string> colorMap = {
|
||||
{ModifierRarity::Common, "#FFFFFF"},
|
||||
{ModifierRarity::Uncommon, "#B5AC15"},
|
||||
{ModifierRarity::Rare, "#3EEA4A"},
|
||||
{ModifierRarity::Epic, "#2F83C1"},
|
||||
{ModifierRarity::Legendary, "#852DCA"},
|
||||
{ModifierRarity::Relic, "#00FFFF"}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const std::string& nejlika::ModifierRarityHelper::GetModifierRarityColor(ModifierRarity rarity)
|
||||
{
|
||||
const auto color = colorMap.find(rarity);
|
||||
|
||||
if (color != colorMap.end()) {
|
||||
return color->second;
|
||||
}
|
||||
|
||||
static const std::string white = "#FFFFFF";
|
||||
|
||||
return white;
|
||||
}
|
||||
24
dGame/ModifierRarity.h
Normal file
24
dGame/ModifierRarity.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
enum class ModifierRarity : uint8_t
|
||||
{
|
||||
Common,
|
||||
Uncommon,
|
||||
Rare,
|
||||
Epic,
|
||||
Legendary,
|
||||
Relic
|
||||
};
|
||||
|
||||
namespace ModifierRarityHelper
|
||||
{
|
||||
const std::string& GetModifierRarityColor(ModifierRarity rarity);
|
||||
}
|
||||
|
||||
}
|
||||
40
dGame/ModifierScale.cpp
Normal file
40
dGame/ModifierScale.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "ModifierScale.h"
|
||||
|
||||
nejlika::ModifierScale::ModifierScale(const nlohmann::json & json)
|
||||
{
|
||||
level = json["level"].get<int32_t>();
|
||||
|
||||
const auto rasio = 1; //45.0f / 85.0f;
|
||||
|
||||
level = std::max(1, static_cast<int32_t>(level * rasio));
|
||||
|
||||
if (json.contains("min")) {
|
||||
min = json["min"].get<float>();
|
||||
}
|
||||
else {
|
||||
min = 0.0f;
|
||||
}
|
||||
|
||||
if (json.contains("max")) {
|
||||
max = json["max"].get<float>();
|
||||
}
|
||||
else {
|
||||
max = 0.0f;
|
||||
}
|
||||
|
||||
if (json.contains("value")) {
|
||||
min = json["value"].get<float>();
|
||||
max = json["value"].get<float>();
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::ModifierScale::ToJson() const
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["level"] = level;
|
||||
json["min"] = min;
|
||||
json["max"] = max;
|
||||
|
||||
return json;
|
||||
}
|
||||
33
dGame/ModifierScale.h
Normal file
33
dGame/ModifierScale.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class ModifierScale
|
||||
{
|
||||
public:
|
||||
ModifierScale() = default;
|
||||
|
||||
ModifierScale(int32_t level, float min, float max) : level(level), min(min), max(max) {}
|
||||
|
||||
ModifierScale(const nlohmann::json& json);
|
||||
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
int32_t GetLevel() const { return level; }
|
||||
|
||||
float GetMin() const { return min; }
|
||||
|
||||
float GetMax() const { return max; }
|
||||
|
||||
private:
|
||||
int32_t level = 0;
|
||||
float min = 0.0f;
|
||||
float max = 0.0f;
|
||||
};
|
||||
|
||||
}
|
||||
501
dGame/ModifierTemplate.cpp
Normal file
501
dGame/ModifierTemplate.cpp
Normal file
@@ -0,0 +1,501 @@
|
||||
#include "ModifierTemplate.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
nejlika::ModifierTemplate::ModifierTemplate(const nlohmann::json& config) {
|
||||
if (config.contains("type"))
|
||||
{
|
||||
selector = ModifierTemplateSelector::One;
|
||||
|
||||
const auto type = magic_enum::enum_cast<ModifierType>(config["type"].get<std::string>());
|
||||
|
||||
if (type.has_value())
|
||||
{
|
||||
types = {type.value()};
|
||||
}
|
||||
else
|
||||
{
|
||||
types = {};
|
||||
}
|
||||
}
|
||||
else if (config.contains("all"))
|
||||
{
|
||||
selector = ModifierTemplateSelector::All;
|
||||
|
||||
types = {};
|
||||
|
||||
for (const auto& type : config["all"])
|
||||
{
|
||||
const auto t = magic_enum::enum_cast<ModifierType>(type.get<std::string>());
|
||||
|
||||
if (t.has_value())
|
||||
{
|
||||
types.push_back(t.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (config.contains("two-of"))
|
||||
{
|
||||
selector = ModifierTemplateSelector::Two;
|
||||
|
||||
types = {};
|
||||
|
||||
for (const auto& type : config["two-of"])
|
||||
{
|
||||
const auto t = magic_enum::enum_cast<ModifierType>(type.get<std::string>());
|
||||
|
||||
if (t.has_value())
|
||||
{
|
||||
types.push_back(t.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
types = {};
|
||||
}
|
||||
|
||||
if (config.contains("convert-to"))
|
||||
{
|
||||
convertTo = magic_enum::enum_cast<ModifierType>(config["convert-to"].get<std::string>()).value_or(ModifierType::Invalid);
|
||||
}
|
||||
else
|
||||
{
|
||||
convertTo = ModifierType::Invalid;
|
||||
}
|
||||
|
||||
if (config.contains("scaling"))
|
||||
{
|
||||
const auto scaling = config["scaling"];
|
||||
|
||||
for (const auto& scaler : scaling)
|
||||
{
|
||||
scales.push_back(ModifierScale(scaler));
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("polynomial"))
|
||||
{
|
||||
const auto polynomialConfig = config["polynomial"];
|
||||
|
||||
for (const auto& term : polynomialConfig)
|
||||
{
|
||||
polynomial.push_back(term.get<float>());
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("category"))
|
||||
{
|
||||
category = magic_enum::enum_cast<ModifierCategory>(config["category"].get<std::string>()).value_or(ModifierCategory::Player);
|
||||
}
|
||||
else
|
||||
{
|
||||
category = ModifierCategory::Player;
|
||||
}
|
||||
|
||||
isResistance = config.contains("resistance") ? config["resistance"].get<bool>() : false;
|
||||
|
||||
effectID = config.contains("effect-id") ? config["effect-id"].get<uint32_t>() : 0;
|
||||
|
||||
effectType = config.contains("effect-type") ? config["effect-type"].get<std::string>() : "";
|
||||
|
||||
if (config.contains("operator"))
|
||||
{
|
||||
operatorType = magic_enum::enum_cast<ModifierOperator>(config["operator"].get<std::string>()).value_or(ModifierOperator::Additive);
|
||||
}
|
||||
else
|
||||
{
|
||||
operatorType = ModifierOperator::Additive;
|
||||
}
|
||||
|
||||
// Old format
|
||||
if (config.contains("percentage"))
|
||||
{
|
||||
if (config["percentage"].get<bool>()) {
|
||||
operatorType = ModifierOperator::Multiplicative;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("pet"))
|
||||
{
|
||||
if (config["pet"].get<bool>()) {
|
||||
category = ModifierCategory::Pet;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("owner"))
|
||||
{
|
||||
if (config["owner"].get<bool>()) {
|
||||
category = ModifierCategory::Player;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("skill"))
|
||||
{
|
||||
upgradeName = config["skill"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::ModifierTemplate::ToJson() const {
|
||||
nlohmann::json config;
|
||||
|
||||
if (selector == ModifierTemplateSelector::One)
|
||||
{
|
||||
config["type"] = magic_enum::enum_name(types[0]);
|
||||
}
|
||||
else if (selector == ModifierTemplateSelector::All)
|
||||
{
|
||||
config["all"] = true;
|
||||
|
||||
for (const auto& type : types)
|
||||
{
|
||||
config["types"].push_back(magic_enum::enum_name(type));
|
||||
}
|
||||
}
|
||||
else if (selector == ModifierTemplateSelector::Two)
|
||||
{
|
||||
config["two-of"] = true;
|
||||
|
||||
for (const auto& type : types)
|
||||
{
|
||||
config["two-of"].push_back(magic_enum::enum_name(type));
|
||||
}
|
||||
}
|
||||
|
||||
if (!scales.empty())
|
||||
{
|
||||
nlohmann::json scaling;
|
||||
|
||||
for (const auto& scale : scales)
|
||||
{
|
||||
scaling.push_back(scale.ToJson());
|
||||
}
|
||||
|
||||
config["scaling"] = scaling;
|
||||
}
|
||||
|
||||
if (!polynomial.empty())
|
||||
{
|
||||
nlohmann::json polynomialConfig;
|
||||
|
||||
for (const auto& term : polynomial)
|
||||
{
|
||||
polynomialConfig.push_back(term);
|
||||
}
|
||||
|
||||
config["polynomial"] = polynomialConfig;
|
||||
}
|
||||
|
||||
config["convert-to"] = magic_enum::enum_name(convertTo);
|
||||
|
||||
config["category"] = magic_enum::enum_name(category);
|
||||
config["resistance"] = isResistance;
|
||||
config["effect-id"] = effectID;
|
||||
config["effect-type"] = effectType;
|
||||
config["operator"] = magic_enum::enum_name(operatorType);
|
||||
|
||||
if (!upgradeName.empty())
|
||||
{
|
||||
config["skill"] = upgradeName;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::ModifierTemplate::GenerateModifiers(int32_t level) const {
|
||||
std::vector<ModifierInstance> modifiers;
|
||||
|
||||
std::vector<ModifierType> selectedTypes;
|
||||
|
||||
if (types.empty())
|
||||
{
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
if (selector == ModifierTemplateSelector::One)
|
||||
{
|
||||
selectedTypes = {types[0]};
|
||||
}
|
||||
else if (selector == ModifierTemplateSelector::All)
|
||||
{
|
||||
selectedTypes = types;
|
||||
}
|
||||
else if (selector == ModifierTemplateSelector::Two)
|
||||
{
|
||||
if (types.size() < 2)
|
||||
{
|
||||
selectedTypes = types;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Randomly select two types
|
||||
selectedTypes = types;
|
||||
|
||||
std::shuffle(selectedTypes.begin(), selectedTypes.end(), std::mt19937(std::random_device()()));
|
||||
|
||||
selectedTypes.resize(2);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& selectedType : selectedTypes)
|
||||
{
|
||||
auto modifierOpt = GenerateModifier(selectedType, level);
|
||||
|
||||
if (modifierOpt.has_value())
|
||||
{
|
||||
modifiers.push_back(modifierOpt.value());
|
||||
}
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
std::string nejlika::ModifierTemplate::GenerateHtmlString(const std::vector<ModifierTemplate>& modifiers, int32_t level) {
|
||||
std::stringstream ss;
|
||||
|
||||
// target -> resistance -> op -> type -> (min, max)
|
||||
std::unordered_map<ModifierCategory, std::unordered_map<bool, std::unordered_map<ModifierOperator, std::unordered_map<ModifierType, std::pair<float, float>>>>> modifierMap;
|
||||
|
||||
bool hasConvertTo = false;
|
||||
bool hasSkillModifier = false;
|
||||
|
||||
for (const auto& modifier : modifiers) {
|
||||
if (modifier.GetConvertTo() != ModifierType::Invalid)
|
||||
{
|
||||
hasConvertTo = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modifier.GetUpgradeName().empty())
|
||||
{
|
||||
hasSkillModifier = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& type : modifier.types) {
|
||||
if (type == ModifierType::Invalid || type == ModifierType::SkillModifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modifier.polynomial.empty())
|
||||
{
|
||||
float value = 0.0f;
|
||||
|
||||
int32_t power = 0;
|
||||
for (const auto& term : modifier.polynomial)
|
||||
{
|
||||
value += term * std::pow(level, power);
|
||||
|
||||
power++;
|
||||
}
|
||||
|
||||
modifierMap[modifier.category][modifier.isResistance][modifier.operatorType][type] = {value, value};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ModifierScale scale;
|
||||
bool found = false;
|
||||
|
||||
// Select the scale with the highest level that is less than or equal to the current level
|
||||
for (const auto& s : modifier.scales) {
|
||||
if (s.GetLevel() <= level && s.GetLevel() > scale.GetLevel()) {
|
||||
scale = s;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
modifierMap[modifier.category][modifier.isResistance][modifier.operatorType][type] = {scale.GetMin(), scale.GetMax()};
|
||||
}
|
||||
}
|
||||
|
||||
// Resistances and addatives are not separated, pet and player are
|
||||
// Summarize the resistances and addatives
|
||||
for (const auto& target : modifierMap) {
|
||||
if (target.first == ModifierCategory::Pet) {
|
||||
ss << "\n<font color=\"#D0AB62\">Pets:</font>\n";
|
||||
}
|
||||
|
||||
for (const auto& resistance : target.second) {
|
||||
for (const auto& math : resistance.second) {
|
||||
for (const auto& modifier : math.second) {
|
||||
ss << "<font color=\"#FFFFFF\">";
|
||||
|
||||
ss << ((modifier.second.first > 0) ? (math.first == ModifierOperator::Multiplicative ? "+" : "") : "-");
|
||||
|
||||
ss << std::fixed << std::setprecision(1) << std::abs(modifier.second.first);
|
||||
|
||||
if (modifier.second.first != modifier.second.second)
|
||||
{
|
||||
ss << "/";
|
||||
|
||||
ss << std::fixed << std::setprecision(1) << std::abs(modifier.second.second);
|
||||
}
|
||||
|
||||
if (math.first == ModifierOperator::Multiplicative) {
|
||||
ss << "%";
|
||||
}
|
||||
|
||||
ss << "</font> <font color=\"#D0AB62\">";
|
||||
|
||||
ss << " " << nejlika::GetModifierTypeName(modifier.first);
|
||||
|
||||
if (resistance.first) {
|
||||
// If the ss now ends with 'Damage' remove it
|
||||
if (ss.str().substr(ss.str().size() - 6) == "Damage") {
|
||||
ss.seekp(-6, std::ios_base::end);
|
||||
}
|
||||
|
||||
ss << " " << "Resistance";
|
||||
}
|
||||
|
||||
ss << "</font>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSkillModifier)
|
||||
{
|
||||
for (const auto& modifier : modifiers) {
|
||||
for (const auto& type : modifier.types) {
|
||||
if (type != ModifierType::SkillModifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& scalors = modifier.GetScales();
|
||||
|
||||
if (scalors.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& m = scalors[0];
|
||||
|
||||
ss << "<font color=\"" << GetModifierTypeColor(type) << "\">";
|
||||
|
||||
ss << ((m.GetMin() > 0) ? "+" : "-");
|
||||
|
||||
ss << std::fixed << std::setprecision(0) << std::abs(m.GetMin());
|
||||
|
||||
ss << " to ";
|
||||
|
||||
ss << modifier.GetUpgradeName();
|
||||
|
||||
ss << "</font>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasConvertTo)
|
||||
{
|
||||
for (const auto& modifier : modifiers) {
|
||||
if (modifier.GetConvertTo() == ModifierType::Invalid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& type : modifier.types) {
|
||||
if (type == ModifierType::Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& scalors = modifier.GetScales();
|
||||
|
||||
auto m = scalors[0];
|
||||
|
||||
for (const auto& s : scalors) {
|
||||
if (s.GetLevel() <= level && s.GetLevel() > m.GetLevel()) {
|
||||
m = s;
|
||||
}
|
||||
}
|
||||
|
||||
ss << "<font color=\"#FFFFFF\">";
|
||||
|
||||
// +xx/yy% of T1 converted to T2
|
||||
ss << ((m.GetMin() > 0) ? "" : "-");
|
||||
|
||||
ss << std::fixed << std::setprecision(0) << std::abs(m.GetMin());
|
||||
|
||||
if (m.GetMin() != m.GetMax())
|
||||
{
|
||||
ss << "/";
|
||||
|
||||
ss << std::fixed << std::setprecision(0) << std::abs(m.GetMax());
|
||||
}
|
||||
|
||||
ss << "%</font> <font color=\"#D0AB62\">";
|
||||
|
||||
ss << " of ";
|
||||
|
||||
ss << nejlika::GetModifierTypeName(type);
|
||||
|
||||
ss << " converted to ";
|
||||
|
||||
ss << nejlika::GetModifierTypeName(modifier.GetConvertTo());
|
||||
|
||||
ss << "</font>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::optional<ModifierInstance> nejlika::ModifierTemplate::GenerateModifier(ModifierType type, int32_t level) const {
|
||||
ModifierScale scale;
|
||||
bool found = false;
|
||||
|
||||
if (!polynomial.empty())
|
||||
{
|
||||
float value = 0.0f;
|
||||
|
||||
int32_t power = 0;
|
||||
for (const auto& term : polynomial)
|
||||
{
|
||||
value += term * std::pow(level, power);
|
||||
|
||||
power++;
|
||||
}
|
||||
|
||||
return ModifierInstance(type, value, operatorType, isResistance, category, effectID, effectType, convertTo, upgradeName);
|
||||
}
|
||||
|
||||
// Select the scale with the highest level that is less than or equal to the current level
|
||||
for (const auto& s : scales) {
|
||||
if ((s.GetLevel() <= level) && (s.GetLevel() > scale.GetLevel())) {
|
||||
std::cout << "Found scale: " << s.GetMin() << " - " << s.GetMax() << " for level " << s.GetLevel() << std::endl;
|
||||
scale = s;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
float value = 0;
|
||||
|
||||
if (scale.GetMax() == scale.GetMin())
|
||||
{
|
||||
value = scale.GetMin();
|
||||
}
|
||||
else
|
||||
{
|
||||
value = (GeneralUtils::GenerateRandomNumber<uint32_t>(0, 100) / 100.0f) * (scale.GetMax() - scale.GetMin()) + scale.GetMin();
|
||||
}
|
||||
|
||||
std::cout << "Generated modifier: " << value << " with level " << level << " for type: " << magic_enum::enum_name(type) << std::endl;
|
||||
|
||||
return ModifierInstance(type, value, operatorType, isResistance, category, effectID, effectType, convertTo, upgradeName);
|
||||
}
|
||||
109
dGame/ModifierTemplate.h
Normal file
109
dGame/ModifierTemplate.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
#include "ModifierInstance.h"
|
||||
#include "ModifierScale.h"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
enum ModifierTemplateSelector : uint8_t
|
||||
{
|
||||
One,
|
||||
All,
|
||||
Two
|
||||
};
|
||||
|
||||
class ModifierTemplate
|
||||
{
|
||||
public:
|
||||
ModifierTemplate(
|
||||
const std::vector<ModifierType>& types, ModifierTemplateSelector selector, ModifierCategory category, bool isResistance, uint32_t effectID, const std::string& effectType
|
||||
) : types(types), selector(selector), category(category), isResistance(isResistance), effectID(effectID), effectType(effectType) {}
|
||||
|
||||
/**
|
||||
* @brief Construct a new Modifier Template object from a json configuration.
|
||||
*
|
||||
* @param config The json configuration.
|
||||
*/
|
||||
ModifierTemplate(const nlohmann::json& config);
|
||||
|
||||
/**
|
||||
* @brief Convert the modifier template to a json representation.
|
||||
*
|
||||
* @return The json representation.
|
||||
*/
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
std::vector<ModifierInstance> GenerateModifiers(int32_t level) const;
|
||||
|
||||
// Getters and setters
|
||||
|
||||
const std::vector<ModifierType>& GetTypes() const { return types; }
|
||||
|
||||
ModifierType GetConvertTo() const { return convertTo; }
|
||||
|
||||
ModifierTemplateSelector GetSelector() const { return selector; }
|
||||
|
||||
const std::vector<ModifierScale>& GetScales() const { return scales; }
|
||||
|
||||
ModifierCategory GetCategory() const { return category; }
|
||||
|
||||
bool IsResistance() const { return isResistance; }
|
||||
|
||||
uint32_t GetEffectID() const { return effectID; }
|
||||
|
||||
std::string GetEffectType() const { return effectType; }
|
||||
|
||||
const std::string& GetUpgradeName() const { return upgradeName; }
|
||||
|
||||
void SetTypes(const std::vector<ModifierType>& types) { this->types = types; }
|
||||
|
||||
void SetConvertTo(ModifierType convertTo) { this->convertTo = convertTo; }
|
||||
|
||||
void SetSelector(ModifierTemplateSelector selector) { this->selector = selector; }
|
||||
|
||||
void SetScales(const std::vector<ModifierScale>& scales) { this->scales = scales; }
|
||||
|
||||
void SetCategory(ModifierCategory category) { this->category = category; }
|
||||
|
||||
void SetIsResistance(bool isResistance) { this->isResistance = isResistance; }
|
||||
|
||||
void SetEffectID(uint32_t effectID) { this->effectID = effectID; }
|
||||
|
||||
void SetEffectType(const std::string& effectType) { this->effectType = effectType; }
|
||||
|
||||
void SetUpgradeName(const std::string& upgradeName) { this->upgradeName = upgradeName; }
|
||||
|
||||
/**
|
||||
* @brief Generate a HTML string representation of a set of modifier templates.
|
||||
*
|
||||
* @param modifiers The modifier templates to generate the HTML string for.
|
||||
* @param level The level of the modifier templates.
|
||||
* @return The HTML string.
|
||||
*/
|
||||
static std::string GenerateHtmlString(const std::vector<ModifierTemplate>& modifiers, int32_t level);
|
||||
|
||||
|
||||
private:
|
||||
std::optional<ModifierInstance> GenerateModifier(ModifierType type, int32_t level) const;
|
||||
|
||||
std::vector<ModifierType> types;
|
||||
ModifierType convertTo;
|
||||
ModifierTemplateSelector selector;
|
||||
std::vector<ModifierScale> scales;
|
||||
std::vector<float> polynomial;
|
||||
ModifierCategory category;
|
||||
ModifierOperator operatorType;
|
||||
bool isResistance;
|
||||
uint32_t effectID;
|
||||
std::string effectType;
|
||||
std::string upgradeName;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
216
dGame/ModifierType.cpp
Normal file
216
dGame/ModifierType.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "ModifierType.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const std::unordered_map<ModifierType, std::string> colorMap = {
|
||||
{ModifierType::Health, "#750000"},
|
||||
{ModifierType::Armor, "#525252"},
|
||||
{ModifierType::Imagination, "#0077FF"},
|
||||
{ModifierType::Offensive, "#71583B"},
|
||||
{ModifierType::Defensive, "#71583B"},
|
||||
{ModifierType::Physical, "#666666"},
|
||||
{ModifierType::Pierce, "#4f4f4f"},
|
||||
{ModifierType::Vitality, "#e84646"},
|
||||
{ModifierType::Fire, "#ff0000"},
|
||||
{ModifierType::Cold, "#94d0f2"},
|
||||
{ModifierType::Lightning, "#00a2ff"},
|
||||
{ModifierType::Corruption, "#3d00ad"},
|
||||
{ModifierType::Psychic, "#4b0161"},
|
||||
{ModifierType::Acid, "#00ff00"},
|
||||
};
|
||||
|
||||
const std::unordered_map<ModifierType, std::string> nameMap = {
|
||||
{ModifierType::Health, "Health"},
|
||||
{ModifierType::Armor, "Armor"},
|
||||
{ModifierType::Imagination, "Imagination"},
|
||||
{ModifierType::Offensive, "Offensive Ability"},
|
||||
{ModifierType::Defensive, "Defensive Ability"},
|
||||
{ModifierType::Physical, "Physical Damage"},
|
||||
{ModifierType::Pierce, "Pierce Damage"},
|
||||
{ModifierType::Vitality, "Vitality Damage"},
|
||||
{ModifierType::Fire, "Fire Damage"},
|
||||
{ModifierType::Cold, "Cold Damage"},
|
||||
{ModifierType::Lightning, "Lightning Damage"},
|
||||
{ModifierType::Corruption, "Corruption Damage"},
|
||||
{ModifierType::Psychic, "Psychic Damage"},
|
||||
{ModifierType::Acid, "Acid Damage"},
|
||||
{ModifierType::InternalDisassembly, "Internal Disassembly"},
|
||||
{ModifierType::InternalDisassemblyDuration, "Internal Disassembly Duration"},
|
||||
{ModifierType::Burn, "Burn"},
|
||||
{ModifierType::BurnDuration, "Burn Duration"},
|
||||
{ModifierType::Frostburn, "Frostburn"},
|
||||
{ModifierType::FrostburnDuration, "Frostburn Duration"},
|
||||
{ModifierType::Poison, "Poison"},
|
||||
{ModifierType::PoisonDuration, "Poison Duration"},
|
||||
{ModifierType::Electrocute, "Electrocute"},
|
||||
{ModifierType::ElectrocuteDuration, "Electrocute Duration"},
|
||||
{ModifierType::VitalityDecay, "Vitality Decay"},
|
||||
{ModifierType::VitalityDecayDuration, "Vitality Decay Duration"},
|
||||
{ModifierType::Seperation, "Seperation"},
|
||||
{ModifierType::SeperationDuration, "Seperation Duration"},
|
||||
{ModifierType::Elemental, "Elemental Damage"},
|
||||
{ModifierType::Damage, "Damage"},
|
||||
{ModifierType::Speed, "Speed"},
|
||||
{ModifierType::AttackSpeed, "Attack Speed"},
|
||||
{ModifierType::BlockRecovery, "Block Recovery Speed"},
|
||||
{ModifierType::BlockChance, "Chance to Block"},
|
||||
{ModifierType::Block, "Damage to Block"},
|
||||
{ModifierType::CriticalDamage, "Critical-hit Damage"},
|
||||
{ModifierType::HealthDrain, "Damage converted to Health"},
|
||||
{ModifierType::ArmorPiercing, "Armor Piercing"},
|
||||
{ModifierType::ReducedStunDuration, "Reduced Stun Duration"},
|
||||
{ModifierType::SkillCooldownReduction, "Skill Cooldown Reduction"},
|
||||
{ModifierType::SkillRecharge, "Skill Recharge Time"},
|
||||
{ModifierType::Slow, "Slow"},
|
||||
{ModifierType::Physique, "Physique"},
|
||||
{ModifierType::Cunning, "Cunning"},
|
||||
{ModifierType::Spirit, "Imagination"},
|
||||
{ModifierType::AttacksPerSecond, "Attacks per Second"},
|
||||
{ModifierType::ImaginationCost, "Imagination Cost"},
|
||||
{ModifierType::MainWeaponDamage, "Main Weapon Damage"},
|
||||
{ModifierType::Stun, "Target Stun Duration"}
|
||||
};
|
||||
|
||||
const std::unordered_map<ModifierType, ModifierType> resistanceMap = {
|
||||
{ModifierType::Physical, ModifierType::Physical},
|
||||
{ModifierType::Pierce, ModifierType::Pierce},
|
||||
{ModifierType::Vitality, ModifierType::Vitality},
|
||||
{ModifierType::Fire, ModifierType::Fire},
|
||||
{ModifierType::Cold, ModifierType::Cold},
|
||||
{ModifierType::Lightning, ModifierType::Lightning},
|
||||
{ModifierType::Corruption, ModifierType::Corruption},
|
||||
{ModifierType::Psychic, ModifierType::Psychic},
|
||||
{ModifierType::Acid, ModifierType::Acid},
|
||||
{ModifierType::InternalDisassembly, ModifierType::Physical},
|
||||
{ModifierType::Burn, ModifierType::Fire},
|
||||
{ModifierType::Frostburn, ModifierType::Cold},
|
||||
{ModifierType::Poison, ModifierType::Acid},
|
||||
{ModifierType::VitalityDecay, ModifierType::Vitality},
|
||||
{ModifierType::Electrocute, ModifierType::Lightning},
|
||||
{ModifierType::Seperation, ModifierType::Seperation}
|
||||
};
|
||||
|
||||
const std::unordered_set<ModifierType> normalDamageTypes = {
|
||||
ModifierType::Physical,
|
||||
ModifierType::Pierce,
|
||||
ModifierType::Vitality,
|
||||
ModifierType::Fire,
|
||||
ModifierType::Cold,
|
||||
ModifierType::Lightning,
|
||||
ModifierType::Corruption,
|
||||
ModifierType::Psychic,
|
||||
ModifierType::Acid
|
||||
};
|
||||
|
||||
const std::unordered_map<ModifierType, ModifierType> durationMap = {
|
||||
{ModifierType::InternalDisassembly, ModifierType::InternalDisassemblyDuration},
|
||||
{ModifierType::Burn, ModifierType::BurnDuration},
|
||||
{ModifierType::Frostburn, ModifierType::FrostburnDuration},
|
||||
{ModifierType::Poison, ModifierType::PoisonDuration},
|
||||
{ModifierType::VitalityDecay, ModifierType::VitalityDecayDuration},
|
||||
{ModifierType::Electrocute, ModifierType::ElectrocuteDuration},
|
||||
{ModifierType::Seperation, ModifierType::SeperationDuration}
|
||||
};
|
||||
|
||||
const std::unordered_map<ModifierType, ModifierType> overTimeMap = {
|
||||
{ModifierType::Physical, ModifierType::InternalDisassembly},
|
||||
{ModifierType::Fire, ModifierType::Burn},
|
||||
{ModifierType::Cold, ModifierType::Frostburn},
|
||||
{ModifierType::Poison, ModifierType::Poison},
|
||||
{ModifierType::Vitality, ModifierType::VitalityDecay},
|
||||
{ModifierType::Lightning, ModifierType::Electrocute}
|
||||
};
|
||||
|
||||
const std::unordered_set<ModifierType> isOverTimeMap = {
|
||||
ModifierType::InternalDisassembly,
|
||||
ModifierType::Burn,
|
||||
ModifierType::Frostburn,
|
||||
ModifierType::Poison,
|
||||
ModifierType::VitalityDecay,
|
||||
ModifierType::Electrocute,
|
||||
ModifierType::Seperation
|
||||
};
|
||||
|
||||
const std::unordered_set<ModifierType> isDurationType = {
|
||||
ModifierType::InternalDisassemblyDuration,
|
||||
ModifierType::BurnDuration,
|
||||
ModifierType::FrostburnDuration,
|
||||
ModifierType::PoisonDuration,
|
||||
ModifierType::VitalityDecayDuration,
|
||||
ModifierType::ElectrocuteDuration,
|
||||
ModifierType::SeperationDuration
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const std::string& nejlika::GetModifierTypeColor(ModifierType type)
|
||||
{
|
||||
const auto color = colorMap.find(type);
|
||||
|
||||
if (color != colorMap.end()) {
|
||||
return color->second;
|
||||
}
|
||||
|
||||
static const std::string white = "#FFFFFF";
|
||||
|
||||
return white;
|
||||
}
|
||||
|
||||
const std::string& nejlika::GetModifierTypeName(ModifierType type) {
|
||||
const auto name = nameMap.find(type);
|
||||
|
||||
if (name != nameMap.end()) {
|
||||
return name->second;
|
||||
}
|
||||
|
||||
static const std::string invalid = "Invalid";
|
||||
|
||||
return invalid;
|
||||
}
|
||||
|
||||
const ModifierType nejlika::GetResistanceType(ModifierType type) {
|
||||
const auto resistance = resistanceMap.find(type);
|
||||
|
||||
if (resistance != resistanceMap.end()) {
|
||||
return resistance->second;
|
||||
}
|
||||
|
||||
return ModifierType::Invalid;
|
||||
}
|
||||
|
||||
const bool nejlika::IsNormalDamageType(ModifierType type) {
|
||||
return normalDamageTypes.find(type) != normalDamageTypes.end();
|
||||
}
|
||||
|
||||
const ModifierType nejlika::GetOverTimeType(ModifierType type) {
|
||||
const auto overTime = overTimeMap.find(type);
|
||||
|
||||
if (overTime != overTimeMap.end()) {
|
||||
return overTime->second;
|
||||
}
|
||||
|
||||
return ModifierType::Invalid;
|
||||
}
|
||||
|
||||
const ModifierType nejlika::GetDurationType(ModifierType type) {
|
||||
const auto duration = durationMap.find(type);
|
||||
|
||||
if (duration != durationMap.end()) {
|
||||
return duration->second;
|
||||
}
|
||||
|
||||
return ModifierType::Invalid;
|
||||
}
|
||||
|
||||
const bool nejlika::IsOverTimeType(ModifierType type) {
|
||||
return isOverTimeMap.find(type) != isOverTimeMap.end();
|
||||
}
|
||||
|
||||
const bool nejlika::IsDurationType(ModifierType type) {
|
||||
return isDurationType.find(type) != isDurationType.end();
|
||||
}
|
||||
96
dGame/ModifierType.h
Normal file
96
dGame/ModifierType.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
enum class ModifierType : uint8_t
|
||||
{
|
||||
Health,
|
||||
Armor,
|
||||
Imagination,
|
||||
|
||||
Physique,
|
||||
Cunning,
|
||||
Spirit,
|
||||
|
||||
Offensive,
|
||||
Defensive,
|
||||
|
||||
// Normal Types
|
||||
Physical,
|
||||
Fire,
|
||||
Cold,
|
||||
Lightning,
|
||||
Acid,
|
||||
Vitality,
|
||||
Pierce,
|
||||
Corruption, // Aether
|
||||
Psychic, // Chaos
|
||||
|
||||
// Duration Types
|
||||
InternalDisassembly, // Internal Trauma
|
||||
InternalDisassemblyDuration,
|
||||
Burn,
|
||||
BurnDuration,
|
||||
Frostburn,
|
||||
FrostburnDuration,
|
||||
Poison,
|
||||
PoisonDuration,
|
||||
VitalityDecay,
|
||||
VitalityDecayDuration,
|
||||
Electrocute,
|
||||
ElectrocuteDuration,
|
||||
Seperation, // Bleeding
|
||||
SeperationDuration,
|
||||
|
||||
// Special
|
||||
Elemental, // Even split between Fire, Cold, Lightning
|
||||
|
||||
Damage,
|
||||
|
||||
Speed,
|
||||
|
||||
AttackSpeed,
|
||||
SkillModifier,
|
||||
|
||||
Slow,
|
||||
ArmorPiercing,
|
||||
ReducedStunDuration,
|
||||
SkillCooldownReduction,
|
||||
SkillRecharge,
|
||||
BlockRecovery,
|
||||
BlockChance,
|
||||
Block,
|
||||
HealthRegen,
|
||||
ImaginationRegen,
|
||||
AttacksPerSecond,
|
||||
ImaginationCost,
|
||||
MainWeaponDamage,
|
||||
Stun,
|
||||
|
||||
CriticalDamage,
|
||||
HealthDrain,
|
||||
|
||||
Invalid
|
||||
};
|
||||
|
||||
const std::string& GetModifierTypeColor(ModifierType type);
|
||||
|
||||
const std::string& GetModifierTypeName(ModifierType type);
|
||||
|
||||
const ModifierType GetResistanceType(ModifierType type);
|
||||
|
||||
const bool IsNormalDamageType(ModifierType type);
|
||||
|
||||
const bool IsOverTimeType(ModifierType type);
|
||||
|
||||
const bool IsDurationType(ModifierType type);
|
||||
|
||||
const ModifierType GetOverTimeType(ModifierType type);
|
||||
|
||||
const ModifierType GetDurationType(ModifierType type);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user