mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-20 11:59:40 -06:00
Compare commits
14 Commits
add-sqlite
...
nejlika
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
328743f1e1 | ||
|
|
3178a702a7 | ||
|
|
5e3312850c | ||
|
|
756dc4e44f | ||
|
|
87613f287f | ||
|
|
ac1b00fdaa | ||
|
|
364bcf822a | ||
|
|
d5b2278dc5 | ||
|
|
0bf5ee02e4 | ||
|
|
c2ad76ee66 | ||
|
|
c6528d444a | ||
|
|
78425aacb1 | ||
|
|
b8d610987f | ||
|
|
7845131649 |
@@ -73,4 +73,4 @@ cpp_space_around_assignment_operator=insert
|
||||
cpp_space_pointer_reference_alignment=left
|
||||
cpp_space_around_ternary_operator=insert
|
||||
cpp_wrap_preserve_blocks=one_liners
|
||||
cpp_indent_comment=false
|
||||
cpp_indent_comment=fasle
|
||||
|
||||
30
.github/workflows/build-and-test.yml
vendored
30
.github/workflows/build-and-test.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
os: [ windows-2022, ubuntu-22.04, macos-13 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Add msbuild to PATH (Windows only)
|
||||
if: ${{ matrix.os == 'windows-2022' }}
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
with:
|
||||
vs-version: '[17,18)'
|
||||
msbuild-architecture: x64
|
||||
@@ -33,19 +33,21 @@ jobs:
|
||||
- name: cmake
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
workflowPreset: "ci-${{matrix.os}}"
|
||||
configurePreset: "ci-${{matrix.os}}"
|
||||
buildPreset: "ci-${{matrix.os}}"
|
||||
testPreset: "ci-${{matrix.os}}"
|
||||
- name: artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build-${{matrix.os}}
|
||||
path: |
|
||||
build/*/*Server*
|
||||
build/*/*.ini
|
||||
build/*/*.so
|
||||
build/*/*.dll
|
||||
build/*/vanity/
|
||||
build/*/navmeshes/
|
||||
build/*/migrations/
|
||||
build/*/*.dcf
|
||||
!build/*/*.pdb
|
||||
!build/*/d*/
|
||||
build/*Server*
|
||||
build/*.ini
|
||||
build/*.so
|
||||
build/*.dll
|
||||
build/vanity/
|
||||
build/navmeshes/
|
||||
build/migrations/
|
||||
build/*.dcf
|
||||
!build/*.pdb
|
||||
!build/d*/
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -122,7 +122,4 @@ docker/__pycache__
|
||||
docker-compose.override.yml
|
||||
|
||||
!*Test.bin
|
||||
|
||||
# CMake scripts
|
||||
!cmake/*
|
||||
!cmake/toolchains/*
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(Darkflame
|
||||
HOMEPAGE_URL "https://github.com/DarkflameUniverse/DarkflameServer"
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
project(Darkflame)
|
||||
|
||||
# check if the path to the source directory contains a space
|
||||
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
|
||||
@@ -11,10 +8,8 @@ endif()
|
||||
|
||||
include(CTest)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
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
|
||||
@@ -66,36 +61,35 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
# Disabled no-register
|
||||
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
|
||||
if(UNIX)
|
||||
add_compile_options("-fPIC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC")
|
||||
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
|
||||
|
||||
# For all except Clang and Apple Clang
|
||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options("-static-libgcc" "-lstdc++fs")
|
||||
if(NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs")
|
||||
endif()
|
||||
|
||||
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_link_options("-export-dynamic")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
|
||||
endif()
|
||||
|
||||
if(${GGDB})
|
||||
add_compile_options("-ggdb")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
endif()
|
||||
|
||||
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
|
||||
# Also disable non-portable MSVC volatile behavior
|
||||
add_compile_options("/wd4267" "/utf-8" "/volatile:iso" "/Zc:inline")
|
||||
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_DEBUG ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
|
||||
@@ -115,39 +109,31 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
|
||||
# Create a /logs directory
|
||||
make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
|
||||
# Get DLU config directory
|
||||
if(DEFINED ENV{DLU_CONFIG_DIR})
|
||||
set(DLU_CONFIG_DIR $ENV{DLU_CONFIG_DIR})
|
||||
else()
|
||||
set(DLU_CONFIG_DIR ${PROJECT_BINARY_DIR})
|
||||
endif()
|
||||
message(STATUS "Variable: DLU_CONFIG_DIR = ${DLU_CONFIG_DIR}")
|
||||
|
||||
# Copy resource files on first build
|
||||
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
|
||||
message(STATUS "Checking resource file integrity")
|
||||
|
||||
include(Utils)
|
||||
UpdateConfigOption(${DLU_CONFIG_DIR}/authconfig.ini "port" "auth_server_port")
|
||||
UpdateConfigOption(${DLU_CONFIG_DIR}/chatconfig.ini "port" "chat_server_port")
|
||||
UpdateConfigOption(${DLU_CONFIG_DIR}/masterconfig.ini "port" "master_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port")
|
||||
UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port")
|
||||
|
||||
foreach(resource_file ${RESOURCE_FILES})
|
||||
set(file_size 0)
|
||||
|
||||
if(EXISTS ${DLU_CONFIG_DIR}/${resource_file})
|
||||
file(SIZE ${DLU_CONFIG_DIR}/${resource_file} file_size)
|
||||
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
|
||||
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
|
||||
endif()
|
||||
|
||||
if(${file_size} EQUAL 0)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${DLU_CONFIG_DIR}/${resource_file}
|
||||
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
|
||||
COPYONLY
|
||||
)
|
||||
message(STATUS "Moved " ${resource_file} " to DLU config directory")
|
||||
message(STATUS "Moved " ${resource_file} " to project binary directory")
|
||||
elseif(resource_file MATCHES ".ini")
|
||||
message(STATUS "Checking " ${resource_file} " for missing config options")
|
||||
file(READ ${DLU_CONFIG_DIR}/${resource_file} current_file_contents)
|
||||
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
|
||||
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
|
||||
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
|
||||
set(parsed_current_file_contents "")
|
||||
@@ -178,10 +164,10 @@ foreach(resource_file ${RESOURCE_FILES})
|
||||
set(line_to_add ${line_to_add} ${line})
|
||||
|
||||
foreach(line_to_append ${line_to_add})
|
||||
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
|
||||
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
|
||||
endif()
|
||||
|
||||
set(line_to_add "")
|
||||
@@ -250,15 +236,14 @@ include_directories(
|
||||
"tests/dGameTests"
|
||||
"tests/dGameTests/dComponentsTests"
|
||||
|
||||
SYSTEM
|
||||
"thirdparty/magic_enum/include/magic_enum"
|
||||
"thirdparty/raknet/Source"
|
||||
"thirdparty/tinyxml2"
|
||||
"thirdparty/recastnavigation"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
"thirdparty/cpp-httplib"
|
||||
"thirdparty/MD5"
|
||||
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)
|
||||
@@ -267,17 +252,10 @@ if(APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
# Set warning flags
|
||||
if(MSVC)
|
||||
# add_compile_options("/W4")
|
||||
# Want to enable warnings eventually, but WAY too much noise right now
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||
add_compile_options("-Wuninitialized" "-Wold-style-cast")
|
||||
else()
|
||||
message(WARNING "Unknown compiler: '${CMAKE_CXX_COMPILER_ID}' - No warning flags enabled.")
|
||||
endif()
|
||||
|
||||
# Add linking directories:
|
||||
if (UNIX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags
|
||||
endif()
|
||||
file(
|
||||
GLOB HEADERS_DZONEMANAGER
|
||||
LIST_DIRECTORIES false
|
||||
|
||||
@@ -1,641 +1,128 @@
|
||||
{
|
||||
"version": 6,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 25,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default configure step",
|
||||
"description": "Use 'build' dir and Unix makefiles",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"environment": {
|
||||
"DLU_CONFIG_DIR": "${sourceDir}/build"
|
||||
},
|
||||
"generator": "Unix Makefiles"
|
||||
},
|
||||
{
|
||||
"name": "debug-config",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "relwithdebinfo-config",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release-config",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "clang-config",
|
||||
"hidden": true,
|
||||
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang.cmake"
|
||||
},
|
||||
{
|
||||
"name": "gnu-config",
|
||||
"hidden": true,
|
||||
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-gnu.cmake"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc",
|
||||
"inherits": "default",
|
||||
"displayName": "[Multi] Windows (MSVC)",
|
||||
"description": "Set architecture to 64-bit (b/c RakNet)",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"binaryDir": "${sourceDir}/build/msvc",
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-default",
|
||||
"inherits": "windows-msvc",
|
||||
"displayName": "Windows only Configure Settings",
|
||||
"description": "Sets build and install directories",
|
||||
"generator": "Ninja",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-config",
|
||||
"inherits": "default",
|
||||
"hidden": true,
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"clang-config",
|
||||
"debug-config"
|
||||
],
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "Create a debug build using the Clang toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/clang-debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"clang-config",
|
||||
"relwithdebinfo-config"
|
||||
],
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "Create a release build with debug info using the Clang toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/clang-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"clang-config",
|
||||
"release-config"
|
||||
],
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "Create a release build using the Clang toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/clang-release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"gnu-config",
|
||||
"debug-config"
|
||||
],
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "Create a debug build using the GNU toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/gnu-debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"gnu-config",
|
||||
"relwithdebinfo-config"
|
||||
],
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "Create a release build with debug info using the GNU toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/gnu-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-release",
|
||||
"inherits": [
|
||||
"linux-config",
|
||||
"gnu-config",
|
||||
"release-config"
|
||||
],
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "Create a release build using the GNU toolchain for Linux",
|
||||
"binaryDir": "${sourceDir}/build/gnu-release"
|
||||
},
|
||||
{
|
||||
"name": "macos",
|
||||
"inherits": "default",
|
||||
"displayName": "[Multi] MacOS",
|
||||
"description": "Create a build for MacOS",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Darwin"
|
||||
},
|
||||
"binaryDir": "${sourceDir}/build/macos"
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"displayName": "Default Build",
|
||||
"description": "Default Build",
|
||||
"version": 3,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 14,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default configure step",
|
||||
"description": "Use 'build' dir and Unix makefiles",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"generator": "Unix Makefiles"
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"displayName": "CI configure step for Ubuntu",
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default"
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"displayName": "CI configure step for MacOS",
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default"
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"displayName": "CI configure step for Windows",
|
||||
"description": "Set architecture to 64-bit (b/c RakNet)",
|
||||
"inherits": "default",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-default",
|
||||
"inherits": "ci-windows-2022",
|
||||
"displayName": "Windows only Configure Settings",
|
||||
"description": "Sets build and install directories",
|
||||
"generator": "Ninja",
|
||||
"architecture": {
|
||||
"value": "x64",
|
||||
"strategy": "external"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"displayName": "Default Build",
|
||||
"description": "Default Build",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"configurePreset": "ci-windows-2022",
|
||||
"displayName": "Windows CI Build",
|
||||
"description": "This preset is used by the CI build on windows",
|
||||
"configuration": "RelWithDebInfo",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"configurePreset": "ci-ubuntu-22.04",
|
||||
"displayName": "Linux CI Build",
|
||||
"description": "This preset is used by the CI build on linux",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"configurePreset": "ci-macos-13",
|
||||
"displayName": "MacOS CI Build",
|
||||
"description": "This preset is used by the CI build on MacOS",
|
||||
"jobs": 2
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"configurePreset": "ci-ubuntu-22.04",
|
||||
"displayName": "CI Tests on Linux",
|
||||
"description": "Runs all tests on a linux configuration",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Debug] Windows (MSVC)",
|
||||
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows",
|
||||
"configuration": "Debug"
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"configurePreset": "ci-macos-13",
|
||||
"displayName": "CI Tests on MacOS",
|
||||
"description": "Runs all tests on a Mac configuration",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[RelWithDebInfo] Windows (MSVC)",
|
||||
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows",
|
||||
"configuration": "RelWithDebInfo"
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"configurePreset": "ci-windows-2022",
|
||||
"displayName": "CI Tests on windows",
|
||||
"description": "Runs all tests on a windows configuration",
|
||||
"configuration": "RelWithDebInfo",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Release] Windows (MSVC)",
|
||||
"description": "This preset is used to build in release mode using the MSVC toolchain on Windows",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-debug",
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "This preset is used to build in debug mode using the Clang toolchain on Linux",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-relwithdebinfo",
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "This preset is used to build in release mode with debug info using the Clang toolchain on Linux",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-release",
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "This preset is used to build in release mode using the Clang toolchain on Linux",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-debug",
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "This preset is used to build in debug mode using the GNU toolchain on Linux",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "This preset is used to build in release mode with debug info using the GNU toolchain on Linux",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-release",
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "This preset is used to build in release mode using the GNU toolchain on Linux",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "macos-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Debug] MacOS",
|
||||
"description": "This preset is used to build in debug mode on MacOS",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "macos-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[RelWithDebInfo] MacOS",
|
||||
"description": "This preset is used to build in release mode with debug info on MacOS",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "macos-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Release] MacOS",
|
||||
"description": "This preset is used to build in release mode on MacOS",
|
||||
"configuration": "Release"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
},
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "((example)|(minigzip))+"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-test",
|
||||
"inherits": "default",
|
||||
"configurePreset": "windows-msvc",
|
||||
"hidden": true,
|
||||
"filter": {
|
||||
"exclude": {
|
||||
"name": "((example)|(minigzip))+"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"inherits": "windows-msvc-test",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Debug] Windows (MSVC)",
|
||||
"description": "Runs all tests on a Windows configuration",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-relwithdebinfo",
|
||||
"inherits": "windows-msvc-test",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[RelWithDebInfo] Windows (MSVC)",
|
||||
"description": "Runs all tests on a Windows configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-release",
|
||||
"inherits": "windows-msvc-test",
|
||||
"configurePreset": "windows-msvc",
|
||||
"displayName": "[Release] Windows (MSVC)",
|
||||
"description": "Runs all tests on a Windows configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-debug",
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "Runs all tests on a Linux Clang configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-relwithdebinfo",
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "Runs all tests on a Linux Clang configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-clang-release",
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "Runs all tests on a Linux Clang configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-debug",
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "Runs all tests on a Linux GNU configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "Runs all tests on a Linux GNU configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "linux-gnu-release",
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "Runs all tests on a Linux GNU configuration",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "macos-debug",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Debug] MacOS",
|
||||
"description": "Runs all tests on a MacOS configuration",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "macos-relwithdebinfo",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[RelWithDebInfo] MacOS",
|
||||
"description": "Runs all tests on a MacOS configuration",
|
||||
"configuration": "RelWithDebInfo"
|
||||
},
|
||||
{
|
||||
"name": "macos-release",
|
||||
"inherits": "default",
|
||||
"configurePreset": "macos",
|
||||
"displayName": "[Release] MacOS",
|
||||
"description": "Runs all tests on a MacOS configuration",
|
||||
"configuration": "Release"
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "default"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "default"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-debug",
|
||||
"displayName": "[Debug] Windows (MSVC)",
|
||||
"description": "MSVC debug workflow preset for Windows",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows-msvc"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows-msvc-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows-msvc-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows-msvc-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Windows (MSVC)",
|
||||
"description": "MSVC release with debug info workflow preset for Windows",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows-msvc"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows-msvc-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows-msvc-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"displayName": "[Release] Windows (MSVC)",
|
||||
"description": "CI workflow preset for Windows",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "windows-msvc"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "windows-msvc-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "windows-msvc-release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-debug",
|
||||
"displayName": "[Debug] Linux (GNU)",
|
||||
"description": "GNU debug workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-gnu-debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-gnu-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-gnu-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-gnu-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] Linux (GNU)",
|
||||
"description": "GNU release with debug info workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-gnu-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-gnu-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-gnu-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ci-ubuntu-22.04",
|
||||
"displayName": "[Release] Linux (GNU)",
|
||||
"description": "CI workflow preset for Ubuntu",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-gnu-release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-gnu-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-gnu-release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-debug",
|
||||
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
|
||||
"description": "Clang debug workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-clang-debug"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-clang-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-clang-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-relwithdebinfo",
|
||||
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
|
||||
"description": "Clang release with debug info workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-clang-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-clang-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-clang-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "linux-clang-release",
|
||||
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
|
||||
"description": "Clang release workflow preset for Linux",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "linux-clang-release"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "linux-clang-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "linux-clang-release"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos-debug",
|
||||
"displayName": "[Debug] MacOS",
|
||||
"description": "Release workflow preset for MacOS",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos-debug"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos-debug"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "macos-relwithdebinfo",
|
||||
"displayName": "[RelWithDebInfo] MacOS",
|
||||
"description": "Release with debug info workflow preset for MacOS",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos-relwithdebinfo"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos-relwithdebinfo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-13",
|
||||
"displayName": "[Release] MacOS",
|
||||
"description": "CI workflow preset for MacOS",
|
||||
"steps": [
|
||||
{
|
||||
"type": "configure",
|
||||
"name": "macos"
|
||||
},
|
||||
{
|
||||
"type": "build",
|
||||
"name": "macos-release"
|
||||
},
|
||||
{
|
||||
"type": "test",
|
||||
"name": "macos-release"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
3
build.sh
3
build.sh
@@ -6,7 +6,8 @@ mkdir -p build
|
||||
cd build
|
||||
|
||||
# Run cmake to generate make files
|
||||
cmake -DCMAKE_BUILD_TYPE="Release" ..
|
||||
cmake ..
|
||||
|
||||
# To build utilizing multiple cores, append `-j` and the amount of cores to utilize, for example `cmake --build . --config Release -j8'
|
||||
cmake --build . --config Release $1
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Try and find a clang-16 install, falling back to a generic clang install otherwise
|
||||
find_program(CLANG_C_COMPILER clang-16 | clang REQUIRED)
|
||||
find_program(CLANG_CXX_COMPILER clang++-16 | clang++ REQUIRED)
|
||||
|
||||
# Debug messages
|
||||
message(DEBUG "CLANG_C_COMPILER = ${CLANG_C_COMPILER}")
|
||||
message(DEBUG "CLANG_CXX_COMPILER = ${CLANG_CXX_COMPILER}")
|
||||
|
||||
# Set compilers to clang (need to cache for VSCode tools to work correctly)
|
||||
set(CMAKE_C_COMPILER ${CLANG_C_COMPILER} CACHE STRING "Set C compiler")
|
||||
set(CMAKE_CXX_COMPILER ${CLANG_CXX_COMPILER} CACHE STRING "Set C++ compiler")
|
||||
|
||||
# Set linker to lld
|
||||
add_link_options("-fuse-ld=lld")
|
||||
@@ -1,11 +0,0 @@
|
||||
# Try and find a gcc/g++ install
|
||||
find_program(GNU_C_COMPILER cc | gcc REQUIRED)
|
||||
find_program(GNU_CXX_COMPILER c++ | g++ REQUIRED)
|
||||
|
||||
# Debug messages
|
||||
message(DEBUG "GNU_C_COMPILER = ${GNU_C_COMPILER}")
|
||||
message(DEBUG "GNU_CXX_COMPILER = ${GNU_CXX_COMPILER}")
|
||||
|
||||
# Set compilers to GNU (need to cache for VSCode tools to work correctly)
|
||||
set(CMAKE_C_COMPILER ${GNU_C_COMPILER} CACHE STRING "Set C compiler")
|
||||
set(CMAKE_CXX_COMPILER ${GNU_CXX_COMPILER} CACHE STRING "Set C++ compiler")
|
||||
@@ -21,8 +21,8 @@
|
||||
//Auth includes:
|
||||
#include "AuthPackets.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "MessageType/Server.h"
|
||||
#include "MessageType/Auth.h"
|
||||
#include "eServerMessageType.h"
|
||||
#include "eAuthMessageType.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Server.h"
|
||||
@@ -102,7 +102,7 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
AuthPackets::LoadClaimCodes();
|
||||
|
||||
|
||||
Game::logger->Flush(); // once immediately before main loop
|
||||
while (!Game::ShouldShutdown()) {
|
||||
//Check if we're still connected to master:
|
||||
@@ -166,11 +166,11 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
if (packet->data[0] == ID_USER_PACKET_ENUM) {
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::SERVER) {
|
||||
if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) {
|
||||
if (static_cast<eServerMessageType>(packet->data[3]) == eServerMessageType::VERSION_CONFIRM) {
|
||||
AuthPackets::HandleHandshake(Game::server, packet);
|
||||
}
|
||||
} else if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::AUTH) {
|
||||
if (static_cast<MessageType::Auth>(packet->data[3]) == MessageType::Auth::LOGIN_REQUEST) {
|
||||
if (static_cast<eAuthMessageType>(packet->data[3]) == eAuthMessageType::LOGIN_REQUEST) {
|
||||
AuthPackets::HandleLoginRequest(Game::server, packet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "MessageType/Chat.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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receivingPlayer);
|
||||
|
||||
//portion that will get routed:
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
#include "dConfig.h"
|
||||
#include "eObjectBits.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "MessageType/Client.h"
|
||||
#include "MessageType/Game.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "eClientMessageType.h"
|
||||
#include "eGameMessageType.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "ChatPackets.h"
|
||||
@@ -60,11 +60,11 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
|
||||
//Now, we need to send the friendlist to the server they came from:
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::GET_FRIENDS_LIST_RESPONSE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
|
||||
bitStream.Write<uint8_t>(0);
|
||||
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
|
||||
bitStream.Write<uint16_t>(player.friends.size());
|
||||
@@ -368,10 +368,10 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
|
||||
bool online = player;
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(request.requestor);
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::WHO_RESPONSE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
|
||||
bitStream.Write<uint8_t>(online);
|
||||
bitStream.Write(player.zoneID.GetMapID());
|
||||
bitStream.Write(player.zoneID.GetInstanceID());
|
||||
@@ -391,10 +391,10 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
||||
if (!sender) return;
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(request.requestor);
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::SHOW_ALL_RESPONSE);
|
||||
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());
|
||||
@@ -416,7 +416,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
// the structure the client uses to send this packet is shared in many chat messages
|
||||
// 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) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
@@ -428,7 +428,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
|
||||
eChatChannel channel;
|
||||
uint32_t size;
|
||||
|
||||
|
||||
inStream.IgnoreBytes(4);
|
||||
inStream.Read(channel);
|
||||
inStream.Read(size);
|
||||
@@ -436,7 +436,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
|
||||
LUWString message(size);
|
||||
inStream.Read(message);
|
||||
|
||||
|
||||
LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str());
|
||||
|
||||
switch (channel) {
|
||||
@@ -457,7 +457,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
}
|
||||
}
|
||||
|
||||
// the structure the client uses to send this packet is shared in many chat messages
|
||||
// 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::HandlePrivateChatMessage(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
@@ -484,7 +484,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||
|
||||
LUWString message(size);
|
||||
inStream.Read(message);
|
||||
|
||||
|
||||
LOG("Got a message from (%s) via [%s]: %s to %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str(), receiverName.c_str());
|
||||
|
||||
const auto& receiver = Game::playerContainer.GetPlayerData(receiverName);
|
||||
@@ -515,10 +515,10 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(routeTo.playerID);
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
|
||||
bitStream.Write(sender.playerID);
|
||||
bitStream.Write(channel);
|
||||
bitStream.Write<uint32_t>(0); // not used
|
||||
@@ -757,11 +757,11 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
|
||||
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
|
||||
|
||||
bitStream.Write(LUWString(sender.playerName.c_str()));
|
||||
bitStream.Write(sender.playerID);
|
||||
@@ -772,14 +772,14 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
|
||||
bitStream.Write(eGameMessageType::TEAM_INVITE_CONFIRM);
|
||||
|
||||
bitStream.Write(bLeaderIsFreeTrial);
|
||||
bitStream.Write(i64LeaderID);
|
||||
@@ -799,14 +799,14 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
|
||||
bitStream.Write(eGameMessageType::TEAM_GET_STATUS_RESPONSE);
|
||||
|
||||
bitStream.Write(i64LeaderID);
|
||||
bitStream.Write(i64LeaderZoneID);
|
||||
@@ -824,14 +824,14 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
|
||||
|
||||
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
|
||||
bitStream.Write(eGameMessageType::TEAM_SET_LEADER);
|
||||
|
||||
bitStream.Write(i64PlayerID);
|
||||
|
||||
@@ -841,14 +841,14 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
|
||||
bitStream.Write(eGameMessageType::TEAM_ADD_PLAYER);
|
||||
|
||||
bitStream.Write(bIsFreeTrial);
|
||||
bitStream.Write(bLocal);
|
||||
@@ -870,14 +870,14 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
|
||||
bitStream.Write(eGameMessageType::TEAM_REMOVE_PLAYER);
|
||||
|
||||
bitStream.Write(bDisband);
|
||||
bitStream.Write(bIsKicked);
|
||||
@@ -896,14 +896,14 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
|
||||
|
||||
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(receiver.playerID);
|
||||
bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
|
||||
bitStream.Write(eGameMessageType::TEAM_SET_OFF_WORLD_FLAG);
|
||||
|
||||
bitStream.Write(i64PlayerID);
|
||||
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
|
||||
@@ -930,11 +930,11 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
|
||||
[bool] - is FTP*/
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(friendData.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::UPDATE_FRIEND_NOTIFY);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
|
||||
bitStream.Write<uint8_t>(notifyType);
|
||||
|
||||
std::string playerName = playerData.playerName.c_str();
|
||||
@@ -967,11 +967,11 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_REQUEST);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
|
||||
bitStream.Write(LUWString(sender.playerName));
|
||||
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
|
||||
|
||||
@@ -981,11 +981,11 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
// Portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
|
||||
bitStream.Write(responseCode);
|
||||
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
|
||||
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
|
||||
@@ -1004,11 +1004,11 @@ 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, MessageType::Chat::WORLD_ROUTE_PACKET);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
|
||||
bitStream.Write(receiver.playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::REMOVE_FRIEND_RESPONSE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
|
||||
bitStream.Write<uint8_t>(isSuccessful); //isOnline
|
||||
bitStream.Write(LUWString(personToRemove));
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include "eConnectionType.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "ChatPacketHandler.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "MessageType/World.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "eWorldMessageType.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
@@ -108,7 +108,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
|
||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
|
||||
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
Game::playerContainer.Initialize();
|
||||
@@ -179,7 +179,6 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
if (packet->length < 1) return;
|
||||
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.");
|
||||
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
|
||||
@@ -190,154 +189,154 @@ void HandlePacket(Packet* packet) {
|
||||
inStream.SetReadOffset(BYTES_TO_BITS(1));
|
||||
|
||||
eConnectionType connection;
|
||||
MessageType::Chat chatMessageID;
|
||||
eChatMessageType chatMessageID;
|
||||
|
||||
inStream.Read(connection);
|
||||
if (connection != eConnectionType::CHAT) return;
|
||||
inStream.Read(chatMessageID);
|
||||
|
||||
|
||||
switch (chatMessageID) {
|
||||
case MessageType::Chat::GM_MUTE:
|
||||
case eChatMessageType::GM_MUTE:
|
||||
Game::playerContainer.MuteUpdate(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::CREATE_TEAM:
|
||||
case eChatMessageType::CREATE_TEAM:
|
||||
Game::playerContainer.CreateTeamServer(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::GET_FRIENDS_LIST:
|
||||
case eChatMessageType::GET_FRIENDS_LIST:
|
||||
ChatPacketHandler::HandleFriendlistRequest(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::GET_IGNORE_LIST:
|
||||
case eChatMessageType::GET_IGNORE_LIST:
|
||||
ChatIgnoreList::GetIgnoreList(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::ADD_IGNORE:
|
||||
case eChatMessageType::ADD_IGNORE:
|
||||
ChatIgnoreList::AddIgnore(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::REMOVE_IGNORE:
|
||||
case eChatMessageType::REMOVE_IGNORE:
|
||||
ChatIgnoreList::RemoveIgnore(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_GET_STATUS:
|
||||
case eChatMessageType::TEAM_GET_STATUS:
|
||||
ChatPacketHandler::HandleTeamStatusRequest(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::ADD_FRIEND_REQUEST:
|
||||
case eChatMessageType::ADD_FRIEND_REQUEST:
|
||||
//this involves someone sending the initial request, the response is below, response as in from the other player.
|
||||
//We basically just check to see if this player is online or not and route the packet.
|
||||
ChatPacketHandler::HandleFriendRequest(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::ADD_FRIEND_RESPONSE:
|
||||
case eChatMessageType::ADD_FRIEND_RESPONSE:
|
||||
//This isn't the response a server sent, rather it is a player's response to a received request.
|
||||
//Here, we'll actually have to add them to eachother's friend lists depending on the response code.
|
||||
ChatPacketHandler::HandleFriendResponse(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::REMOVE_FRIEND:
|
||||
case eChatMessageType::REMOVE_FRIEND:
|
||||
ChatPacketHandler::HandleRemoveFriend(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::GENERAL_CHAT_MESSAGE:
|
||||
case eChatMessageType::GENERAL_CHAT_MESSAGE:
|
||||
ChatPacketHandler::HandleChatMessage(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::PRIVATE_CHAT_MESSAGE:
|
||||
case eChatMessageType::PRIVATE_CHAT_MESSAGE:
|
||||
//This message is supposed to be echo'd to both the sender and the receiver
|
||||
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
|
||||
ChatPacketHandler::HandlePrivateChatMessage(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_INVITE:
|
||||
case eChatMessageType::TEAM_INVITE:
|
||||
ChatPacketHandler::HandleTeamInvite(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_INVITE_RESPONSE:
|
||||
case eChatMessageType::TEAM_INVITE_RESPONSE:
|
||||
ChatPacketHandler::HandleTeamInviteResponse(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_LEAVE:
|
||||
case eChatMessageType::TEAM_LEAVE:
|
||||
ChatPacketHandler::HandleTeamLeave(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_SET_LEADER:
|
||||
case eChatMessageType::TEAM_SET_LEADER:
|
||||
ChatPacketHandler::HandleTeamPromote(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_KICK:
|
||||
case eChatMessageType::TEAM_KICK:
|
||||
ChatPacketHandler::HandleTeamKick(packet);
|
||||
break;
|
||||
|
||||
case MessageType::Chat::TEAM_SET_LOOT:
|
||||
case eChatMessageType::TEAM_SET_LOOT:
|
||||
ChatPacketHandler::HandleTeamLootOption(packet);
|
||||
break;
|
||||
case MessageType::Chat::GMLEVEL_UPDATE:
|
||||
case eChatMessageType::GMLEVEL_UPDATE:
|
||||
ChatPacketHandler::HandleGMLevelUpdate(packet);
|
||||
break;
|
||||
case MessageType::Chat::LOGIN_SESSION_NOTIFY:
|
||||
case eChatMessageType::LOGIN_SESSION_NOTIFY:
|
||||
Game::playerContainer.InsertPlayer(packet);
|
||||
break;
|
||||
case MessageType::Chat::GM_ANNOUNCE:{
|
||||
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 MessageType::Chat::UNEXPECTED_DISCONNECT:
|
||||
case eChatMessageType::UNEXPECTED_DISCONNECT:
|
||||
Game::playerContainer.RemovePlayer(packet);
|
||||
break;
|
||||
case MessageType::Chat::WHO:
|
||||
case eChatMessageType::WHO:
|
||||
ChatPacketHandler::HandleWho(packet);
|
||||
break;
|
||||
case MessageType::Chat::SHOW_ALL:
|
||||
case eChatMessageType::SHOW_ALL:
|
||||
ChatPacketHandler::HandleShowAll(packet);
|
||||
break;
|
||||
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
|
||||
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
|
||||
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
|
||||
case MessageType::Chat::WORLD_PARCEL_RESPONSE:
|
||||
case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
|
||||
case MessageType::Chat::GUILD_CREATE:
|
||||
case MessageType::Chat::GUILD_INVITE:
|
||||
case MessageType::Chat::GUILD_INVITE_RESPONSE:
|
||||
case MessageType::Chat::GUILD_LEAVE:
|
||||
case MessageType::Chat::GUILD_KICK:
|
||||
case MessageType::Chat::GUILD_GET_STATUS:
|
||||
case MessageType::Chat::GUILD_GET_ALL:
|
||||
case MessageType::Chat::BLUEPRINT_MODERATED:
|
||||
case MessageType::Chat::BLUEPRINT_MODEL_READY:
|
||||
case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL:
|
||||
case MessageType::Chat::PROPERTY_MODERATION_CHANGED:
|
||||
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED:
|
||||
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT:
|
||||
case MessageType::Chat::MAIL:
|
||||
case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST:
|
||||
case MessageType::Chat::REPUTATION_UPDATE:
|
||||
case MessageType::Chat::SEND_CANNED_TEXT:
|
||||
case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST:
|
||||
case MessageType::Chat::CSR_REQUEST:
|
||||
case MessageType::Chat::CSR_REPLY:
|
||||
case MessageType::Chat::GM_KICK:
|
||||
case MessageType::Chat::WORLD_ROUTE_PACKET:
|
||||
case MessageType::Chat::GET_ZONE_POPULATIONS:
|
||||
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
|
||||
case MessageType::Chat::MATCH_REQUEST:
|
||||
case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE:
|
||||
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE:
|
||||
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
||||
case MessageType::Chat::UGCC_REQUEST:
|
||||
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
||||
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
|
||||
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||
case MessageType::Chat::PLAYER_READY:
|
||||
case MessageType::Chat::GET_DONATION_TOTAL:
|
||||
case MessageType::Chat::UPDATE_DONATION:
|
||||
case MessageType::Chat::PRG_CSR_COMMAND:
|
||||
case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD:
|
||||
case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS:
|
||||
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
|
||||
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
|
||||
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
|
||||
case eChatMessageType::WORLD_PARCEL_RESPONSE:
|
||||
case eChatMessageType::TEAM_MISSED_INVITE_CHECK:
|
||||
case eChatMessageType::GUILD_CREATE:
|
||||
case eChatMessageType::GUILD_INVITE:
|
||||
case eChatMessageType::GUILD_INVITE_RESPONSE:
|
||||
case eChatMessageType::GUILD_LEAVE:
|
||||
case eChatMessageType::GUILD_KICK:
|
||||
case eChatMessageType::GUILD_GET_STATUS:
|
||||
case eChatMessageType::GUILD_GET_ALL:
|
||||
case eChatMessageType::BLUEPRINT_MODERATED:
|
||||
case eChatMessageType::BLUEPRINT_MODEL_READY:
|
||||
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
|
||||
case eChatMessageType::PROPERTY_MODERATION_CHANGED:
|
||||
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED:
|
||||
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT:
|
||||
case eChatMessageType::MAIL:
|
||||
case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST:
|
||||
case eChatMessageType::REPUTATION_UPDATE:
|
||||
case eChatMessageType::SEND_CANNED_TEXT:
|
||||
case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST:
|
||||
case eChatMessageType::CSR_REQUEST:
|
||||
case eChatMessageType::CSR_REPLY:
|
||||
case eChatMessageType::GM_KICK:
|
||||
case eChatMessageType::WORLD_ROUTE_PACKET:
|
||||
case eChatMessageType::GET_ZONE_POPULATIONS:
|
||||
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
|
||||
case eChatMessageType::MATCH_REQUEST:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
|
||||
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
|
||||
case eChatMessageType::UGCC_REQUEST:
|
||||
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
|
||||
case eChatMessageType::ACHIEVEMENT_NOTIFY:
|
||||
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
|
||||
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(chatMessageID).data(), chatMessageID);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "eConnectionType.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "eChatMessageType.h"
|
||||
|
||||
void PlayerContainer::Initialize() {
|
||||
m_MaxNumberOfBestFriends =
|
||||
@@ -36,19 +36,16 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
data.playerID = playerId;
|
||||
|
||||
uint32_t len;
|
||||
if (!inStream.Read<uint32_t>(len)) return;
|
||||
inStream.Read<uint32_t>(len);
|
||||
|
||||
if (len > 33) {
|
||||
LOG("Received a really long player name, probably a fake packet %i.", len);
|
||||
return;
|
||||
for (int i = 0; i < len; i++) {
|
||||
char character; inStream.Read<char>(character);
|
||||
data.playerName += character;
|
||||
}
|
||||
|
||||
data.playerName.resize(len);
|
||||
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
|
||||
|
||||
if (!inStream.Read(data.zoneID)) return;
|
||||
if (!inStream.Read(data.muteExpire)) return;
|
||||
if (!inStream.Read(data.gmLevel)) return;
|
||||
inStream.Read(data.zoneID);
|
||||
inStream.Read(data.muteExpire);
|
||||
inStream.Read(data.gmLevel);
|
||||
data.sysAddr = packet->systemAddress;
|
||||
|
||||
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
|
||||
@@ -125,11 +122,6 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||
size_t membersSize = 0;
|
||||
inStream.Read(membersSize);
|
||||
|
||||
if (membersSize >= 4) {
|
||||
LOG("Tried to create a team with more than 4 players");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LWOOBJID> members;
|
||||
|
||||
members.reserve(membersSize);
|
||||
@@ -148,13 +140,14 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
|
||||
|
||||
if (team != nullptr) {
|
||||
team->zoneId = zoneId;
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
|
||||
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_MUTE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
|
||||
|
||||
bitStream.Write(player);
|
||||
bitStream.Write(time);
|
||||
@@ -361,7 +354,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
|
||||
|
||||
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::TEAM_GET_STATUS);
|
||||
|
||||
bitStream.Write(team->teamID);
|
||||
bitStream.Write(deleteTeam);
|
||||
|
||||
@@ -9,54 +9,73 @@
|
||||
* AMF3 Deserializer written by EmosewaMC
|
||||
*/
|
||||
|
||||
std::unique_ptr<AMFBaseValue> AMFDeserialize::Read(RakNet::BitStream& inStream) {
|
||||
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream& inStream) {
|
||||
AMFBaseValue* returnValue = nullptr;
|
||||
// Read in the value type from the bitStream
|
||||
eAmf 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:
|
||||
return std::make_unique<AMFBaseValue>();
|
||||
case eAmf::Null:
|
||||
return std::make_unique<AMFNullValue>();
|
||||
case eAmf::False:
|
||||
return std::make_unique<AMFBoolValue>(false);
|
||||
case eAmf::True:
|
||||
return std::make_unique<AMFBoolValue>(true);
|
||||
case eAmf::Integer:
|
||||
return ReadAmfInteger(inStream);
|
||||
case eAmf::Double:
|
||||
return ReadAmfDouble(inStream);
|
||||
case eAmf::String:
|
||||
return ReadAmfString(inStream);
|
||||
case eAmf::Array:
|
||||
return ReadAmfArray(inStream);
|
||||
case eAmf::Undefined: {
|
||||
returnValue = new AMFBaseValue();
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Null: {
|
||||
returnValue = new AMFNullValue();
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::False: {
|
||||
returnValue = new AMFBoolValue(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::True: {
|
||||
returnValue = new AMFBoolValue(true);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Integer: {
|
||||
returnValue = ReadAmfInteger(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Double: {
|
||||
returnValue = ReadAmfDouble(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::String: {
|
||||
returnValue = ReadAmfString(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case eAmf::Array: {
|
||||
returnValue = ReadAmfArray(inStream);
|
||||
break;
|
||||
}
|
||||
|
||||
// These values are unimplemented in the live client and will remain unimplemented
|
||||
// unless someone modifies the client to allow serializing of these values.
|
||||
case eAmf::XMLDoc:
|
||||
[[fallthrough]];
|
||||
case eAmf::Date:
|
||||
[[fallthrough]];
|
||||
case eAmf::Object:
|
||||
[[fallthrough]];
|
||||
case eAmf::XML:
|
||||
[[fallthrough]];
|
||||
case eAmf::ByteArray:
|
||||
[[fallthrough]];
|
||||
case eAmf::VectorInt:
|
||||
[[fallthrough]];
|
||||
case eAmf::VectorUInt:
|
||||
[[fallthrough]];
|
||||
case eAmf::VectorDouble:
|
||||
[[fallthrough]];
|
||||
case eAmf::VectorObject:
|
||||
[[fallthrough]];
|
||||
case eAmf::Dictionary:
|
||||
case eAmf::Dictionary: {
|
||||
throw marker;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
|
||||
break;
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream& inStream) {
|
||||
@@ -99,14 +118,14 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AMFDoubleValue> AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
|
||||
double value;
|
||||
inStream.Read<double>(value);
|
||||
return std::make_unique<AMFDoubleValue>(value);
|
||||
return new AMFDoubleValue(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
|
||||
auto arrayValue = std::make_unique<AMFArrayValue>();
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
|
||||
auto arrayValue = new AMFArrayValue();
|
||||
|
||||
// Read size of dense array
|
||||
const auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
|
||||
@@ -124,10 +143,10 @@ std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& i
|
||||
return arrayValue;
|
||||
}
|
||||
|
||||
std::unique_ptr<AMFStringValue> AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
|
||||
return std::make_unique<AMFStringValue>(ReadString(inStream));
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
|
||||
return new AMFStringValue(ReadString(inStream));
|
||||
}
|
||||
|
||||
std::unique_ptr<AMFIntValue> AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
|
||||
return std::make_unique<AMFIntValue>(ReadU29(inStream)); // NOTE: NARROWING CONVERSION FROM UINT TO INT. IS THIS INTENDED?
|
||||
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
|
||||
return new AMFIntValue(ReadU29(inStream));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Amf3.h"
|
||||
#include "BitStream.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class AMFBaseValue;
|
||||
|
||||
class AMFDeserialize {
|
||||
public:
|
||||
/**
|
||||
@@ -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.
|
||||
*/
|
||||
std::unique_ptr<AMFBaseValue> Read(RakNet::BitStream& inStream);
|
||||
AMFBaseValue* Read(RakNet::BitStream& inStream);
|
||||
private:
|
||||
/**
|
||||
* @brief Private method to read a U29 integer from a bitstream
|
||||
@@ -39,7 +39,7 @@ private:
|
||||
* @param inStream bitStream to read data from
|
||||
* @return Double value represented as an AMFValue
|
||||
*/
|
||||
static std::unique_ptr<AMFDoubleValue> 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
|
||||
*/
|
||||
std::unique_ptr<AMFArrayValue> 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
|
||||
*/
|
||||
std::unique_ptr<AMFStringValue> 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
|
||||
*/
|
||||
static std::unique_ptr<AMFIntValue> ReadAmfInteger(RakNet::BitStream& inStream);
|
||||
AMFBaseValue* ReadAmfInteger(RakNet::BitStream& inStream);
|
||||
|
||||
/**
|
||||
* List of strings read so far saved to be read by reference.
|
||||
|
||||
@@ -105,14 +105,27 @@ using AMFDoubleValue = AMFValue<double>;
|
||||
* and are not to be deleted by a caller.
|
||||
*/
|
||||
class AMFArrayValue : public AMFBaseValue {
|
||||
using AMFAssociative =
|
||||
std::unordered_map<std::string, std::unique_ptr<AMFBaseValue>, GeneralUtils::transparent_string_hash, std::equal_to<>>;
|
||||
|
||||
using AMFDense = std::vector<std::unique_ptr<AMFBaseValue>>;
|
||||
using AMFAssociative = std::unordered_map<std::string, AMFBaseValue*>;
|
||||
using AMFDense = std::vector<AMFBaseValue*>;
|
||||
|
||||
public:
|
||||
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
|
||||
|
||||
~AMFArrayValue() override {
|
||||
for (const auto* valueToDelete : GetDense()) {
|
||||
if (valueToDelete) {
|
||||
delete valueToDelete;
|
||||
valueToDelete = nullptr;
|
||||
}
|
||||
}
|
||||
for (auto valueToDelete : GetAssociative()) {
|
||||
if (valueToDelete.second) {
|
||||
delete valueToDelete.second;
|
||||
valueToDelete.second = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Associative portion of the object
|
||||
*/
|
||||
@@ -138,32 +151,30 @@ public:
|
||||
* or nullptr if a key existed and was not the same type
|
||||
*/
|
||||
template <typename ValueType>
|
||||
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string_view key, const ValueType value) {
|
||||
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, const ValueType value) {
|
||||
const auto element = m_Associative.find(key);
|
||||
AMFValue<ValueType>* val = nullptr;
|
||||
bool found = true;
|
||||
if (element == m_Associative.cend()) {
|
||||
auto newVal = std::make_unique<AMFValue<ValueType>>(value);
|
||||
val = newVal.get();
|
||||
m_Associative.emplace(key, std::move(newVal));
|
||||
val = new AMFValue<ValueType>(value);
|
||||
m_Associative.emplace(key, val);
|
||||
} else {
|
||||
val = dynamic_cast<AMFValue<ValueType>*>(element->second.get());
|
||||
val = dynamic_cast<AMFValue<ValueType>*>(element->second);
|
||||
found = false;
|
||||
}
|
||||
return std::make_pair(val, found);
|
||||
}
|
||||
|
||||
// Associates an array with a string key
|
||||
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string_view key) {
|
||||
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
|
||||
const auto element = m_Associative.find(key);
|
||||
AMFArrayValue* val = nullptr;
|
||||
bool found = true;
|
||||
if (element == m_Associative.cend()) {
|
||||
auto newVal = std::make_unique<AMFArrayValue>();
|
||||
val = newVal.get();
|
||||
m_Associative.emplace(key, std::move(newVal));
|
||||
val = new AMFArrayValue();
|
||||
m_Associative.emplace(key, val);
|
||||
} else {
|
||||
val = dynamic_cast<AMFArrayValue*>(element->second.get());
|
||||
val = dynamic_cast<AMFArrayValue*>(element->second);
|
||||
found = false;
|
||||
}
|
||||
return std::make_pair(val, found);
|
||||
@@ -171,13 +182,15 @@ public:
|
||||
|
||||
// Associates an array with an integer key
|
||||
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
|
||||
AMFArrayValue* val = nullptr;
|
||||
bool inserted = false;
|
||||
if (index >= m_Dense.size()) {
|
||||
m_Dense.resize(index + 1);
|
||||
m_Dense.at(index) = std::make_unique<AMFArrayValue>();
|
||||
val = new AMFArrayValue();
|
||||
m_Dense.at(index) = val;
|
||||
inserted = true;
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()), inserted);
|
||||
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index)), inserted);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,13 +205,15 @@ public:
|
||||
*/
|
||||
template <typename ValueType>
|
||||
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
|
||||
AMFValue<ValueType>* val = nullptr;
|
||||
bool inserted = false;
|
||||
if (index >= m_Dense.size()) {
|
||||
m_Dense.resize(index + 1);
|
||||
m_Dense.at(index) = std::make_unique<AMFValue<ValueType>>(value);
|
||||
val = new AMFValue<ValueType>(value);
|
||||
m_Dense.at(index) = val;
|
||||
inserted = true;
|
||||
}
|
||||
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index).get()), inserted);
|
||||
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index)), inserted);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,12 +225,13 @@ public:
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void Insert(const std::string_view key, std::unique_ptr<AMFBaseValue> value) {
|
||||
void Insert(const std::string& key, AMFBaseValue* const value) {
|
||||
const auto element = m_Associative.find(key);
|
||||
if (element != m_Associative.cend() && element->second) {
|
||||
element->second = std::move(value);
|
||||
delete element->second;
|
||||
element->second = value;
|
||||
} else {
|
||||
m_Associative.emplace(key, std::move(value));
|
||||
m_Associative.emplace(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +244,14 @@ public:
|
||||
* @param key The key to associate with the value
|
||||
* @param value The value to insert
|
||||
*/
|
||||
void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
|
||||
if (index >= m_Dense.size()) {
|
||||
void Insert(const size_t index, AMFBaseValue* const value) {
|
||||
if (index < m_Dense.size()) {
|
||||
const AMFDense::const_iterator itr = m_Dense.cbegin() + index;
|
||||
if (*itr) delete m_Dense.at(index);
|
||||
} else {
|
||||
m_Dense.resize(index + 1);
|
||||
}
|
||||
m_Dense.at(index) = std::move(value);
|
||||
m_Dense.at(index) = value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +279,8 @@ public:
|
||||
void Remove(const std::string& key, const bool deleteValue = true) {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
if (it != m_Associative.cend()) {
|
||||
if (deleteValue) m_Associative.erase(it);
|
||||
if (deleteValue) delete it->second;
|
||||
m_Associative.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +290,7 @@ public:
|
||||
void Remove(const size_t index) {
|
||||
if (!m_Dense.empty() && index < m_Dense.size()) {
|
||||
const auto itr = m_Dense.cbegin() + index;
|
||||
if (*itr) delete (*itr);
|
||||
m_Dense.erase(itr);
|
||||
}
|
||||
}
|
||||
@@ -278,16 +299,16 @@ public:
|
||||
if (!m_Dense.empty()) Remove(m_Dense.size() - 1);
|
||||
}
|
||||
|
||||
[[nodiscard]] AMFArrayValue* GetArray(const std::string_view key) const {
|
||||
[[nodiscard]] AMFArrayValue* GetArray(const std::string& key) const {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second.get()) : nullptr;
|
||||
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second) : nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
|
||||
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()) : nullptr;
|
||||
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index)) : nullptr;
|
||||
}
|
||||
|
||||
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string_view key) {
|
||||
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) {
|
||||
return static_cast<AMFArrayValue*>(Insert(key).first);
|
||||
}
|
||||
|
||||
@@ -309,17 +330,17 @@ public:
|
||||
* @return The AMFValue
|
||||
*/
|
||||
template <typename AmfType>
|
||||
[[nodiscard]] AMFValue<AmfType>* Get(const std::string_view key) const {
|
||||
[[nodiscard]] AMFValue<AmfType>* Get(const std::string& key) const {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
return it != m_Associative.cend() ?
|
||||
dynamic_cast<AMFValue<AmfType>*>(it->second.get()) :
|
||||
dynamic_cast<AMFValue<AmfType>*>(it->second) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
// Get from the array but dont cast it
|
||||
[[nodiscard]] AMFBaseValue* Get(const std::string_view key) const {
|
||||
[[nodiscard]] AMFBaseValue* Get(const std::string& key) const {
|
||||
const AMFAssociative::const_iterator it = m_Associative.find(key);
|
||||
return it != m_Associative.cend() ? it->second.get() : nullptr;
|
||||
return it != m_Associative.cend() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,13 +355,13 @@ public:
|
||||
template <typename AmfType>
|
||||
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
|
||||
return index < m_Dense.size() ?
|
||||
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index).get()) :
|
||||
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index)) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
// Get from the dense but dont cast it
|
||||
[[nodiscard]] AMFBaseValue* Get(const size_t index) const {
|
||||
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
|
||||
return index < m_Dense.size() ? m_Dense.at(index) : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -201,7 +201,7 @@ void OnTerminate() {
|
||||
}
|
||||
|
||||
void MakeBacktrace() {
|
||||
struct sigaction sigact{};
|
||||
struct sigaction sigact;
|
||||
|
||||
sigact.sa_sigaction = CritErrHdlr;
|
||||
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
|
||||
|
||||
@@ -129,29 +129,6 @@ namespace GeneralUtils {
|
||||
|
||||
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
|
||||
|
||||
/**
|
||||
* Transparent string hasher - used to allow string_view key lookups for maps storing std::string keys
|
||||
* https://www.reddit.com/r/cpp_questions/comments/12xw3sn/find_stdstring_view_in_unordered_map_with/jhki225/
|
||||
* https://godbolt.org/z/789xv8Eeq
|
||||
*/
|
||||
template <typename... Bases>
|
||||
struct overload : Bases... {
|
||||
using is_transparent = void;
|
||||
using Bases::operator() ... ;
|
||||
};
|
||||
|
||||
struct char_pointer_hash {
|
||||
auto operator()(const char* const ptr) const noexcept {
|
||||
return std::hash<std::string_view>{}(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
using transparent_string_hash = overload<
|
||||
std::hash<std::string>,
|
||||
std::hash<std::string_view>,
|
||||
char_pointer_hash
|
||||
>;
|
||||
|
||||
// Concept constraining to enum types
|
||||
template <typename T>
|
||||
concept Enum = std::is_enum_v<T>;
|
||||
|
||||
@@ -89,4 +89,4 @@ private:
|
||||
|
||||
};
|
||||
|
||||
#endif //!__IMPLEMENTATION_H__
|
||||
#endif //!__IMPLEMENTATION_H__
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace MessageType {
|
||||
enum class Auth : uint32_t {
|
||||
LOGIN_REQUEST = 0,
|
||||
LOGOUT_REQUEST,
|
||||
CREATE_NEW_ACCOUNT_REQUEST,
|
||||
LEGOINTERFACE_AUTH_RESPONSE,
|
||||
SESSIONKEY_RECEIVED_CONFIRM,
|
||||
RUNTIME_CONFIG
|
||||
};
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace MessageType {
|
||||
//! The Internal Chat Packet Identifiers
|
||||
enum class Chat : uint32_t {
|
||||
LOGIN_SESSION_NOTIFY = 0,
|
||||
GENERAL_CHAT_MESSAGE,
|
||||
PRIVATE_CHAT_MESSAGE,
|
||||
USER_CHANNEL_CHAT_MESSAGE,
|
||||
WORLD_DISCONNECT_REQUEST,
|
||||
WORLD_PROXIMITY_RESPONSE,
|
||||
WORLD_PARCEL_RESPONSE,
|
||||
ADD_FRIEND_REQUEST,
|
||||
ADD_FRIEND_RESPONSE,
|
||||
REMOVE_FRIEND,
|
||||
GET_FRIENDS_LIST,
|
||||
ADD_IGNORE,
|
||||
REMOVE_IGNORE,
|
||||
GET_IGNORE_LIST,
|
||||
TEAM_MISSED_INVITE_CHECK,
|
||||
TEAM_INVITE,
|
||||
TEAM_INVITE_RESPONSE,
|
||||
TEAM_KICK,
|
||||
TEAM_LEAVE,
|
||||
TEAM_SET_LOOT,
|
||||
TEAM_SET_LEADER,
|
||||
TEAM_GET_STATUS,
|
||||
GUILD_CREATE,
|
||||
GUILD_INVITE,
|
||||
GUILD_INVITE_RESPONSE,
|
||||
GUILD_LEAVE,
|
||||
GUILD_KICK,
|
||||
GUILD_GET_STATUS,
|
||||
GUILD_GET_ALL,
|
||||
SHOW_ALL,
|
||||
BLUEPRINT_MODERATED,
|
||||
BLUEPRINT_MODEL_READY,
|
||||
PROPERTY_READY_FOR_APPROVAL,
|
||||
PROPERTY_MODERATION_CHANGED,
|
||||
PROPERTY_BUILDMODE_CHANGED,
|
||||
PROPERTY_BUILDMODE_CHANGED_REPORT,
|
||||
MAIL,
|
||||
WORLD_INSTANCE_LOCATION_REQUEST,
|
||||
REPUTATION_UPDATE,
|
||||
SEND_CANNED_TEXT,
|
||||
GMLEVEL_UPDATE,
|
||||
CHARACTER_NAME_CHANGE_REQUEST,
|
||||
CSR_REQUEST,
|
||||
CSR_REPLY,
|
||||
GM_KICK,
|
||||
GM_ANNOUNCE,
|
||||
GM_MUTE,
|
||||
ACTIVITY_UPDATE,
|
||||
WORLD_ROUTE_PACKET,
|
||||
GET_ZONE_POPULATIONS,
|
||||
REQUEST_MINIMUM_CHAT_MODE,
|
||||
REQUEST_MINIMUM_CHAT_MODE_PRIVATE,
|
||||
MATCH_REQUEST,
|
||||
UGCMANIFEST_REPORT_MISSING_FILE,
|
||||
UGCMANIFEST_REPORT_DONE_FILE,
|
||||
UGCMANIFEST_REPORT_DONE_BLUEPRINT,
|
||||
UGCC_REQUEST,
|
||||
WHO,
|
||||
WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE,
|
||||
ACHIEVEMENT_NOTIFY,
|
||||
GM_CLOSE_PRIVATE_CHAT_WINDOW,
|
||||
UNEXPECTED_DISCONNECT,
|
||||
PLAYER_READY,
|
||||
GET_DONATION_TOTAL,
|
||||
UPDATE_DONATION,
|
||||
PRG_CSR_COMMAND,
|
||||
HEARTBEAT_REQUEST_FROM_WORLD,
|
||||
UPDATE_FREE_TRIAL_STATUS,
|
||||
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
|
||||
CREATE_TEAM,
|
||||
};
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace MessageType {
|
||||
enum class Client : uint32_t {
|
||||
LOGIN_RESPONSE = 0,
|
||||
LOGOUT_RESPONSE,
|
||||
LOAD_STATIC_ZONE,
|
||||
CREATE_OBJECT,
|
||||
CREATE_CHARACTER,
|
||||
CREATE_CHARACTER_EXTENDED,
|
||||
CHARACTER_LIST_RESPONSE,
|
||||
CHARACTER_CREATE_RESPONSE,
|
||||
CHARACTER_RENAME_RESPONSE,
|
||||
CHAT_CONNECT_RESPONSE,
|
||||
AUTH_ACCOUNT_CREATE_RESPONSE,
|
||||
DELETE_CHARACTER_RESPONSE,
|
||||
GAME_MSG,
|
||||
CONNECT_CHAT,
|
||||
TRANSFER_TO_WORLD,
|
||||
IMPENDING_RELOAD_NOTIFY,
|
||||
MAKE_GM_RESPONSE,
|
||||
HTTP_MONITOR_INFO_RESPONSE,
|
||||
SLASH_PUSH_MAP_RESPONSE,
|
||||
SLASH_PULL_MAP_RESPONSE,
|
||||
SLASH_LOCK_MAP_RESPONSE,
|
||||
BLUEPRINT_SAVE_RESPONSE,
|
||||
BLUEPRINT_LUP_SAVE_RESPONSE,
|
||||
BLUEPRINT_LOAD_RESPONSE_ITEMID,
|
||||
BLUEPRINT_GET_ALL_DATA_RESPONSE,
|
||||
MODEL_INSTANTIATE_RESPONSE,
|
||||
DEBUG_OUTPUT,
|
||||
ADD_FRIEND_REQUEST,
|
||||
ADD_FRIEND_RESPONSE,
|
||||
REMOVE_FRIEND_RESPONSE,
|
||||
GET_FRIENDS_LIST_RESPONSE,
|
||||
UPDATE_FRIEND_NOTIFY,
|
||||
ADD_IGNORE_RESPONSE,
|
||||
REMOVE_IGNORE_RESPONSE,
|
||||
GET_IGNORE_LIST_RESPONSE,
|
||||
TEAM_INVITE,
|
||||
TEAM_INVITE_INITIAL_RESPONSE,
|
||||
GUILD_CREATE_RESPONSE,
|
||||
GUILD_GET_STATUS_RESPONSE,
|
||||
GUILD_INVITE,
|
||||
GUILD_INVITE_INITIAL_RESPONSE,
|
||||
GUILD_INVITE_FINAL_RESPONSE,
|
||||
GUILD_INVITE_CONFIRM,
|
||||
GUILD_ADD_PLAYER,
|
||||
GUILD_REMOVE_PLAYER,
|
||||
GUILD_LOGIN_LOGOUT,
|
||||
GUILD_RANK_CHANGE,
|
||||
GUILD_DATA,
|
||||
GUILD_STATUS,
|
||||
MAIL,
|
||||
DB_PROXY_RESULT,
|
||||
SHOW_ALL_RESPONSE,
|
||||
WHO_RESPONSE,
|
||||
SEND_CANNED_TEXT,
|
||||
UPDATE_CHARACTER_NAME,
|
||||
SET_NETWORK_SIMULATOR,
|
||||
INVALID_CHAT_MESSAGE,
|
||||
MINIMUM_CHAT_MODE_RESPONSE,
|
||||
MINIMUM_CHAT_MODE_RESPONSE_PRIVATE,
|
||||
CHAT_MODERATION_STRING,
|
||||
UGC_MANIFEST_RESPONSE,
|
||||
IN_LOGIN_QUEUE,
|
||||
SERVER_STATES,
|
||||
GM_CLOSE_TARGET_CHAT_WINDOW,
|
||||
GENERAL_TEXT_FOR_LOCALIZATION,
|
||||
UPDATE_FREE_TRIAL_STATUS,
|
||||
UGC_DOWNLOAD_FAILED = 120
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace MessageType {
|
||||
enum class Master : uint32_t {
|
||||
REQUEST_PERSISTENT_ID = 1,
|
||||
REQUEST_PERSISTENT_ID_RESPONSE,
|
||||
REQUEST_ZONE_TRANSFER,
|
||||
REQUEST_ZONE_TRANSFER_RESPONSE,
|
||||
SERVER_INFO,
|
||||
REQUEST_SESSION_KEY,
|
||||
SET_SESSION_KEY,
|
||||
SESSION_KEY_RESPONSE,
|
||||
PLAYER_ADDED,
|
||||
PLAYER_REMOVED,
|
||||
|
||||
CREATE_PRIVATE_ZONE,
|
||||
REQUEST_PRIVATE_ZONE,
|
||||
|
||||
WORLD_READY,
|
||||
PREP_ZONE,
|
||||
|
||||
SHUTDOWN,
|
||||
SHUTDOWN_RESPONSE,
|
||||
SHUTDOWN_IMMEDIATE,
|
||||
|
||||
SHUTDOWN_UNIVERSE,
|
||||
|
||||
AFFIRM_TRANSFER_REQUEST,
|
||||
AFFIRM_TRANSFER_RESPONSE,
|
||||
|
||||
NEW_SESSION_ALERT
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace MessageType {
|
||||
//! The Internal Server Packet Identifiers
|
||||
enum class Server : uint32_t {
|
||||
VERSION_CONFIRM = 0,
|
||||
DISCONNECT_NOTIFY,
|
||||
GENERAL_NOTIFY
|
||||
};
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace MessageType {
|
||||
enum class World : uint32_t {
|
||||
VALIDATION = 1, // Session info
|
||||
CHARACTER_LIST_REQUEST,
|
||||
CHARACTER_CREATE_REQUEST,
|
||||
LOGIN_REQUEST, // Character selected
|
||||
GAME_MSG,
|
||||
CHARACTER_DELETE_REQUEST,
|
||||
CHARACTER_RENAME_REQUEST,
|
||||
HAPPY_FLOWER_MODE_NOTIFY,
|
||||
SLASH_RELOAD_MAP, // Reload map cmp
|
||||
SLASH_PUSH_MAP_REQUEST, // Push map req cmd
|
||||
SLASH_PUSH_MAP, // Push map cmd
|
||||
SLASH_PULL_MAP, // Pull map cmd
|
||||
LOCK_MAP_REQUEST,
|
||||
GENERAL_CHAT_MESSAGE, // General chat message
|
||||
HTTP_MONITOR_INFO_REQUEST,
|
||||
SLASH_DEBUG_SCRIPTS, // Debug scripts cmd
|
||||
MODELS_CLEAR,
|
||||
EXHIBIT_INSERT_MODEL,
|
||||
LEVEL_LOAD_COMPLETE, // Character data request
|
||||
TMP_GUILD_CREATE,
|
||||
ROUTE_PACKET, // Social?
|
||||
POSITION_UPDATE,
|
||||
MAIL,
|
||||
WORD_CHECK, // AllowList word check
|
||||
STRING_CHECK, // AllowList string check
|
||||
GET_PLAYERS_IN_ZONE,
|
||||
REQUEST_UGC_MANIFEST_INFO,
|
||||
BLUEPRINT_GET_ALL_DATA_REQUEST,
|
||||
CANCEL_MAP_QUEUE,
|
||||
HANDLE_FUNNESS,
|
||||
FAKE_PRG_CSR_MESSAGE,
|
||||
REQUEST_FREE_TRIAL_REFRESH,
|
||||
GM_SET_FREE_TRIAL_STATUS,
|
||||
UI_HELP_TOP_5 = 91
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<MessageType::World> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 91;
|
||||
};
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <set>
|
||||
#include "BitStream.h"
|
||||
#include "eConnectionType.h"
|
||||
#include "MessageType/Client.h"
|
||||
#include "eClientMessageType.h"
|
||||
#include "BitStreamUtils.h"
|
||||
|
||||
#pragma warning (disable:4251) //Disables SQL warnings
|
||||
@@ -33,7 +33,7 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
|
||||
#define CBITSTREAM RakNet::BitStream bitStream;
|
||||
#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, MessageType::Client::GAME_MSG);
|
||||
#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);
|
||||
|
||||
|
||||
15
dCommon/dEnums/eAuthMessageType.h
Normal file
15
dCommon/dEnums/eAuthMessageType.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __EAUTHMESSAGETYPE__H__
|
||||
#define __EAUTHMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eAuthMessageType : uint32_t {
|
||||
LOGIN_REQUEST = 0,
|
||||
LOGOUT_REQUEST,
|
||||
CREATE_NEW_ACCOUNT_REQUEST,
|
||||
LEGOINTERFACE_AUTH_RESPONSE,
|
||||
SESSIONKEY_RECEIVED_CONFIRM,
|
||||
RUNTIME_CONFIG
|
||||
};
|
||||
|
||||
#endif //!__EAUTHMESSAGETYPE__H__
|
||||
@@ -15,8 +15,7 @@ enum class eCharacterVersion : uint32_t {
|
||||
// Fixes vault size value
|
||||
VAULT_SIZE,
|
||||
// Fixes speed base value in level component
|
||||
SPEED_BASE,
|
||||
UP_TO_DATE, // will become NJ_JAYMISSIONS
|
||||
UP_TO_DATE, // will become SPEED_BASE
|
||||
};
|
||||
|
||||
#endif //!__ECHARACTERVERSION__H__
|
||||
|
||||
80
dCommon/dEnums/eChatMessageType.h
Normal file
80
dCommon/dEnums/eChatMessageType.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef __ECHATMESSAGETYPE__H__
|
||||
#define __ECHATMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
//! The Internal Chat Packet Identifiers
|
||||
enum class eChatMessageType :uint32_t {
|
||||
LOGIN_SESSION_NOTIFY = 0,
|
||||
GENERAL_CHAT_MESSAGE,
|
||||
PRIVATE_CHAT_MESSAGE,
|
||||
USER_CHANNEL_CHAT_MESSAGE,
|
||||
WORLD_DISCONNECT_REQUEST,
|
||||
WORLD_PROXIMITY_RESPONSE,
|
||||
WORLD_PARCEL_RESPONSE,
|
||||
ADD_FRIEND_REQUEST,
|
||||
ADD_FRIEND_RESPONSE,
|
||||
REMOVE_FRIEND,
|
||||
GET_FRIENDS_LIST,
|
||||
ADD_IGNORE,
|
||||
REMOVE_IGNORE,
|
||||
GET_IGNORE_LIST,
|
||||
TEAM_MISSED_INVITE_CHECK,
|
||||
TEAM_INVITE,
|
||||
TEAM_INVITE_RESPONSE,
|
||||
TEAM_KICK,
|
||||
TEAM_LEAVE,
|
||||
TEAM_SET_LOOT,
|
||||
TEAM_SET_LEADER,
|
||||
TEAM_GET_STATUS,
|
||||
GUILD_CREATE,
|
||||
GUILD_INVITE,
|
||||
GUILD_INVITE_RESPONSE,
|
||||
GUILD_LEAVE,
|
||||
GUILD_KICK,
|
||||
GUILD_GET_STATUS,
|
||||
GUILD_GET_ALL,
|
||||
SHOW_ALL,
|
||||
BLUEPRINT_MODERATED,
|
||||
BLUEPRINT_MODEL_READY,
|
||||
PROPERTY_READY_FOR_APPROVAL,
|
||||
PROPERTY_MODERATION_CHANGED,
|
||||
PROPERTY_BUILDMODE_CHANGED,
|
||||
PROPERTY_BUILDMODE_CHANGED_REPORT,
|
||||
MAIL,
|
||||
WORLD_INSTANCE_LOCATION_REQUEST,
|
||||
REPUTATION_UPDATE,
|
||||
SEND_CANNED_TEXT,
|
||||
GMLEVEL_UPDATE,
|
||||
CHARACTER_NAME_CHANGE_REQUEST,
|
||||
CSR_REQUEST,
|
||||
CSR_REPLY,
|
||||
GM_KICK,
|
||||
GM_ANNOUNCE,
|
||||
GM_MUTE,
|
||||
ACTIVITY_UPDATE,
|
||||
WORLD_ROUTE_PACKET,
|
||||
GET_ZONE_POPULATIONS,
|
||||
REQUEST_MINIMUM_CHAT_MODE,
|
||||
REQUEST_MINIMUM_CHAT_MODE_PRIVATE,
|
||||
MATCH_REQUEST,
|
||||
UGCMANIFEST_REPORT_MISSING_FILE,
|
||||
UGCMANIFEST_REPORT_DONE_FILE,
|
||||
UGCMANIFEST_REPORT_DONE_BLUEPRINT,
|
||||
UGCC_REQUEST,
|
||||
WHO,
|
||||
WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE,
|
||||
ACHIEVEMENT_NOTIFY,
|
||||
GM_CLOSE_PRIVATE_CHAT_WINDOW,
|
||||
UNEXPECTED_DISCONNECT,
|
||||
PLAYER_READY,
|
||||
GET_DONATION_TOTAL,
|
||||
UPDATE_DONATION,
|
||||
PRG_CSR_COMMAND,
|
||||
HEARTBEAT_REQUEST_FROM_WORLD,
|
||||
UPDATE_FREE_TRIAL_STATUS,
|
||||
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
|
||||
CREATE_TEAM,
|
||||
};
|
||||
|
||||
#endif //!__ECHATMESSAGETYPE__H__
|
||||
76
dCommon/dEnums/eClientMessageType.h
Normal file
76
dCommon/dEnums/eClientMessageType.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef __ECLIENTMESSAGETYPE__H__
|
||||
#define __ECLIENTMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eClientMessageType : uint32_t {
|
||||
LOGIN_RESPONSE = 0,
|
||||
LOGOUT_RESPONSE,
|
||||
LOAD_STATIC_ZONE,
|
||||
CREATE_OBJECT,
|
||||
CREATE_CHARACTER,
|
||||
CREATE_CHARACTER_EXTENDED,
|
||||
CHARACTER_LIST_RESPONSE,
|
||||
CHARACTER_CREATE_RESPONSE,
|
||||
CHARACTER_RENAME_RESPONSE,
|
||||
CHAT_CONNECT_RESPONSE,
|
||||
AUTH_ACCOUNT_CREATE_RESPONSE,
|
||||
DELETE_CHARACTER_RESPONSE,
|
||||
GAME_MSG,
|
||||
CONNECT_CHAT,
|
||||
TRANSFER_TO_WORLD,
|
||||
IMPENDING_RELOAD_NOTIFY,
|
||||
MAKE_GM_RESPONSE,
|
||||
HTTP_MONITOR_INFO_RESPONSE,
|
||||
SLASH_PUSH_MAP_RESPONSE,
|
||||
SLASH_PULL_MAP_RESPONSE,
|
||||
SLASH_LOCK_MAP_RESPONSE,
|
||||
BLUEPRINT_SAVE_RESPONSE,
|
||||
BLUEPRINT_LUP_SAVE_RESPONSE,
|
||||
BLUEPRINT_LOAD_RESPONSE_ITEMID,
|
||||
BLUEPRINT_GET_ALL_DATA_RESPONSE,
|
||||
MODEL_INSTANTIATE_RESPONSE,
|
||||
DEBUG_OUTPUT,
|
||||
ADD_FRIEND_REQUEST,
|
||||
ADD_FRIEND_RESPONSE,
|
||||
REMOVE_FRIEND_RESPONSE,
|
||||
GET_FRIENDS_LIST_RESPONSE,
|
||||
UPDATE_FRIEND_NOTIFY,
|
||||
ADD_IGNORE_RESPONSE,
|
||||
REMOVE_IGNORE_RESPONSE,
|
||||
GET_IGNORE_LIST_RESPONSE,
|
||||
TEAM_INVITE,
|
||||
TEAM_INVITE_INITIAL_RESPONSE,
|
||||
GUILD_CREATE_RESPONSE,
|
||||
GUILD_GET_STATUS_RESPONSE,
|
||||
GUILD_INVITE,
|
||||
GUILD_INVITE_INITIAL_RESPONSE,
|
||||
GUILD_INVITE_FINAL_RESPONSE,
|
||||
GUILD_INVITE_CONFIRM,
|
||||
GUILD_ADD_PLAYER,
|
||||
GUILD_REMOVE_PLAYER,
|
||||
GUILD_LOGIN_LOGOUT,
|
||||
GUILD_RANK_CHANGE,
|
||||
GUILD_DATA,
|
||||
GUILD_STATUS,
|
||||
MAIL,
|
||||
DB_PROXY_RESULT,
|
||||
SHOW_ALL_RESPONSE,
|
||||
WHO_RESPONSE,
|
||||
SEND_CANNED_TEXT,
|
||||
UPDATE_CHARACTER_NAME,
|
||||
SET_NETWORK_SIMULATOR,
|
||||
INVALID_CHAT_MESSAGE,
|
||||
MINIMUM_CHAT_MODE_RESPONSE,
|
||||
MINIMUM_CHAT_MODE_RESPONSE_PRIVATE,
|
||||
CHAT_MODERATION_STRING,
|
||||
UGC_MANIFEST_RESPONSE,
|
||||
IN_LOGIN_QUEUE,
|
||||
SERVER_STATES,
|
||||
GM_CLOSE_TARGET_CHAT_WINDOW,
|
||||
GENERAL_TEXT_FOR_LOCALIZATION,
|
||||
UPDATE_FREE_TRIAL_STATUS,
|
||||
UGC_DOWNLOAD_FAILED = 120
|
||||
};
|
||||
|
||||
#endif //!__ECLIENTMESSAGETYPE__H__
|
||||
1614
dCommon/dEnums/eGameMessageType.h
Normal file
1614
dCommon/dEnums/eGameMessageType.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,6 @@
|
||||
#define __EINVENTORYTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
static const uint8_t NUMBER_OF_INVENTORIES = 17;
|
||||
/**
|
||||
* Represents the different types of inventories an entity may have
|
||||
@@ -59,10 +56,4 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<eInventoryType> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 16;
|
||||
};
|
||||
|
||||
#endif //!__EINVENTORYTYPE__H__
|
||||
|
||||
36
dCommon/dEnums/eMasterMessageType.h
Normal file
36
dCommon/dEnums/eMasterMessageType.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef __EMASTERMESSAGETYPE__H__
|
||||
#define __EMASTERMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eMasterMessageType : uint32_t {
|
||||
REQUEST_PERSISTENT_ID = 1,
|
||||
REQUEST_PERSISTENT_ID_RESPONSE,
|
||||
REQUEST_ZONE_TRANSFER,
|
||||
REQUEST_ZONE_TRANSFER_RESPONSE,
|
||||
SERVER_INFO,
|
||||
REQUEST_SESSION_KEY,
|
||||
SET_SESSION_KEY,
|
||||
SESSION_KEY_RESPONSE,
|
||||
PLAYER_ADDED,
|
||||
PLAYER_REMOVED,
|
||||
|
||||
CREATE_PRIVATE_ZONE,
|
||||
REQUEST_PRIVATE_ZONE,
|
||||
|
||||
WORLD_READY,
|
||||
PREP_ZONE,
|
||||
|
||||
SHUTDOWN,
|
||||
SHUTDOWN_RESPONSE,
|
||||
SHUTDOWN_IMMEDIATE,
|
||||
|
||||
SHUTDOWN_UNIVERSE,
|
||||
|
||||
AFFIRM_TRANSFER_REQUEST,
|
||||
AFFIRM_TRANSFER_RESPONSE,
|
||||
|
||||
NEW_SESSION_ALERT
|
||||
};
|
||||
|
||||
#endif //!__EMASTERMESSAGETYPE__H__
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef EPROPERTYSORTTYPE_H
|
||||
#define EPROPERTYSORTTYPE_H
|
||||
|
||||
enum ePropertySortType : int32_t {
|
||||
SORT_TYPE_FRIENDS = 0,
|
||||
SORT_TYPE_REPUTATION = 1,
|
||||
SORT_TYPE_RECENT = 3,
|
||||
SORT_TYPE_FEATURED = 5
|
||||
};
|
||||
|
||||
#endif //!EPROPERTYSORTTYPE_H
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
|
||||
SUCCESS,
|
||||
FAIL_GENERIC,
|
||||
FAIL_INV_FULL,
|
||||
FAIL_ITEM_NOT_FOUND,
|
||||
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
|
||||
FAIL_NOT_NEAR_BANK,
|
||||
FAIL_CANT_SWAP_ITEMS,
|
||||
FAIL_SOURCE_TYPE,
|
||||
FAIL_WRONG_DEST_TYPE,
|
||||
FAIL_SWAP_DEST_TYPE,
|
||||
FAIL_CANT_MOVE_THINKING_HAT,
|
||||
FAIL_DISMOUNT_BEFORE_MOVING
|
||||
};
|
||||
|
||||
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
|
||||
12
dCommon/dEnums/eServerMessageType.h
Normal file
12
dCommon/dEnums/eServerMessageType.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef __ESERVERMESSAGETYPE__H__
|
||||
#define __ESERVERMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
//! The Internal Server Packet Identifiers
|
||||
enum class eServerMessageType : uint32_t {
|
||||
VERSION_CONFIRM = 0,
|
||||
DISCONNECT_NOTIFY,
|
||||
GENERAL_NOTIFY
|
||||
};
|
||||
|
||||
#endif //!__ESERVERMESSAGETYPE__H__
|
||||
51
dCommon/dEnums/eWorldMessageType.h
Normal file
51
dCommon/dEnums/eWorldMessageType.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef __EWORLDMESSAGETYPE__H__
|
||||
#define __EWORLDMESSAGETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
enum class eWorldMessageType : uint32_t {
|
||||
VALIDATION = 1, // Session info
|
||||
CHARACTER_LIST_REQUEST,
|
||||
CHARACTER_CREATE_REQUEST,
|
||||
LOGIN_REQUEST, // Character selected
|
||||
GAME_MSG,
|
||||
CHARACTER_DELETE_REQUEST,
|
||||
CHARACTER_RENAME_REQUEST,
|
||||
HAPPY_FLOWER_MODE_NOTIFY,
|
||||
SLASH_RELOAD_MAP, // Reload map cmp
|
||||
SLASH_PUSH_MAP_REQUEST, // Push map req cmd
|
||||
SLASH_PUSH_MAP, // Push map cmd
|
||||
SLASH_PULL_MAP, // Pull map cmd
|
||||
LOCK_MAP_REQUEST,
|
||||
GENERAL_CHAT_MESSAGE, // General chat message
|
||||
HTTP_MONITOR_INFO_REQUEST,
|
||||
SLASH_DEBUG_SCRIPTS, // Debug scripts cmd
|
||||
MODELS_CLEAR,
|
||||
EXHIBIT_INSERT_MODEL,
|
||||
LEVEL_LOAD_COMPLETE, // Character data request
|
||||
TMP_GUILD_CREATE,
|
||||
ROUTE_PACKET, // Social?
|
||||
POSITION_UPDATE,
|
||||
MAIL,
|
||||
WORD_CHECK, // AllowList word check
|
||||
STRING_CHECK, // AllowList string check
|
||||
GET_PLAYERS_IN_ZONE,
|
||||
REQUEST_UGC_MANIFEST_INFO,
|
||||
BLUEPRINT_GET_ALL_DATA_REQUEST,
|
||||
CANCEL_MAP_QUEUE,
|
||||
HANDLE_FUNNESS,
|
||||
FAKE_PRG_CSR_MESSAGE,
|
||||
REQUEST_FREE_TRIAL_REFRESH,
|
||||
GM_SET_FREE_TRIAL_STATUS,
|
||||
UI_HELP_TOP_5 = 91
|
||||
};
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<eWorldMessageType> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 91;
|
||||
};
|
||||
|
||||
#endif //!__EWORLDMESSAGETYPE__H__
|
||||
@@ -8,15 +8,9 @@ foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES})
|
||||
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(TestSQL)
|
||||
|
||||
foreach(file ${DDATABSE_DATABSES_TEST_SQL_SOURCES})
|
||||
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "TestSQL/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dDatabaseGame STATIC ${DDATABASE_GAMEDATABASE_SOURCES})
|
||||
target_include_directories(dDatabaseGame PUBLIC "."
|
||||
"ITables" PRIVATE "MySQL" "TestSQL"
|
||||
"ITables" PRIVATE "MySQL"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon"
|
||||
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
|
||||
)
|
||||
|
||||
@@ -38,8 +38,3 @@ void Database::Destroy(std::string source) {
|
||||
LOG("Trying to destroy database when it's not connected!");
|
||||
}
|
||||
}
|
||||
|
||||
void Database::_setDatabase(GameDatabase* const db) {
|
||||
if (database) delete database;
|
||||
database = db;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,4 @@ namespace Database {
|
||||
void Connect();
|
||||
GameDatabase* Get();
|
||||
void Destroy(std::string source = "");
|
||||
|
||||
// Used for assigning a test database as the handler for database logic.
|
||||
// Do not use in production code.
|
||||
void _setDatabase(GameDatabase* const db);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "IActivityLog.h"
|
||||
#include "IIgnoreList.h"
|
||||
#include "IAccountsRewardCodes.h"
|
||||
#include "IBehaviors.h"
|
||||
|
||||
namespace sql {
|
||||
class Statement;
|
||||
@@ -41,8 +40,7 @@ class GameDatabase :
|
||||
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
|
||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||
public IBehaviors {
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
|
||||
public:
|
||||
virtual ~GameDatabase() = default;
|
||||
// TODO: These should be made private.
|
||||
|
||||
@@ -33,9 +33,6 @@ public:
|
||||
|
||||
// Add a new account to the database.
|
||||
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;
|
||||
|
||||
// Update the GameMaster level of an account.
|
||||
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IACCOUNTS__H__
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef IBEHAVIORS_H
|
||||
#define IBEHAVIORS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
class IBehaviors {
|
||||
public:
|
||||
struct Info {
|
||||
int32_t behaviorId{};
|
||||
uint32_t characterId{};
|
||||
std::string behaviorInfo;
|
||||
};
|
||||
|
||||
// This Add also takes care of updating if it exists.
|
||||
virtual void AddBehavior(const Info& info) = 0;
|
||||
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
|
||||
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
|
||||
};
|
||||
|
||||
#endif //!IBEHAVIORS_H
|
||||
@@ -4,8 +4,6 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
enum ePropertySortType : int32_t;
|
||||
|
||||
class IProperty {
|
||||
public:
|
||||
struct Info {
|
||||
@@ -20,33 +18,11 @@ public:
|
||||
uint32_t lastUpdatedTime{};
|
||||
uint32_t claimedTime{};
|
||||
uint32_t reputation{};
|
||||
float performanceCost{};
|
||||
};
|
||||
|
||||
struct PropertyLookup {
|
||||
uint32_t mapId{};
|
||||
std::string searchString;
|
||||
ePropertySortType sortChoice{};
|
||||
uint32_t playerId{};
|
||||
uint32_t numResults{};
|
||||
uint32_t startIndex{};
|
||||
uint32_t playerSort{};
|
||||
};
|
||||
|
||||
struct PropertyEntranceResult {
|
||||
int32_t totalEntriesMatchingQuery{};
|
||||
// The entries that match the query. This should only contain up to 12 entries.
|
||||
std::vector<IProperty::Info> entries;
|
||||
};
|
||||
|
||||
// Get the property info for the given property id.
|
||||
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0;
|
||||
|
||||
// Get the properties for the given property lookup params.
|
||||
// This is expected to return a result set of up to 12 properties
|
||||
// so as not to transfer too much data at once.
|
||||
virtual std::optional<IProperty::PropertyEntranceResult> GetProperties(const PropertyLookup& params) = 0;
|
||||
|
||||
// Update the property moderation info for the given property id.
|
||||
virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef __IPROPERTIESCONTENTS__H__
|
||||
#define __IPROPERTIESCONTENTS__H__
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
@@ -17,7 +16,6 @@ public:
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
uint32_t ugcId{};
|
||||
std::array<int32_t, 5> behaviors{};
|
||||
};
|
||||
|
||||
// Inserts a new UGC model into the database.
|
||||
@@ -34,7 +32,7 @@ public:
|
||||
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
|
||||
|
||||
// Update the model position and rotation for the given property id.
|
||||
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
|
||||
virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0;
|
||||
|
||||
// Remove the model for the given property id.
|
||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "Logger.h"
|
||||
#include "dPlatforms.h"
|
||||
|
||||
namespace {
|
||||
std::string databaseName;
|
||||
@@ -40,13 +39,14 @@ void MySQLDatabase::Connect() {
|
||||
properties["autoReconnect"] = "true";
|
||||
|
||||
databaseName = Game::config->GetValue("mysql_database").c_str();
|
||||
|
||||
// `connect(const Properties& props)` segfaults in windows debug, but
|
||||
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
|
||||
#if defined(DARKFLAME_PLATFORM_WIN32) && defined(_DEBUG)
|
||||
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str());
|
||||
#else
|
||||
if (properties.find("localSocket") != properties.end() || properties.find("pipe") != properties.end()) {
|
||||
con = driver->connect(properties);
|
||||
#endif
|
||||
} else {
|
||||
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str());
|
||||
}
|
||||
con->setSchema(databaseName.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
@@ -108,11 +108,6 @@ public:
|
||||
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||
void AddBehavior(const IBehaviors::Info& info) override;
|
||||
std::string GetBehavior(const int32_t behaviorId) override;
|
||||
void RemoveBehavior(const int32_t characterId) override;
|
||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
|
||||
private:
|
||||
|
||||
// Generic query functions that can be used for any query.
|
||||
|
||||
@@ -35,7 +35,3 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
|
||||
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
|
||||
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
|
||||
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "IBehaviors.h"
|
||||
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?",
|
||||
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
|
||||
);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
||||
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||
}
|
||||
|
||||
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
|
||||
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
|
||||
return result->next() ? result->getString("behavior_info").c_str() : "";
|
||||
}
|
||||
@@ -2,7 +2,6 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Accounts.cpp"
|
||||
"AccountsRewardCodes.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"Behaviors.cpp"
|
||||
"BugReports.cpp"
|
||||
"CharInfo.cpp"
|
||||
"CharXml.cpp"
|
||||
|
||||
@@ -1,140 +1,8 @@
|
||||
#include "MySQLDatabase.h"
|
||||
#include "ePropertySortType.h"
|
||||
|
||||
std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
|
||||
std::optional<IProperty::PropertyEntranceResult> result;
|
||||
std::string query;
|
||||
std::unique_ptr<sql::ResultSet> properties;
|
||||
|
||||
if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) {
|
||||
query = R"QUERY(
|
||||
FROM properties as p
|
||||
JOIN charinfo as ci
|
||||
ON ci.prop_clone_id = p.clone_id
|
||||
where p.zone_id = ?
|
||||
AND (
|
||||
p.description LIKE ?
|
||||
OR p.name LIKE ?
|
||||
OR ci.name LIKE ?
|
||||
)
|
||||
AND p.privacy_option >= ?
|
||||
AND p.owner_id IN (
|
||||
SELECT fr.requested_player AS player FROM (
|
||||
SELECT CASE
|
||||
WHEN player_id = ? THEN friend_id
|
||||
WHEN friend_id = ? THEN player_id
|
||||
END AS requested_player FROM friends
|
||||
) AS fr
|
||||
JOIN charinfo AS ci ON ci.id = fr.requested_player
|
||||
WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?
|
||||
) ORDER BY ci.name ASC
|
||||
)QUERY";
|
||||
const auto completeQuery = "SELECT p.* " + query + " LIMIT ? OFFSET ?;";
|
||||
properties = ExecuteSelect(
|
||||
completeQuery,
|
||||
params.mapId,
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
params.playerSort,
|
||||
params.playerId,
|
||||
params.playerId,
|
||||
params.playerId,
|
||||
params.numResults,
|
||||
params.startIndex
|
||||
);
|
||||
const auto countQuery = "SELECT COUNT(*) as count" + query + ";";
|
||||
auto count = ExecuteSelect(
|
||||
countQuery,
|
||||
params.mapId,
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
params.playerSort,
|
||||
params.playerId,
|
||||
params.playerId,
|
||||
params.playerId
|
||||
);
|
||||
if (count->next()) {
|
||||
result->totalEntriesMatchingQuery = count->getUInt("count");
|
||||
}
|
||||
} else {
|
||||
if (params.sortChoice == SORT_TYPE_REPUTATION) {
|
||||
query = R"QUERY(
|
||||
FROM properties as p
|
||||
JOIN charinfo as ci
|
||||
ON ci.prop_clone_id = p.clone_id
|
||||
where p.zone_id = ?
|
||||
AND (
|
||||
p.description LIKE ?
|
||||
OR p.name LIKE ?
|
||||
OR ci.name LIKE ?
|
||||
)
|
||||
AND p.privacy_option >= ?
|
||||
ORDER BY p.reputation DESC, p.last_updated DESC
|
||||
)QUERY";
|
||||
} else {
|
||||
query = R"QUERY(
|
||||
FROM properties as p
|
||||
JOIN charinfo as ci
|
||||
ON ci.prop_clone_id = p.clone_id
|
||||
where p.zone_id = ?
|
||||
AND (
|
||||
p.description LIKE ?
|
||||
OR p.name LIKE ?
|
||||
OR ci.name LIKE ?
|
||||
)
|
||||
AND p.privacy_option >= ?
|
||||
ORDER BY p.last_updated DESC
|
||||
)QUERY";
|
||||
}
|
||||
const auto completeQuery = "SELECT p.* " + query + " LIMIT ? OFFSET ?;";
|
||||
properties = ExecuteSelect(
|
||||
completeQuery,
|
||||
params.mapId,
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
params.playerSort,
|
||||
params.numResults,
|
||||
params.startIndex
|
||||
);
|
||||
const auto countQuery = "SELECT COUNT(*) as count" + query + ";";
|
||||
auto count = ExecuteSelect(
|
||||
countQuery,
|
||||
params.mapId,
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
"%" + params.searchString + "%",
|
||||
params.playerSort
|
||||
);
|
||||
if (count->next()) {
|
||||
result->totalEntriesMatchingQuery = count->getUInt("count");
|
||||
}
|
||||
}
|
||||
|
||||
while (properties->next()) {
|
||||
auto& entry = result->entries.emplace_back();
|
||||
entry.id = properties->getUInt64("id");
|
||||
entry.ownerId = properties->getUInt64("owner_id");
|
||||
entry.cloneId = properties->getUInt64("clone_id");
|
||||
entry.name = properties->getString("name").c_str();
|
||||
entry.description = properties->getString("description").c_str();
|
||||
entry.privacyOption = properties->getInt("privacy_option");
|
||||
entry.rejectionReason = properties->getString("rejection_reason").c_str();
|
||||
entry.lastUpdatedTime = properties->getUInt("last_updated");
|
||||
entry.claimedTime = properties->getUInt("time_claimed");
|
||||
entry.reputation = properties->getUInt("reputation");
|
||||
entry.modApproved = properties->getUInt("mod_approved");
|
||||
entry.performanceCost = properties->getFloat("performance_cost");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) {
|
||||
auto propertyEntry = ExecuteSelect(
|
||||
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost "
|
||||
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved "
|
||||
"FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId);
|
||||
|
||||
if (!propertyEntry->next()) {
|
||||
@@ -153,7 +21,6 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map
|
||||
toReturn.claimedTime = propertyEntry->getUInt("time_claimed");
|
||||
toReturn.reputation = propertyEntry->getUInt("reputation");
|
||||
toReturn.modApproved = propertyEntry->getUInt("mod_approved");
|
||||
toReturn.performanceCost = propertyEntry->getFloat("performance_cost");
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
|
||||
auto result = ExecuteSelect(
|
||||
"SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, "
|
||||
"behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 "
|
||||
"FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||
auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId);
|
||||
|
||||
std::vector<IPropertyContents::Model> toReturn;
|
||||
toReturn.reserve(result->rowsCount());
|
||||
@@ -20,12 +17,6 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
|
||||
model.rotation.y = result->getFloat("ry");
|
||||
model.rotation.z = result->getFloat("rz");
|
||||
model.ugcId = result->getUInt64("ugc_id");
|
||||
model.behaviors[0] = result->getInt("behavior_1");
|
||||
model.behaviors[1] = result->getInt("behavior_2");
|
||||
model.behaviors[2] = result->getInt("behavior_3");
|
||||
model.behaviors[3] = result->getInt("behavior_4");
|
||||
model.behaviors[4] = result->getInt("behavior_5");
|
||||
|
||||
toReturn.push_back(std::move(model));
|
||||
}
|
||||
return toReturn;
|
||||
@@ -41,23 +32,21 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
|
||||
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
|
||||
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
|
||||
name, "", // Model description. TODO implement this.
|
||||
model.behaviors[0], // behavior 1
|
||||
model.behaviors[1], // behavior 2
|
||||
model.behaviors[2], // behavior 3
|
||||
model.behaviors[3], // behavior 4
|
||||
model.behaviors[4] // behavior 5
|
||||
0, // behavior 1. TODO implement this.
|
||||
0, // behavior 2. TODO implement this.
|
||||
0, // behavior 3. TODO implement this.
|
||||
0, // behavior 4. TODO implement this.
|
||||
0 // behavior 5. TODO implement this.
|
||||
);
|
||||
} catch (sql::SQLException& e) {
|
||||
LOG("Error inserting new property model: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
|
||||
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
|
||||
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
|
||||
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;",
|
||||
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
SET(DDATABSE_DATABSES_TEST_SQL_SOURCES
|
||||
"TestSQLDatabase.cpp"
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,306 +0,0 @@
|
||||
#include "TestSQLDatabase.h"
|
||||
|
||||
void TestSQLDatabase::Connect() {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::Destroy(std::string source) {
|
||||
|
||||
}
|
||||
|
||||
sql::PreparedStatement* TestSQLDatabase::CreatePreppedStmt(const std::string& query) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TestSQLDatabase::Commit() {
|
||||
|
||||
}
|
||||
|
||||
bool TestSQLDatabase::GetAutoCommit() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetAutoCommit(bool value) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::ExecuteCustomQuery(const std::string_view query) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<IServers::MasterInfo> TestSQLDatabase::GetMasterInfo() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> TestSQLDatabase::GetApprovedCharacterNames() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<FriendData> TestSQLDatabase::GetFriendsList(uint32_t charID) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<IFriends::BestFriendStatus> TestSQLDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<IUgc::Model> TestSQLDatabase::GetAllUgcModels() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::CreateMigrationHistoryTable() {
|
||||
|
||||
}
|
||||
|
||||
bool TestSQLDatabase::IsMigrationRun(const std::string_view str) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertMigration(const std::string_view str) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<ICharInfo::Info> TestSQLDatabase::GetCharacterInfo(const uint32_t charId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<ICharInfo::Info> TestSQLDatabase::GetCharacterInfo(const std::string_view charId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string TestSQLDatabase::GetCharacterXml(const uint32_t accountId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<IAccounts::Info> TestSQLDatabase::GetAccountInfo(const std::string_view username) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewCharacter(const ICharInfo::Info info) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<uint32_t> TestSQLDatabase::GetAccountCharacterIds(uint32_t accountId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::DeleteCharacter(const uint32_t characterId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<IPetNames::Info> TestSQLDatabase::GetPetNameInfo(const LWOOBJID& petId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<IProperty::Info> TestSQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<IPropertyContents::Model> TestSQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::RemoveUnreferencedUgcModels() {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::RemoveModel(const LWOOBJID& modelId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewBugReport(const IBugReports::Info& info) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& info) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<IMail::MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<IMail::MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t TestSQLDatabase::GetUnreadMailCount(const uint32_t characterId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::MarkMailRead(const uint64_t mailId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::DeleteMail(const uint64_t mailId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::ClaimMailItem(const uint64_t mailId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateAccountBan(const uint32_t accountId, const bool banned) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<uint32_t> TestSQLDatabase::GetCurrentPersistentId() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertDefaultPersistentId() {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdatePersistentId(const uint32_t id) {
|
||||
|
||||
}
|
||||
|
||||
std::optional<uint32_t> TestSQLDatabase::GetDonationTotal(const uint32_t activityId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<bool> TestSQLDatabase::IsPlaykeyActive(const int32_t playkeyId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<IUgc::Model> TestSQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<IIgnoreList::Info> TestSQLDatabase::GetIgnoreList(const uint32_t playerId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<uint32_t> TestSQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::AddBehavior(const IBehaviors::Info& info) {
|
||||
|
||||
}
|
||||
|
||||
std::string TestSQLDatabase::GetBehavior(const int32_t behaviorId) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void TestSQLDatabase::RemoveBehavior(const int32_t behaviorId) {
|
||||
|
||||
}
|
||||
|
||||
void TestSQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
#ifndef TESTSQLDATABASE_H
|
||||
#define TESTSQLDATABASE_H
|
||||
|
||||
#include "GameDatabase.h"
|
||||
|
||||
class TestSQLDatabase : public GameDatabase {
|
||||
void Connect() override;
|
||||
void Destroy(std::string source = "") override;
|
||||
|
||||
sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override;
|
||||
void Commit() override;
|
||||
bool GetAutoCommit() override;
|
||||
void SetAutoCommit(bool value) override;
|
||||
void ExecuteCustomQuery(const std::string_view query) override;
|
||||
|
||||
// Overloaded queries
|
||||
std::optional<IServers::MasterInfo> GetMasterInfo() override;
|
||||
|
||||
std::vector<std::string> GetApprovedCharacterNames() override;
|
||||
|
||||
std::vector<FriendData> GetFriendsList(uint32_t charID) override;
|
||||
|
||||
std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override;
|
||||
void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override;
|
||||
void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
||||
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
|
||||
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
|
||||
void DeleteUgcModelData(const LWOOBJID& modelId) override;
|
||||
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
|
||||
std::vector<IUgc::Model> GetAllUgcModels() override;
|
||||
void CreateMigrationHistoryTable() override;
|
||||
bool IsMigrationRun(const std::string_view str) override;
|
||||
void InsertMigration(const std::string_view str) override;
|
||||
std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) override;
|
||||
std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view charId) override;
|
||||
std::string GetCharacterXml(const uint32_t accountId) override;
|
||||
void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override;
|
||||
std::optional<IAccounts::Info> GetAccountInfo(const std::string_view username) override;
|
||||
void InsertNewCharacter(const ICharInfo::Info info) override;
|
||||
void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override;
|
||||
std::vector<uint32_t> GetAccountCharacterIds(uint32_t accountId) override;
|
||||
void DeleteCharacter(const uint32_t characterId) override;
|
||||
void SetCharacterName(const uint32_t characterId, const std::string_view name) override;
|
||||
void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override;
|
||||
void UpdateLastLoggedInCharacter(const uint32_t characterId) override;
|
||||
void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override;
|
||||
std::optional<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override;
|
||||
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
|
||||
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
|
||||
void UpdatePropertyDetails(const IProperty::Info& info) override;
|
||||
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
|
||||
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
|
||||
void RemoveUnreferencedUgcModels() override;
|
||||
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
|
||||
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
|
||||
void RemoveModel(const LWOOBJID& modelId) override;
|
||||
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
|
||||
void InsertNewBugReport(const IBugReports::Info& info) override;
|
||||
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
|
||||
void InsertNewMail(const IMail::MailInfo& mail) override;
|
||||
void InsertNewUgcModel(
|
||||
std::istringstream& sd0Data,
|
||||
const uint32_t blueprintId,
|
||||
const uint32_t accountId,
|
||||
const uint32_t characterId) override;
|
||||
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
|
||||
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
|
||||
void MarkMailRead(const uint64_t mailId) override;
|
||||
void DeleteMail(const uint64_t mailId) override;
|
||||
void ClaimMailItem(const uint64_t mailId) override;
|
||||
void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override;
|
||||
void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override;
|
||||
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
|
||||
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
|
||||
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
|
||||
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
|
||||
std::optional<uint32_t> GetCurrentPersistentId() override;
|
||||
void InsertDefaultPersistentId() override;
|
||||
void UpdatePersistentId(const uint32_t id) override;
|
||||
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
|
||||
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
|
||||
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
|
||||
void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
|
||||
void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
|
||||
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
|
||||
void AddBehavior(const IBehaviors::Info& info) override;
|
||||
std::string GetBehavior(const int32_t behaviorId) override;
|
||||
void RemoveBehavior(const int32_t behaviorId) override;
|
||||
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
|
||||
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
|
||||
};
|
||||
|
||||
#endif //!TESTSQLDATABASE_H
|
||||
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,7 +6,24 @@ 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
|
||||
|
||||
@@ -27,8 +27,6 @@ Character::Character(uint32_t id, User* parentUser) {
|
||||
m_ID = id;
|
||||
m_ParentUser = parentUser;
|
||||
m_OurEntity = nullptr;
|
||||
m_GMLevel = eGameMasterLevel::CIVILIAN;
|
||||
m_PermissionMap = static_cast<ePermissionMap>(0);
|
||||
}
|
||||
|
||||
Character::~Character() {
|
||||
@@ -40,8 +38,8 @@ void Character::UpdateInfoFromDatabase() {
|
||||
auto charInfo = Database::Get()->GetCharacterInfo(m_ID);
|
||||
|
||||
if (charInfo) {
|
||||
m_Name = charInfo->name;
|
||||
m_UnapprovedName = charInfo->pendingName;
|
||||
m_Name = charInfo->name;
|
||||
m_UnapprovedName = charInfo->pendingName;
|
||||
m_NameRejected = charInfo->needsRename;
|
||||
m_PropertyCloneID = charInfo->cloneId;
|
||||
m_PermissionMap = charInfo->permissionMap;
|
||||
@@ -76,7 +74,7 @@ void Character::DoQuickXMLDataParse() {
|
||||
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 (%i) (%s) (%s)!", m_Doc.ErrorID(), m_Doc.ErrorIDToName(m_Doc.ErrorID()), m_Doc.ErrorStr());
|
||||
LOG("Failed to load xmlData!");
|
||||
//Server::rakServer->CloseConnection(m_ParentUser->GetSystemAddress(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ public:
|
||||
|
||||
const std::string& GetXMLData() const { return m_XMLData; }
|
||||
const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
|
||||
void _setXmlDoc(tinyxml2::XMLDocument& doc) { doc.DeepCopy(&m_Doc); }
|
||||
|
||||
/**
|
||||
* Out of abundance of safety and clarity of what this saves, this is its own function.
|
||||
@@ -460,31 +459,27 @@ public:
|
||||
|
||||
User* GetParentUser() const { return m_ParentUser; }
|
||||
|
||||
void _doQuickXMLDataParse() { DoQuickXMLDataParse(); }
|
||||
|
||||
void _setXmlData(const std::string& xmlData) { m_XMLData = xmlData; }
|
||||
|
||||
private:
|
||||
void UpdateInfoFromDatabase();
|
||||
/**
|
||||
* The ID of this character. First 32 bits of the ObjectID.
|
||||
*/
|
||||
uint32_t m_ID{};
|
||||
uint32_t m_ID;
|
||||
|
||||
/**
|
||||
* The 64-bit unique ID used in the game.
|
||||
*/
|
||||
LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
|
||||
LWOOBJID m_ObjectID;
|
||||
|
||||
/**
|
||||
* The user that owns this character.
|
||||
*/
|
||||
User* m_ParentUser{};
|
||||
User* m_ParentUser;
|
||||
|
||||
/**
|
||||
* If the character is in game, this is the entity that it represents, else nullptr.
|
||||
*/
|
||||
Entity* m_OurEntity{};
|
||||
Entity* m_OurEntity;
|
||||
|
||||
/**
|
||||
* 0-9, the Game Master level of this character.
|
||||
@@ -511,17 +506,17 @@ private:
|
||||
/**
|
||||
* Whether the custom name of this character is rejected
|
||||
*/
|
||||
bool m_NameRejected{};
|
||||
bool m_NameRejected;
|
||||
|
||||
/**
|
||||
* The current amount of coins of this character
|
||||
*/
|
||||
int64_t m_Coins{};
|
||||
int64_t m_Coins;
|
||||
|
||||
/**
|
||||
* Whether the character is building
|
||||
*/
|
||||
bool m_BuildMode{};
|
||||
bool m_BuildMode;
|
||||
|
||||
/**
|
||||
* The items equipped by the character on world load
|
||||
@@ -588,7 +583,7 @@ private:
|
||||
/**
|
||||
* The ID of the properties of this character
|
||||
*/
|
||||
uint32_t m_PropertyCloneID{};
|
||||
uint32_t m_PropertyCloneID;
|
||||
|
||||
/**
|
||||
* The XML data for this character, stored as string
|
||||
@@ -618,7 +613,7 @@ private:
|
||||
/**
|
||||
* The last time this character logged in
|
||||
*/
|
||||
uint64_t m_LastLogin{};
|
||||
uint64_t m_LastLogin;
|
||||
|
||||
/**
|
||||
* The gameplay flags this character has (not just true values)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "eTriggerEventType.h"
|
||||
#include "eObjectBits.h"
|
||||
#include "PositionUpdate.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "eChatMessageType.h"
|
||||
#include "PlayerManager.h"
|
||||
|
||||
//Component includes:
|
||||
@@ -97,6 +97,7 @@
|
||||
#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;
|
||||
@@ -227,7 +228,7 @@ void Entity::Initialize() {
|
||||
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
AddComponent<ModelComponent>();
|
||||
|
||||
AddComponent<RenderComponent>();
|
||||
|
||||
@@ -651,7 +652,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
|
||||
AddComponent<ModelComponent>()->LoadBehaviors();
|
||||
AddComponent<ModelComponent>();
|
||||
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
|
||||
auto* destroyableComponent = AddComponent<DestroyableComponent>();
|
||||
destroyableComponent->SetHealth(1);
|
||||
@@ -883,7 +884,7 @@ void Entity::SetGMLevel(eGameMasterLevel value) {
|
||||
// Update the chat server of our GM Level
|
||||
{
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GMLEVEL_UPDATE);
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GMLEVEL_UPDATE);
|
||||
bitStream.Write(m_ObjectID);
|
||||
bitStream.Write(m_GMLevel);
|
||||
|
||||
@@ -1351,6 +1352,11 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) {
|
||||
callback(other);
|
||||
}
|
||||
|
||||
SwitchComponent* switchComp = GetComponent<SwitchComponent>();
|
||||
if (switchComp) {
|
||||
switchComp->EntityEnter(other);
|
||||
}
|
||||
|
||||
TriggerEvent(eTriggerEventType::ENTER, other);
|
||||
|
||||
// POI system
|
||||
@@ -1775,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;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
|
||||
void SetOwnerOverride(LWOOBJID value);
|
||||
|
||||
void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; }
|
||||
void SetPlayerReadyForUpdates();
|
||||
|
||||
void SetObservers(int8_t value);
|
||||
|
||||
@@ -304,6 +304,8 @@ public:
|
||||
* @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);
|
||||
}
|
||||
@@ -505,6 +514,7 @@ void EntityManager::UpdateGhosting(Entity* player) {
|
||||
|
||||
if (collectionId != 0) {
|
||||
collectionId = static_cast<uint32_t>(collectionId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||
|
||||
if (missionComponent->HasCollectible(collectionId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Observable.h"
|
||||
|
||||
class Entity;
|
||||
class EntityInfo;
|
||||
@@ -72,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;
|
||||
};
|
||||
|
||||
}
|
||||
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);
|
||||
|
||||
}
|
||||
215
dGame/NejlikaData.cpp
Normal file
215
dGame/NejlikaData.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
#include "NejlikaData.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "json.hpp"
|
||||
#include "Logger.h"
|
||||
#include "Lookup.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::unordered_map<nejlika::ModifierNameType, std::vector<nejlika::ModifierNameTemplate>> modifierNameTemplates;
|
||||
|
||||
std::unordered_map<LWOOBJID, nejlika::AdditionalItemData> additionalItemData;
|
||||
|
||||
std::unordered_map<LWOOBJID, nejlika::AdditionalEntityData> additionalEntityData;
|
||||
|
||||
std::unordered_map<LOT, nejlika::EntityTemplate> entityTemplates;
|
||||
|
||||
std::unordered_map<LOT, nejlika::UpgradeTemplate> upgradeTemplates;
|
||||
|
||||
nejlika::Lookup lookup;
|
||||
|
||||
}
|
||||
|
||||
const nejlika::Lookup& nejlika::NejlikaData::GetLookup()
|
||||
{
|
||||
return lookup;
|
||||
}
|
||||
|
||||
const std::unordered_map<ModifierNameType,std::vector<ModifierNameTemplate>>& nejlika::NejlikaData::GetModifierNameTemplates()
|
||||
{
|
||||
return modifierNameTemplates;
|
||||
}
|
||||
|
||||
const std::vector<nejlika::ModifierNameTemplate>& nejlika::NejlikaData::GetModifierNameTemplates(ModifierNameType type)
|
||||
{
|
||||
const auto it = modifierNameTemplates.find(type);
|
||||
|
||||
if (it != modifierNameTemplates.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static const std::vector<nejlika::ModifierNameTemplate> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
const std::optional<AdditionalItemData*> nejlika::NejlikaData::GetAdditionalItemData(LWOOBJID id) {
|
||||
const auto& it = additionalItemData.find(id);
|
||||
|
||||
if (it != additionalItemData.end()) {
|
||||
return std::optional<AdditionalItemData*>(&it->second);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::optional<AdditionalEntityData*> nejlika::NejlikaData::GetAdditionalEntityData(LWOOBJID id) {
|
||||
const auto& it = additionalEntityData.find(id);
|
||||
|
||||
if (it != additionalEntityData.end()) {
|
||||
return std::optional<AdditionalEntityData*>(&it->second);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::optional<EntityTemplate*> nejlika::NejlikaData::GetEntityTemplate(LOT lot) {
|
||||
const auto& it = entityTemplates.find(lot);
|
||||
|
||||
if (it != entityTemplates.end()) {
|
||||
return std::optional<EntityTemplate*>(&it->second);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::optional<UpgradeTemplate*> nejlika::NejlikaData::GetUpgradeTemplate(LOT lot) {
|
||||
const auto& it = upgradeTemplates.find(lot);
|
||||
|
||||
if (it != upgradeTemplates.end()) {
|
||||
return std::optional<UpgradeTemplate*>(&it->second);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void nejlika::NejlikaData::SetAdditionalItemData(LWOOBJID id, AdditionalItemData data) {
|
||||
additionalItemData[id] = data;
|
||||
}
|
||||
|
||||
void nejlika::NejlikaData::SetAdditionalEntityData(LWOOBJID id, AdditionalEntityData data) {
|
||||
additionalEntityData[id] = data;
|
||||
}
|
||||
|
||||
void nejlika::NejlikaData::UnsetAdditionalItemData(LWOOBJID id) {
|
||||
additionalItemData.erase(id);
|
||||
}
|
||||
|
||||
void nejlika::NejlikaData::UnsetAdditionalEntityData(LWOOBJID id) {
|
||||
additionalEntityData.erase(id);
|
||||
}
|
||||
|
||||
void nejlika::NejlikaData::LoadNejlikaData()
|
||||
{
|
||||
const auto& lookupFile = Game::config->GetValue("lookup");
|
||||
|
||||
if (!lookupFile.empty())
|
||||
{
|
||||
lookup = Lookup(lookupFile);
|
||||
}
|
||||
|
||||
modifierNameTemplates.clear();
|
||||
|
||||
// Load data from json file
|
||||
const auto& directory_name = Game::config->GetValue("nejlika");
|
||||
|
||||
if (directory_name.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through all files in the directory
|
||||
for (const auto& entry : std::filesystem::directory_iterator(directory_name))
|
||||
{
|
||||
if (!entry.is_regular_file())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// It has to end on .mod.json
|
||||
const auto& path = entry.path().string();
|
||||
|
||||
if (path.size() < 9 || path.substr(path.size() - 9) != ".mod.json")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LoadNejlikaDataFile(entry.path().string());
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::NejlikaData::LoadNejlikaDataFile(const std::string& path) {
|
||||
std::ifstream file(path);
|
||||
|
||||
if (!file.is_open())
|
||||
{
|
||||
LOG("Failed to open nejlika data file: %s", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json json;
|
||||
|
||||
try
|
||||
{
|
||||
json = nlohmann::json::parse(file);
|
||||
}
|
||||
catch (const nlohmann::json::exception& e)
|
||||
{
|
||||
LOG("Failed to parse nejlika data file: %s", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.contains("modifier-templates"))
|
||||
{
|
||||
const auto& modifierTemplates = json["modifier-templates"];
|
||||
|
||||
for (const auto& value : modifierTemplates)
|
||||
{
|
||||
auto modifierTemplate = ModifierNameTemplate(value);
|
||||
|
||||
if (modifierTemplate.GetModifiers().empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
modifierNameTemplates[modifierTemplate.GetType()].push_back(modifierTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Loaded %d modifier templates", modifierNameTemplates.size());
|
||||
|
||||
if (json.contains("entity-templates"))
|
||||
{
|
||||
const auto& entityTemplatesArray = json["entity-templates"];
|
||||
|
||||
for (const auto& value : entityTemplatesArray)
|
||||
{
|
||||
auto entityTemplate = EntityTemplate(value);
|
||||
|
||||
entityTemplates[entityTemplate.GetLOT()] = entityTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Loaded %d entity templates", entityTemplates.size());
|
||||
|
||||
if (json.contains("upgrade-templates"))
|
||||
{
|
||||
const auto& upgradeTemplatesArray = json["upgrade-templates"];
|
||||
|
||||
for (const auto& value : upgradeTemplatesArray)
|
||||
{
|
||||
auto upgradeTemplate = UpgradeTemplate(value);
|
||||
|
||||
upgradeTemplates[upgradeTemplate.GetLot()] = upgradeTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Loaded %d upgrade templates", upgradeTemplates.size());
|
||||
}
|
||||
|
||||
42
dGame/NejlikaData.h
Normal file
42
dGame/NejlikaData.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "ModifierNameTemplate.h"
|
||||
#include "EntityTemplate.h"
|
||||
#include "AdditionalItemData.h"
|
||||
#include "AdditionalEntityData.h"
|
||||
#include "UpgradeTemplate.h"
|
||||
#include "Lookup.h"
|
||||
|
||||
namespace nejlika::NejlikaData
|
||||
{
|
||||
|
||||
const std::unordered_map<ModifierNameType, std::vector<ModifierNameTemplate>>& GetModifierNameTemplates();
|
||||
|
||||
const std::vector<ModifierNameTemplate>& GetModifierNameTemplates(ModifierNameType type);
|
||||
|
||||
const std::optional<AdditionalItemData*> GetAdditionalItemData(LWOOBJID id);
|
||||
|
||||
const std::optional<AdditionalEntityData*> GetAdditionalEntityData(LWOOBJID id);
|
||||
|
||||
const std::optional<EntityTemplate*> GetEntityTemplate(LOT lot);
|
||||
|
||||
const std::optional<UpgradeTemplate*> GetUpgradeTemplate(LOT lot);
|
||||
|
||||
void SetAdditionalItemData(LWOOBJID id, AdditionalItemData data);
|
||||
|
||||
void SetAdditionalEntityData(LWOOBJID id, AdditionalEntityData data);
|
||||
|
||||
void UnsetAdditionalItemData(LWOOBJID id);
|
||||
|
||||
void UnsetAdditionalEntityData(LWOOBJID id);
|
||||
|
||||
void LoadNejlikaData();
|
||||
|
||||
void LoadNejlikaDataFile(const std::string& path);
|
||||
|
||||
const Lookup& GetLookup();
|
||||
|
||||
}
|
||||
33
dGame/NejlikaHelpers.cpp
Normal file
33
dGame/NejlikaHelpers.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "NejlikaHelpers.h"
|
||||
|
||||
|
||||
void nejlika::NejlikaHelpers::RenderDamageText(const std::string & text, Entity * attacker, Entity * damaged,
|
||||
float scale, int32_t fontSize, int32_t colorR, int32_t colorG, int32_t colorB, int32_t colorA)
|
||||
{
|
||||
if (damaged == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto damagedPosition = damaged->GetPosition();
|
||||
|
||||
// Add a slight random offset to the damage position
|
||||
damagedPosition.x += (rand() % 10 - 5) / 5.0f;
|
||||
damagedPosition.y += (rand() % 10 - 5) / 5.0f;
|
||||
damagedPosition.z += (rand() % 10 - 5) / 5.0f;
|
||||
|
||||
const auto& damageText = text;
|
||||
|
||||
std::stringstream damageUIMessage;
|
||||
|
||||
damageUIMessage << 0.0825 << ";" << 0.12 << ";" << damagedPosition.x << ";" << damagedPosition.y + 4.5f << ";" << damagedPosition.z << ";" << 0.1 << ";";
|
||||
damageUIMessage << 200 * scale << ";" << 200 * scale << ";" << 0.5 << ";" << 1.0 << ";" << damageText << ";" << 4 << ";" << fontSize << ";" << colorR << ";" << colorG << ";" << colorB << ";";
|
||||
damageUIMessage << colorA;
|
||||
|
||||
const auto damageUIStr = damageUIMessage.str();
|
||||
|
||||
if (damaged->IsPlayer()) {
|
||||
damaged->SetNetworkVar<std::string>(u"renderText", damageUIStr, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
} else if (attacker != nullptr && attacker->IsPlayer()) {
|
||||
attacker->SetNetworkVar<std::string>(u"renderText", damageUIStr, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
}
|
||||
9
dGame/NejlikaHelpers.h
Normal file
9
dGame/NejlikaHelpers.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <Entity.h>
|
||||
|
||||
namespace nejlika::NejlikaHelpers
|
||||
{
|
||||
|
||||
void RenderDamageText(const std::string& text, Entity* attacker, Entity* damaged,
|
||||
float scale = 1, int32_t fontSize = 4, int32_t colorR = 255, int32_t colorG = 255, int32_t colorB = 255, int32_t colorA = 0);
|
||||
|
||||
}
|
||||
1048
dGame/NejlikaHooks.cpp
Normal file
1048
dGame/NejlikaHooks.cpp
Normal file
File diff suppressed because it is too large
Load Diff
10
dGame/NejlikaHooks.h
Normal file
10
dGame/NejlikaHooks.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace nejlika::NejlikaHooks
|
||||
{
|
||||
|
||||
void InstallHooks();
|
||||
|
||||
void ItemDescription(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
|
||||
}
|
||||
@@ -18,6 +18,8 @@ void PlayerManager::AddPlayer(Entity* player) {
|
||||
|
||||
if (iter == m_Players.end()) {
|
||||
m_Players.push_back(player);
|
||||
|
||||
OnPlayerAdded(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +29,8 @@ bool PlayerManager::RemovePlayer(Entity* player) {
|
||||
const bool toReturn = iter != m_Players.end();
|
||||
if (toReturn) {
|
||||
m_Players.erase(iter);
|
||||
|
||||
OnPlayerRemoved(player);
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __PLAYERMANAGER__H__
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Observable.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -20,6 +21,9 @@ namespace PlayerManager {
|
||||
Entity* GetPlayer(LWOOBJID playerID);
|
||||
|
||||
const std::vector<Entity*>& GetAllPlayers();
|
||||
|
||||
static Observable<Entity*> OnPlayerAdded;
|
||||
static Observable<Entity*> OnPlayerRemoved;
|
||||
};
|
||||
|
||||
#endif //!__PLAYERMANAGER__H__
|
||||
|
||||
16
dGame/TriggerParameters.h
Normal file
16
dGame/TriggerParameters.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "BehaviorSlot.h"
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class TriggerParameters
|
||||
{
|
||||
public:
|
||||
int32_t SkillID = 0;
|
||||
BehaviorSlot SelectedBehaviorSlot = BehaviorSlot::Invalid;
|
||||
};
|
||||
|
||||
}
|
||||
355
dGame/UpgradeEffect.cpp
Normal file
355
dGame/UpgradeEffect.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
#include "UpgradeEffect.h"
|
||||
|
||||
#include "GeneralUtils.h"
|
||||
#include "GameMessages.h"
|
||||
#include "InventoryComponent.h"
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
nejlika::UpgradeEffect::UpgradeEffect(const nlohmann::json& json)
|
||||
{
|
||||
Load(json);
|
||||
}
|
||||
|
||||
nlohmann::json nejlika::UpgradeEffect::ToJson() const
|
||||
{
|
||||
nlohmann::json json;
|
||||
|
||||
json["trigger-type"] = static_cast<int32_t>(triggerType);
|
||||
|
||||
nlohmann::json modifiersJson = nlohmann::json::array();
|
||||
|
||||
for (const auto& modifier : modifiers) {
|
||||
modifiersJson.push_back(modifier.ToJson());
|
||||
}
|
||||
|
||||
json["modifiers"] = modifiersJson;
|
||||
|
||||
if (!chance.empty()) {
|
||||
nlohmann::json chanceJson = nlohmann::json::array();
|
||||
|
||||
for (const auto& scale : chance) {
|
||||
chanceJson.push_back({
|
||||
{"level", scale.level},
|
||||
{"value", scale.value}
|
||||
});
|
||||
}
|
||||
|
||||
json["chance"] = chanceJson;
|
||||
}
|
||||
|
||||
if (effectID != 0) {
|
||||
json["effect-id"] = effectID;
|
||||
}
|
||||
|
||||
if (!effectType.empty()) {
|
||||
json["effect-type"] = effectType;
|
||||
}
|
||||
|
||||
if (!conditions.empty()) {
|
||||
nlohmann::json conditionsJson = nlohmann::json::array();
|
||||
|
||||
for (const auto& condition : conditions) {
|
||||
conditionsJson.push_back(magic_enum::enum_name(condition));
|
||||
}
|
||||
|
||||
json["conditions"] = conditionsJson;
|
||||
}
|
||||
|
||||
if (equipSkillID != 0) {
|
||||
json["grant-skill-id"] = equipSkillID;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::UpgradeEffect::GenerateModifiers(int32_t level) const
|
||||
{
|
||||
std::vector<ModifierInstance> result;
|
||||
|
||||
for (const auto& modifier : modifiers) {
|
||||
auto instances = modifier.GenerateModifiers(level);
|
||||
|
||||
result.insert(result.end(), instances.begin(), instances.end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nejlika::UpgradeEffect::Load(const nlohmann::json& json)
|
||||
{
|
||||
triggerType = magic_enum::enum_cast<UpgradeTriggerType>(json["trigger-type"].get<std::string>()).value_or(UpgradeTriggerType::OnHit);
|
||||
|
||||
modifiers.clear();
|
||||
|
||||
if (json.contains("modifiers")){
|
||||
for (const auto& modifier : json["modifiers"]) {
|
||||
ModifierTemplate effect(modifier);
|
||||
modifiers.push_back(effect);
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("chance")) {
|
||||
chance.clear();
|
||||
|
||||
for (const auto& scale : json["chance"]) {
|
||||
chance.push_back({
|
||||
scale["level"].get<int32_t>(),
|
||||
scale["value"].get<float>()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("effect-id")) {
|
||||
effectID = json["effect-id"].get<int32_t>();
|
||||
}
|
||||
|
||||
if (json.contains("effect-type")) {
|
||||
effectType = json["effect-type"].get<std::string>();
|
||||
}
|
||||
|
||||
if (json.contains("conditions")) {
|
||||
conditions.clear();
|
||||
|
||||
for (const auto& condition : json["conditions"]) {
|
||||
conditions.push_back(magic_enum::enum_cast<UpgradeTriggerCondition>(condition.get<std::string>()).value_or(UpgradeTriggerCondition::None));
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("grant-skill-id")) {
|
||||
equipSkillID = json["grant-skill-id"].get<int32_t>();
|
||||
}
|
||||
}
|
||||
|
||||
float nejlika::UpgradeEffect::CalculateChance(int32_t level) const {
|
||||
if (chance.empty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find the highest level that is less than or equal to the given level
|
||||
float value = 0;
|
||||
|
||||
for (const auto& scale : chance) {
|
||||
if (scale.level <= level) {
|
||||
value = scale.value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool nejlika::UpgradeEffect::CheckConditions(LWOOBJID origin, const TriggerParameters& params) const {
|
||||
auto* entity = Game::entityManager->GetEntity(origin);
|
||||
|
||||
if (!entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* inventory = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventory) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& skills = inventory->GetSkills();
|
||||
const auto& equipped = inventory->GetEquippedItems();
|
||||
|
||||
for (const auto& condition : conditions) {
|
||||
switch (condition) {
|
||||
case UpgradeTriggerCondition::PrimaryAbility:
|
||||
if (params.SelectedBehaviorSlot != BehaviorSlot::Primary) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case UpgradeTriggerCondition::UseSkill:
|
||||
if (params.SkillID != equipSkillID) {
|
||||
return false;
|
||||
}
|
||||
case UpgradeTriggerCondition::None:
|
||||
break;
|
||||
case UpgradeTriggerCondition::Unarmed:
|
||||
if (equipped.contains("special_r")) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case UpgradeTriggerCondition::Melee:
|
||||
if (!equipped.contains("special_r")) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case UpgradeTriggerCondition::TwoHanded:
|
||||
{
|
||||
if (!equipped.contains("special_r")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& weaponLot = equipped.at("special_r").lot;
|
||||
|
||||
const auto& info = Inventory::FindItemComponent(weaponLot);
|
||||
|
||||
if (!info.isTwoHanded) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UpgradeTriggerCondition::Shield:
|
||||
if (!equipped.contains("special_l")) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void nejlika::UpgradeEffect::OnTrigger(LWOOBJID origin) const {
|
||||
auto* entity = Game::entityManager->GetEntity(origin);
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventory = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventory) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::UpgradeEffect::Trigger(const std::vector<UpgradeEffect>& modifiers, int32_t level, UpgradeTriggerType triggerType, LWOOBJID origin, const TriggerParameters& params) {
|
||||
std::vector<ModifierInstance> result;
|
||||
|
||||
for (const auto& modifier : modifiers) {
|
||||
if (modifier.GetTriggerType() != triggerType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modifier.CheckConditions(origin, params)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float chanceRoll = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||
|
||||
if (chanceRoll > modifier.CalculateChance(level)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << "Triggering effect trigger type: " << magic_enum::enum_name(triggerType) << std::endl;
|
||||
|
||||
modifier.OnTrigger(origin);
|
||||
|
||||
auto instances = modifier.GenerateModifiers(level);
|
||||
|
||||
result.insert(result.end(), instances.begin(), instances.end());
|
||||
|
||||
if (modifier.effectID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GameMessages::SendPlayFXEffect(
|
||||
origin,
|
||||
modifier.effectID,
|
||||
GeneralUtils::UTF8ToUTF16(modifier.effectType),
|
||||
std::to_string(GeneralUtils::GenerateRandomNumber<size_t>())
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nejlika::UpgradeEffect::AddSkill(LWOOBJID origin) const {
|
||||
auto* entity = Game::entityManager->GetEntity(origin);
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventory = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerType != UpgradeTriggerType::Active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipSkillID != 0) {
|
||||
inventory->AddSkill(equipSkillID);
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::UpgradeEffect::RemoveSkill(LWOOBJID origin) const {
|
||||
auto* entity = Game::entityManager->GetEntity(origin);
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventory = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerType != UpgradeTriggerType::Active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipSkillID != 0) {
|
||||
inventory->RemoveSkill(equipSkillID);
|
||||
}
|
||||
}
|
||||
|
||||
std::string nejlika::UpgradeEffect::GenerateHtmlString(const std::vector<UpgradeEffect>& effects, int32_t level) {
|
||||
std::stringstream ss;
|
||||
|
||||
for (const auto& effect : effects) {
|
||||
const auto chance = effect.CalculateChance(level);
|
||||
|
||||
|
||||
for (const auto& condition : effect.conditions) {
|
||||
if (condition == UpgradeTriggerCondition::None || condition == UpgradeTriggerCondition::UseSkill) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ss << "<font color=\"#D0AB62\">";
|
||||
|
||||
switch (condition) {
|
||||
case UpgradeTriggerCondition::PrimaryAbility:
|
||||
ss << "On main-hand attack";
|
||||
break;
|
||||
case UpgradeTriggerCondition::Melee:
|
||||
ss << "Requires melee weapon";
|
||||
break;
|
||||
case UpgradeTriggerCondition::TwoHanded:
|
||||
ss << "Requires two-handed weapon";
|
||||
break;
|
||||
case UpgradeTriggerCondition::Shield:
|
||||
ss << "Requires a shield";
|
||||
break;
|
||||
case UpgradeTriggerCondition::Unarmed:
|
||||
ss << "Requires unarmed attack";
|
||||
break;
|
||||
}
|
||||
|
||||
ss << "</font>\n\n";
|
||||
}
|
||||
|
||||
if (chance < 1.0f) {
|
||||
ss << "<font color=\"#FFFFFF\">" << chance * 100 << "%</font><font color=\"#D0AB62\"> chance to trigger</font>\n";
|
||||
}
|
||||
|
||||
std::cout << "Level " << level << " chance: " << chance << std::endl;
|
||||
|
||||
ss << ModifierInstance::GenerateHtmlString(effect.GenerateModifiers(level));
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
69
dGame/UpgradeEffect.h
Normal file
69
dGame/UpgradeEffect.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "ModifierTemplate.h"
|
||||
#include "UpgradeTriggerType.h"
|
||||
#include "UpgradeTriggerCondition.h"
|
||||
#include <InventoryComponent.h>
|
||||
#include "TriggerParameters.h"
|
||||
|
||||
#include <dCommonVars.h>
|
||||
|
||||
namespace nejlika
|
||||
{
|
||||
|
||||
class UpgradeEffect
|
||||
{
|
||||
public:
|
||||
UpgradeEffect(const nlohmann::json& json);
|
||||
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
std::vector<ModifierInstance> GenerateModifiers(int32_t level) const;
|
||||
|
||||
void Load(const nlohmann::json& json);
|
||||
|
||||
float CalculateChance(int32_t level) const;
|
||||
|
||||
bool CheckConditions(LWOOBJID origin, const TriggerParameters& params) const;
|
||||
|
||||
void OnTrigger(LWOOBJID origin) const;
|
||||
|
||||
static std::vector<ModifierInstance> Trigger(const std::vector<UpgradeEffect>& modifiers, int32_t level, UpgradeTriggerType triggerType, LWOOBJID origin, const TriggerParameters& params);
|
||||
|
||||
// Getters
|
||||
|
||||
const std::vector<ModifierTemplate>& GetModifiers() const { return modifiers; }
|
||||
|
||||
UpgradeTriggerType GetTriggerType() const { return triggerType; }
|
||||
|
||||
void AddSkill(LWOOBJID origin) const;
|
||||
void RemoveSkill(LWOOBJID origin) const;
|
||||
|
||||
/**
|
||||
* @brief Generate a HTML string representation of a set of upgrade effects.
|
||||
*
|
||||
* @param modifiers The upgrade effects to generate the HTML string for.
|
||||
* @param level The level of the upgrade effects.
|
||||
* @return The HTML string.
|
||||
*/
|
||||
static std::string GenerateHtmlString(const std::vector<UpgradeEffect>& effects, int32_t level);
|
||||
|
||||
private:
|
||||
struct UpgradeScale
|
||||
{
|
||||
int32_t level;
|
||||
float value;
|
||||
};
|
||||
|
||||
std::vector<UpgradeScale> chance;
|
||||
std::vector<UpgradeTriggerCondition> conditions;
|
||||
UpgradeTriggerType triggerType;
|
||||
int32_t equipSkillID = 0;
|
||||
std::vector<ModifierTemplate> modifiers;
|
||||
int32_t effectID = 0;
|
||||
std::string effectType = "";
|
||||
std::vector<ModifierTemplate> debuffs;
|
||||
std::vector<UpgradeScale> debuffDuration;
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user