mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-05-23 15:22:28 +00:00
Merge branch 'main' into scripting-lua
This commit is contained in:
commit
e37f1bcee3
8
.github/workflows/build-and-test.yml
vendored
8
.github/workflows/build-and-test.yml
vendored
@ -12,18 +12,21 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-2022, ubuntu-20.04 ]
|
||||
os: [ windows-2022, ubuntu-20.04, macos-11 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Add msbuild to PATH (windows only)
|
||||
- name: Add msbuild to PATH (Windows only)
|
||||
if: ${{ matrix.os == 'windows-2022' }}
|
||||
uses: microsoft/setup-msbuild@v1.1
|
||||
with:
|
||||
vs-version: '[17,18)'
|
||||
msbuild-architecture: x64
|
||||
- name: Install libssl (Mac Only)
|
||||
if: ${{ matrix.os == 'macos-11' }}
|
||||
run: brew install openssl@3
|
||||
- name: cmake
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
@ -32,6 +35,7 @@ jobs:
|
||||
testPreset: "ci-${{matrix.os}}"
|
||||
- name: artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ github.ref == 'ref/head/main' }}
|
||||
with:
|
||||
name: build-${{matrix.os}}
|
||||
path: |
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -10,6 +10,10 @@
|
||||
[submodule "thirdparty/libbcrypt"]
|
||||
path = thirdparty/libbcrypt
|
||||
url = https://github.com/trusch/libbcrypt.git
|
||||
[submodule "thirdparty/mariadb-connector-cpp"]
|
||||
path = thirdparty/mariadb-connector-cpp
|
||||
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "thirdparty/docker-utils"]
|
||||
path = thirdparty/docker-utils
|
||||
url = https://github.com/lcdr/utils.git
|
||||
|
649
CMakeLists.txt
649
CMakeLists.txt
@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.14)
|
||||
project(Darkflame)
|
||||
include(CTest)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Read variables from file
|
||||
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
|
||||
|
||||
@ -10,113 +12,67 @@ string(REPLACE "\n" ";" variables ${variables})
|
||||
|
||||
# Set the cmake variables, formatted as "VARIABLE #" in variables
|
||||
foreach(variable ${variables})
|
||||
# If the string contains a #, skip it
|
||||
if("${variable}" MATCHES "#")
|
||||
continue()
|
||||
endif()
|
||||
# If the string contains a #, skip it
|
||||
if(NOT "${variable}" MATCHES "#")
|
||||
|
||||
# Split the variable into name and value
|
||||
string(REPLACE "=" ";" variable ${variable})
|
||||
|
||||
# Split the variable into name and value
|
||||
string(REPLACE "=" ";" variable ${variable})
|
||||
# Check that the length of the variable is 2 (name and value)
|
||||
list(LENGTH variable length)
|
||||
if(${length} EQUAL 2)
|
||||
|
||||
# Check that the length of the variable is 2 (name and value)
|
||||
list(LENGTH variable length)
|
||||
if(NOT ${length} EQUAL 2)
|
||||
continue()
|
||||
endif()
|
||||
list(GET variable 0 variable_name)
|
||||
list(GET variable 1 variable_value)
|
||||
|
||||
list(GET variable 0 variable_name)
|
||||
list(GET variable 1 variable_value)
|
||||
# Set the variable
|
||||
set(${variable_name} ${variable_value})
|
||||
|
||||
# Set the variable
|
||||
set(${variable_name} ${variable_value})
|
||||
# Add compiler definition
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
|
||||
|
||||
# Add compiler definition
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
|
||||
|
||||
message(STATUS "Variable: ${variable_name} = ${variable_value}")
|
||||
message(STATUS "Variable: ${variable_name} = ${variable_value}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# On windows it's better to build this from source, as there's no way FindZLIB is gonna find it
|
||||
if(NOT WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
endif()
|
||||
|
||||
# Fetch External (Non-Submodule) Libraries
|
||||
if(WIN32)
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
mysql
|
||||
URL https://dev.mysql.com/get/Downloads/Connector-C++/mysql-connector-c++-8.0.27-winx64.zip
|
||||
URL_HASH MD5=e3c53f6e4d0a72fde2713f7597bf9468
|
||||
)
|
||||
|
||||
FetchContent_Declare(
|
||||
zlib
|
||||
URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
|
||||
URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(zlib)
|
||||
FetchContent_MakeAvailable(mysql)
|
||||
|
||||
set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR})
|
||||
set_target_properties(zlib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIRS}") # Why?
|
||||
add_library(ZLIB::ZLIB ALIAS zlib) # You're welcome
|
||||
|
||||
endif(WIN32)
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
else()
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
mysql
|
||||
URL https://dev.mysql.com/get/Downloads/Connector-C++/mysql-connector-c++-8.0.27-linux-glibc2.12-x86-64bit.tar.gz
|
||||
URL_HASH MD5=12f086b76c11022cc7139b41a36cdf9e
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(mysql)
|
||||
|
||||
if (__include_backtrace__ AND __compile_backtrace__)
|
||||
FetchContent_Declare(
|
||||
backtrace
|
||||
GIT_REPOSITORY https://github.com/ianlancetaylor/libbacktrace.git
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(backtrace)
|
||||
|
||||
if (NOT EXISTS ${backtrace_SOURCE_DIR}/.libs)
|
||||
set(backtrace_make_cmd "${backtrace_SOURCE_DIR}/configure --prefix=\"/usr\" --enable-shared --with-system-libunwind")
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "cd ${backtrace_SOURCE_DIR} && ${backtrace_make_cmd} && make && cd ${CMAKE_SOURCE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
link_directories(${backtrace_SOURCE_DIR}/.libs/)
|
||||
include_directories(${backtrace_SOURCE_DIR})
|
||||
endif(__include_backtrace__)
|
||||
|
||||
endif()
|
||||
endif(UNIX)
|
||||
|
||||
# Set the version
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
|
||||
|
||||
# Echo the version
|
||||
message(STATUS "Version: ${PROJECT_VERSION}")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(WIN32)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif(WIN32)
|
||||
# Compiler flags:
|
||||
# Disabled deprecated warnings as the MySQL includes have deprecated code in them.
|
||||
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
|
||||
# Disabled no-register
|
||||
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC")
|
||||
endif()
|
||||
if (__dynamic)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
|
||||
endif()
|
||||
if (__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
|
||||
add_compile_options("/wd4267")
|
||||
elseif(WIN32)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
# Our output dir
|
||||
set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Create a /res directory
|
||||
make_directory(${CMAKE_BINARY_DIR}/res)
|
||||
@ -128,86 +84,82 @@ make_directory(${CMAKE_BINARY_DIR}/locale)
|
||||
make_directory(${CMAKE_BINARY_DIR}/logs)
|
||||
|
||||
# Copy ini files on first build
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/authconfig.ini)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/authconfig.ini ${PROJECT_BINARY_DIR}/authconfig.ini
|
||||
COPYONLY
|
||||
)
|
||||
endif()
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/chatconfig.ini)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/chatconfig.ini ${PROJECT_BINARY_DIR}/chatconfig.ini
|
||||
COPYONLY
|
||||
)
|
||||
endif()
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/worldconfig.ini)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/worldconfig.ini ${PROJECT_BINARY_DIR}/worldconfig.ini
|
||||
COPYONLY
|
||||
)
|
||||
endif()
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/masterconfig.ini)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/masterconfig.ini ${PROJECT_BINARY_DIR}/masterconfig.ini
|
||||
COPYONLY
|
||||
)
|
||||
set(INI_FILES "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini")
|
||||
foreach(ini ${INI_FILES})
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${ini})
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/resources/${ini} ${PROJECT_BINARY_DIR}/${ini}
|
||||
COPYONLY
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Copy vanity files on first build
|
||||
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
|
||||
foreach(file ${VANITY_FILES})
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
|
||||
endforeach()
|
||||
|
||||
# Move our migrations for MasterServer to run
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/)
|
||||
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
|
||||
foreach(file ${SQL_FILES})
|
||||
get_filename_component(file ${file} NAME)
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/${file})
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/${file}
|
||||
COPYONLY
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Create our list of include directories
|
||||
set(INCLUDED_DIRECTORIES
|
||||
"dCommon"
|
||||
"dChatFilter"
|
||||
"dGame"
|
||||
"dGame/dBehaviors"
|
||||
"dGame/dComponents"
|
||||
"dGame/dGameMessages"
|
||||
"dGame/dInventory"
|
||||
"dGame/dMission"
|
||||
"dGame/dEntity"
|
||||
"dGame/dUtilities"
|
||||
"dPhysics"
|
||||
"dZoneManager"
|
||||
"dDatabase"
|
||||
"dDatabase/Tables"
|
||||
"dNet"
|
||||
"dScripts"
|
||||
|
||||
"thirdparty/raknet/Source"
|
||||
"thirdparty/tinyxml2"
|
||||
"thirdparty/recastnavigation/Recast/Include"
|
||||
"thirdparty/recastnavigation/Detour/Include"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
)
|
||||
|
||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
|
||||
if (APPLE)
|
||||
include_directories("/usr/local/include/")
|
||||
endif()
|
||||
|
||||
# Copy files to output
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/CREDITS.md" "${CMAKE_BINARY_DIR}/vanity/CREDITS.md" COPYONLY)
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/INFO.md" "${CMAKE_BINARY_DIR}/vanity/INFO.md" COPYONLY)
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/TESTAMENT.md" "${CMAKE_BINARY_DIR}/vanity/TESTAMENT.md" COPYONLY)
|
||||
configure_file("${CMAKE_SOURCE_DIR}/vanity/NPC.xml" "${CMAKE_BINARY_DIR}/vanity/NPC.xml" COPYONLY)
|
||||
if (WIN32)
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
|
||||
elseif (UNIX)
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
|
||||
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
|
||||
endif()
|
||||
|
||||
# 3rdparty includes
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/raknet/Source/)
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
include_directories(/usr/local/include/)
|
||||
include_directories(/usr/local/mysql-connector-c++/include/jdbc/)
|
||||
include_directories(/usr/local/mysql-connector-c++/include/jdbc/cppconn/)
|
||||
else()
|
||||
include_directories(${mysql_SOURCE_DIR}/include/jdbc/)
|
||||
include_directories(${mysql_SOURCE_DIR}/include/jdbc/cppconn/)
|
||||
endif(APPLE)
|
||||
endif(UNIX)
|
||||
if(WIN32)
|
||||
include_directories(${mysql_SOURCE_DIR}/include/jdbc)
|
||||
include_directories(${mysql_SOURCE_DIR}/include/jdbc/cppconn)
|
||||
endif(WIN32)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
|
||||
# Bcrypt
|
||||
if (NOT WIN32)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt)
|
||||
else ()
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include)
|
||||
endif ()
|
||||
|
||||
# Our includes
|
||||
# Add binary directory as an include directory
|
||||
include_directories(${PROJECT_BINARY_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dChatFilter/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dBehaviors)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dComponents)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dGameMessages)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dInventory)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dMission)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dEntity)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dGame/dUtilities)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dPhysics/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dZoneManager/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dDatabase/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dDatabase/Tables/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/SQLite/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/thirdparty/cpplinq/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dNet/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dScripts/)
|
||||
|
||||
# Actually include the directories from our list
|
||||
foreach (dir ${INCLUDED_DIRECTORIES})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/${dir})
|
||||
endforeach()
|
||||
|
||||
# Link dGame to LUA
|
||||
if(__include_lua__)
|
||||
@ -217,311 +169,116 @@ if(__include_lua__)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dLua/)
|
||||
endif(UNIX)
|
||||
|
||||
# Default to linking to libmysql
|
||||
set(MYSQL_LIB mysql)
|
||||
if(WIN32)
|
||||
set(MYSQL_LIB mysqlcppconn)
|
||||
endif(WIN32)
|
||||
|
||||
# Lib folders:
|
||||
# Add linking directories:
|
||||
link_directories(${PROJECT_BINARY_DIR})
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
link_directories(/usr/local/mysql-connector-c++/lib64/)
|
||||
else()
|
||||
link_directories(${mysql_SOURCE_DIR}/lib64/)
|
||||
|
||||
# Link to libmysqlcppconn on Linux
|
||||
set(MYSQL_LIB mysqlcppconn)
|
||||
endif(APPLE)
|
||||
endif(UNIX)
|
||||
if(WIN32)
|
||||
link_directories(${mysql_SOURCE_DIR}/lib64/vs14)
|
||||
endif(WIN32)
|
||||
# Load all of our third party directories
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
# Source Code
|
||||
# Glob together all headers that need to be precompiled
|
||||
file(
|
||||
GLOB SOURCES
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dWorldServer/*.cpp
|
||||
GLOB HEADERS_DDATABASE
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||
)
|
||||
|
||||
# Source Code for AuthServer
|
||||
file(
|
||||
GLOB SOURCES_AUTH
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dAuthServer/*.cpp
|
||||
GLOB HEADERS_DZONEMANAGER
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dZoneManager/*.h
|
||||
)
|
||||
|
||||
# Source Code for MasterServer
|
||||
file(
|
||||
GLOB SOURCES_MASTER
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dMasterServer/*.cpp
|
||||
GLOB HEADERS_DCOMMON
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dCommon/*.h
|
||||
)
|
||||
|
||||
# Source Code for ChatServer
|
||||
file(
|
||||
GLOB SOURCES_CHAT
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dChatServer/*.cpp
|
||||
GLOB HEADERS_DGAME
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dGame/Entity.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h
|
||||
${PROJECT_SOURCE_DIR}/dGame/EntityManager.h
|
||||
${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h
|
||||
)
|
||||
|
||||
# Source Code for raknet
|
||||
file(
|
||||
GLOB SOURCES_RAKNET
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/raknet/Source/*.cpp
|
||||
# Add our library subdirectories for creation of the library object
|
||||
add_subdirectory(dCommon)
|
||||
add_subdirectory(dDatabase)
|
||||
add_subdirectory(dChatFilter)
|
||||
add_subdirectory(dNet)
|
||||
add_subdirectory(dScripts) # Add for dGame to use
|
||||
add_subdirectory(dGame)
|
||||
add_subdirectory(dZoneManager)
|
||||
add_subdirectory(dPhysics)
|
||||
|
||||
# Create a list of common libraries shared between all binaries
|
||||
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp")
|
||||
|
||||
# Add platform specific common libraries
|
||||
if (UNIX)
|
||||
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
|
||||
|
||||
if (NOT APPLE AND __include_backtrace__)
|
||||
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(tests)
|
||||
|
||||
# Include all of our binary directories
|
||||
add_subdirectory(dWorldServer)
|
||||
add_subdirectory(dAuthServer)
|
||||
add_subdirectory(dChatServer)
|
||||
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
|
||||
|
||||
# Add our precompiled headers
|
||||
target_precompile_headers(
|
||||
dGame PRIVATE
|
||||
${HEADERS_DGAME}
|
||||
)
|
||||
|
||||
# Source Code for recast
|
||||
file(
|
||||
GLOB SOURCES_RECAST
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Source/*.cpp
|
||||
)
|
||||
|
||||
# Source Code for detour
|
||||
file(
|
||||
GLOB SOURCES_DETOUR
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Source/*.cpp
|
||||
)
|
||||
|
||||
# Source Code for tinyxml2
|
||||
file(
|
||||
GLOB SOURCES_TINYXML2
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.cpp
|
||||
)
|
||||
|
||||
# Source Code for libbcrypt
|
||||
file(
|
||||
GLOB SOURCES_LIBBCRYPT
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/*.c
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/src/*.c
|
||||
)
|
||||
|
||||
# Source Code for dCommon
|
||||
file(
|
||||
GLOB SOURCES_DCOMMON
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dCommon/*.cpp
|
||||
)
|
||||
|
||||
# Source Code for dChatFilter
|
||||
file(
|
||||
GLOB SOURCES_DCHATFILTER
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dChatFilter/*.cpp
|
||||
)
|
||||
|
||||
# Source Code for dDatabase
|
||||
file(
|
||||
GLOB SOURCES_DDATABASE
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.c
|
||||
)
|
||||
|
||||
# Source Code for dNet
|
||||
file(
|
||||
GLOB SOURCES_DNET
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dNet/*.cpp
|
||||
)
|
||||
|
||||
# Source Code for dGame
|
||||
file(
|
||||
GLOB SOURCES_DGAME
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dGame/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dBehaviors/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dComponents/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dGameMessages/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dInventory/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dMission/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dEntity/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dGame/dUtilities/*.cpp
|
||||
${PROJECT_SOURCE_DIR}/dScripts/*.cpp
|
||||
target_precompile_headers(
|
||||
dZoneManager PRIVATE
|
||||
${HEADERS_DZONEMANAGER}
|
||||
)
|
||||
|
||||
# If we are including LUA, include the dLua files in dGame
|
||||
if(__include_lua__)
|
||||
file(
|
||||
GLOB SOURCES_DLUA
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dLua/*.cpp
|
||||
)
|
||||
#if(__include_lua__)
|
||||
# file(
|
||||
# GLOB SOURCES_DLUA
|
||||
# LIST_DIRECTORIES false
|
||||
# RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
# ${PROJECT_SOURCE_DIR}/dLua/*.cpp
|
||||
# )
|
||||
|
||||
# Append the dLua files to the dGame files
|
||||
set(SOURCES_DGAME ${SOURCES_DGAME} ${SOURCES_DLUA})
|
||||
endif(__include_lua__)
|
||||
# Append the dLua files to the dGame files
|
||||
#set(SOURCES_DGAME ${SOURCES_DGAME} ${SOURCES_DLUA})
|
||||
#endif(__include_lua__)
|
||||
## Need to specify to use the CXX compiler language here or else we get errors including <string>.
|
||||
#target_precompile_headers(
|
||||
# dDatabase PRIVATE
|
||||
# "$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE}>"
|
||||
#)
|
||||
|
||||
# Source Code for dZoneManager
|
||||
file(
|
||||
GLOB SOURCES_DZM
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dZoneManager/*.cpp
|
||||
target_precompile_headers(
|
||||
dCommon PRIVATE
|
||||
${HEADERS_DCOMMON}
|
||||
)
|
||||
|
||||
# Source Code for dPhysics
|
||||
file(
|
||||
GLOB SOURCES_DPHYSICS
|
||||
LIST_DIRECTORIES false
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
${PROJECT_SOURCE_DIR}/dPhysics/*.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
|
||||
add_compile_options("/wd4267")
|
||||
endif(MSVC)
|
||||
|
||||
# 3rdparty static libraries:
|
||||
#add_library(zlib ${SOURCES_ZLIB})
|
||||
add_library(raknet ${SOURCES_RAKNET})
|
||||
add_library(tinyxml2 ${SOURCES_TINYXML2})
|
||||
add_library(detour ${SOURCES_DETOUR})
|
||||
add_library(recast ${SOURCES_RECAST})
|
||||
add_library(libbcrypt ${SOURCES_LIBBCRYPT})
|
||||
|
||||
# Our static libraries:
|
||||
add_library(dCommon ${SOURCES_DCOMMON})
|
||||
add_library(dChatFilter ${SOURCES_DCHATFILTER})
|
||||
add_library(dDatabase ${SOURCES_DDATABASE})
|
||||
add_library(dNet ${SOURCES_DNET})
|
||||
add_library(dGame ${SOURCES_DGAME})
|
||||
add_library(dZoneManager ${SOURCES_DZM})
|
||||
add_library(dPhysics ${SOURCES_DPHYSICS})
|
||||
target_link_libraries(dNet dCommon) #Needed because otherwise linker errors occur.
|
||||
target_link_libraries(dCommon ZLIB::ZLIB)
|
||||
target_link_libraries(dCommon libbcrypt)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(raknet ws2_32)
|
||||
endif(WIN32)
|
||||
|
||||
# Our executables:
|
||||
add_executable(WorldServer ${SOURCES})
|
||||
add_executable(AuthServer ${SOURCES_AUTH})
|
||||
add_executable(MasterServer ${SOURCES_MASTER})
|
||||
add_executable(ChatServer ${SOURCES_CHAT})
|
||||
|
||||
# Target libraries to link to:
|
||||
target_link_libraries(WorldServer dCommon)
|
||||
target_link_libraries(WorldServer dChatFilter)
|
||||
target_link_libraries(WorldServer dDatabase)
|
||||
target_link_libraries(WorldServer dNet)
|
||||
target_link_libraries(WorldServer dGame)
|
||||
target_link_libraries(WorldServer dZoneManager)
|
||||
target_link_libraries(WorldServer dPhysics)
|
||||
target_link_libraries(WorldServer detour)
|
||||
target_link_libraries(WorldServer recast)
|
||||
target_link_libraries(WorldServer raknet)
|
||||
target_link_libraries(WorldServer ${MYSQL_LIB})
|
||||
if(UNIX)
|
||||
target_link_libraries(WorldServer pthread)
|
||||
target_link_libraries(WorldServer dl)
|
||||
|
||||
if(NOT APPLE AND __include_backtrace__)
|
||||
target_link_libraries(WorldServer backtrace)
|
||||
target_link_libraries(MasterServer backtrace)
|
||||
target_link_libraries(AuthServer backtrace)
|
||||
target_link_libraries(ChatServer backtrace)
|
||||
endif()
|
||||
|
||||
endif(UNIX)
|
||||
target_link_libraries(WorldServer tinyxml2)
|
||||
|
||||
# Target libraries for Auth:
|
||||
target_link_libraries(AuthServer dCommon)
|
||||
target_link_libraries(AuthServer dDatabase)
|
||||
target_link_libraries(AuthServer dNet)
|
||||
target_link_libraries(AuthServer raknet)
|
||||
target_link_libraries(AuthServer ${MYSQL_LIB})
|
||||
if(UNIX)
|
||||
target_link_libraries(AuthServer pthread)
|
||||
target_link_libraries(AuthServer dl)
|
||||
endif(UNIX)
|
||||
|
||||
# Target libraries for Master:
|
||||
target_link_libraries(MasterServer dCommon)
|
||||
target_link_libraries(MasterServer dDatabase)
|
||||
target_link_libraries(MasterServer dNet)
|
||||
target_link_libraries(MasterServer raknet)
|
||||
target_link_libraries(MasterServer ${MYSQL_LIB})
|
||||
if(UNIX)
|
||||
target_link_libraries(MasterServer pthread)
|
||||
target_link_libraries(MasterServer dl)
|
||||
endif(UNIX)
|
||||
|
||||
# Target libraries for Chat:
|
||||
target_link_libraries(ChatServer dCommon)
|
||||
target_link_libraries(ChatServer dChatFilter)
|
||||
target_link_libraries(ChatServer dDatabase)
|
||||
target_link_libraries(ChatServer dNet)
|
||||
target_link_libraries(ChatServer raknet)
|
||||
target_link_libraries(ChatServer ${MYSQL_LIB})
|
||||
if(UNIX)
|
||||
target_link_libraries(ChatServer pthread)
|
||||
target_link_libraries(ChatServer dl)
|
||||
endif(UNIX)
|
||||
|
||||
# Link dGame to LUA
|
||||
if(__include_lua__)
|
||||
find_package(Lua REQUIRED)
|
||||
#if(__include_lua__)
|
||||
# find_package(Lua REQUIRED)
|
||||
|
||||
target_link_libraries(dGame ${LUA_LIBRARIES})
|
||||
# target_link_libraries(dGame ${LUA_LIBRARIES})
|
||||
|
||||
message(STATUS "Linking dGame to LUA " ${LUA_LIBRARIES})
|
||||
endif(UNIX)
|
||||
|
||||
# Compiler flags:
|
||||
# Disabled deprecated warnings as the MySQL includes have deprecated code in them.
|
||||
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
|
||||
# Disabled no-register
|
||||
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -Wno-unused-result -Wno-unknown-pragmas -fpermissive -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -Wno-unused-result -Wno-unknown-pragmas -fpermissive -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC")
|
||||
endif()
|
||||
if (__dynamic)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
|
||||
endif()
|
||||
if (__ggdb)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
|
||||
endif(UNIX)
|
||||
|
||||
if(WIN32)
|
||||
add_dependencies(MasterServer WorldServer)
|
||||
add_dependencies(MasterServer AuthServer)
|
||||
add_dependencies(MasterServer ChatServer)
|
||||
endif()
|
||||
|
||||
# Finally, add the tests
|
||||
add_subdirectory(tests)
|
||||
# message(STATUS "Linking dGame to LUA " ${LUA_LIBRARIES})
|
||||
#endif(UNIX)
|
||||
|
||||
target_precompile_headers(
|
||||
tinyxml2 PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
|
||||
)
|
||||
|
@ -19,6 +19,15 @@
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default"
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-11",
|
||||
"displayName": "CI configure step for MacOS",
|
||||
"description": "Same as default, Used in GitHub actions workflow",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"OPENSSL_ROOT_DIR": "/usr/local/Cellar/openssl@3/3.0.5/"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"displayName": "CI configure step for Windows",
|
||||
@ -66,6 +75,13 @@
|
||||
"displayName": "Linux CI Build",
|
||||
"description": "This preset is used by the CI build on linux",
|
||||
"jobs": 2
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-11",
|
||||
"configurePreset": "ci-macos-11",
|
||||
"displayName": "MacOS CI Build",
|
||||
"description": "This preset is used by the CI build on MacOS",
|
||||
"jobs": 2
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
@ -81,6 +97,18 @@
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-macos-11",
|
||||
"configurePreset": "ci-macos-11",
|
||||
"displayName": "CI Tests on MacOS",
|
||||
"description": "Runs all tests on a Mac configuration",
|
||||
"execution": {
|
||||
"jobs": 2
|
||||
},
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "ci-windows-2022",
|
||||
"configurePreset": "ci-windows-2022",
|
||||
|
@ -1,6 +1,6 @@
|
||||
PROJECT_VERSION_MAJOR=1
|
||||
PROJECT_VERSION_MINOR=0
|
||||
PROJECT_VERSION_PATCH=2
|
||||
PROJECT_VERSION_PATCH=3
|
||||
# LICENSE
|
||||
LICENSE=AGPL-3.0
|
||||
# The network version.
|
||||
@ -16,3 +16,5 @@ NET_VERSION=171022
|
||||
# Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs.
|
||||
# __compile_backtrace__=1
|
||||
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
|
||||
__maria_db_connector_compile_jobs__=1
|
||||
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
|
11
Docker.md
11
Docker.md
@ -4,7 +4,7 @@
|
||||
|
||||
- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop or on Linux normal Docker)
|
||||
- [Docker Compose](https://docs.docker.com/compose/install/) (Included in Docker Desktop)
|
||||
- LEGO® Universe Client (packed or unpacked). Check the main [README](./README.md) for details on this.
|
||||
- LEGO® Universe packed Client. Check the main [README](./README.md) for details on this.
|
||||
|
||||
## Run server inside Docker
|
||||
|
||||
@ -21,6 +21,15 @@
|
||||
|
||||
**NOTE #2**: To stop the server simply run `docker compose down` and to restart it just run `docker compose up -d` again. No need to run all the steps above every time.
|
||||
|
||||
**NOTE #3**: Docker buildkit needs to be enabled. https://docs.docker.com/develop/develop-images/build_enhancements/#to-enable-buildkit-builds
|
||||
|
||||
**NOTE #4**: Make sure to run the following in the repo root directory after cloning so submodules are also downloaded.
|
||||
```
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
**NOTE #5**: If DarkflameSetup fails due to not having cdclient.fdb, rename CDClient.fdb (in the same folder) to cdclient.fdb
|
||||
|
||||
## Disable brickbuildfix
|
||||
|
||||
If you don't need the http server running on port 80 do this:
|
||||
|
94
README.md
94
README.md
@ -31,7 +31,6 @@ Development of the latest iteration of Darkflame Universe has been done primaril
|
||||
```bash
|
||||
git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
|
||||
```
|
||||
|
||||
**Python**
|
||||
|
||||
Some tools utilized to streamline the setup process require Python 3, make sure you have it installed.
|
||||
@ -44,7 +43,7 @@ This was done make sure that older and incomplete clients wouldn't produce false
|
||||
If you're using a DLU client you'll have to go into the "CMakeVariables.txt" file and change the NET_VERSION variable to 171023 to match the modified client's version number.
|
||||
|
||||
### Linux builds
|
||||
Make sure packages like `gcc`, `cmake`, and `zlib` are installed. Depending on the distribution, these packages might already be installed. Note that on systems like Ubuntu, you will need the `zlib1g-dev` package so that the header files are available.
|
||||
Make sure packages like `gcc`, `cmake`, and `zlib` are installed. Depending on the distribution, these packages might already be installed. Note that on systems like Ubuntu, you will need the `zlib1g-dev` package so that the header files are available. `libssl-dev` will also be required as well as `openssl`.
|
||||
|
||||
CMake must be version 3.14 or higher!
|
||||
|
||||
@ -71,22 +70,19 @@ make
|
||||
```
|
||||
|
||||
### MacOS builds
|
||||
Ensure `cmake`, `zlib` and `open ssl` are installed as well as a compiler (e.g `clang` or `gcc`).
|
||||
|
||||
**Download precompiled MySQL connector**
|
||||
In the repository root folder run the following. Ensure -DOPENSSL_ROOT_DIR=/path/to/openssl points to your openssl install location
|
||||
```bash
|
||||
# Install required tools
|
||||
brew install boost mysql-connector-c++
|
||||
# Create the build directory, preserving it if it already exists
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
# Symlinks for finding the required modules
|
||||
sudo ln -s /usr/local/mysql-connector-c++/lib64/libmysqlcppconn.dylib /usr/local/mysql-connector-c++/lib64/libmysql.dylib
|
||||
sudo ln -s /usr/local/mysql-connector-c++/lib64/libcrypto.1.1.dylib /usr/local/mysql/lib/libcrypto.1.1.dylib
|
||||
```
|
||||
# Run CMake to generate build files
|
||||
cmake .. -DOPENSSL_ROOT_DIR=/path/to/openssl
|
||||
|
||||
Then follow the Linux build steps (gcc is not required), but before running `make`, run the following to make sure all the libs are available in the build folder:
|
||||
|
||||
```bash
|
||||
sudo ln -s /usr/local/mysql-connector-c++/lib64/libssl.1.1.dylib /path/to/build/folder/libssl.1.1.dylib
|
||||
sudo ln -s /usr/local/mysql-connector-c++/lib64/libcrypto.1.1.dylib /path/to/build/folder/libcrypto.1.1.dylib
|
||||
# Get cmake to build the project. If make files are being used then using make and appending `-j` and the amount of cores to utilize may be preferable, for example `make -j8`
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
### Windows builds (native)
|
||||
@ -102,9 +98,20 @@ cd build
|
||||
cmake ..
|
||||
|
||||
:: Run CMake with build flag to build
|
||||
cmake --build .
|
||||
cmake --build . --config Release
|
||||
```
|
||||
**Windows for ARM** has not been tested but should build by doing the following
|
||||
```batch
|
||||
:: Create the build directory
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
:: Run CMake to generate make files
|
||||
cmake .. -DMARIADB_BUILD_SOURCE=ON
|
||||
|
||||
:: Run CMake with build flag to build
|
||||
cmake --build . --config Release
|
||||
```
|
||||
|
||||
### Windows builds (WSL)
|
||||
This section will go through how to install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) and building in a Linux environment under Windows. WSL requires Windows 10 version 2004 and higher (Build 19041 and higher) or Windows 11.
|
||||
@ -128,15 +135,23 @@ sudo apt install build-essential
|
||||
|
||||
[**Follow the Linux instructions**](#linux-builds)
|
||||
|
||||
### ARM builds
|
||||
AArch64 builds should work on linux and MacOS using their respective build steps. Windows ARM should build but it has not been tested
|
||||
|
||||
### Updating your build
|
||||
To update your server to the latest version navigate to your cloned directory
|
||||
```bash
|
||||
cd /path/to/DarkflameServer
|
||||
```
|
||||
run the following commands to update to the latest changes
|
||||
```bash
|
||||
git pull
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
now follow the build section for your system
|
||||
|
||||
## Setting up the environment
|
||||
|
||||
### Database
|
||||
Darkflame Universe utilizes a MySQL/MariaDB database for account and character information.
|
||||
|
||||
Initial setup can vary drastically based on which operating system or distribution you are running; there are instructions out there for most setups, follow those and come back here when you have a database up and running.
|
||||
* Create a database for Darkflame Universe to use
|
||||
* Run each SQL file in the order at which they appear [here](migrations/dlu/) on the database
|
||||
|
||||
### Resources
|
||||
|
||||
**LEGO® Universe 1.10.64**
|
||||
@ -180,6 +195,13 @@ certutil -hashfile <file> SHA256
|
||||
* Move and rename `cdclient.sqlite` into `build/res/CDServer.sqlite`
|
||||
* Run each SQL file in the order at which they appear [here](migrations/cdserver/) on the SQLite database
|
||||
|
||||
### Database
|
||||
Darkflame Universe utilizes a MySQL/MariaDB database for account and character information.
|
||||
|
||||
Initial setup can vary drastically based on which operating system or distribution you are running; there are instructions out there for most setups, follow those and come back here when you have a database up and running.
|
||||
* Create a database for Darkflame Universe to use
|
||||
* Use the command `./MasterServer -m` to automatically run them.
|
||||
|
||||
**Configuration**
|
||||
|
||||
After the server has been built there should be four `ini` files in the build director: `authconfig.ini`, `chatconfig.ini`, `masterconfig.ini`, and `worldconfig.ini`. Go through them and fill in the database credentials and configure other settings if necessary.
|
||||
@ -396,20 +418,28 @@ Here is a summary of the commands available in-game. All commands are prefixed b
|
||||
</table>
|
||||
|
||||
## Credits
|
||||
### Contributors to DLUv3
|
||||
* DarwinAnim8or
|
||||
* Wincent01
|
||||
* Mick
|
||||
* averysumner
|
||||
* Jon002
|
||||
* Jonny
|
||||
* Xiphoseer
|
||||
## Active Contributors
|
||||
* [EmosewaMC](https://github.com/EmosewaMC)
|
||||
* [Jettford](https://github.com/Jettford)
|
||||
|
||||
## DLU Team
|
||||
* [DarwinAnim8or](https://github.com/DarwinAnim8or)
|
||||
* [Wincent01](https://github.com/Wincent01)
|
||||
* [Mick](https://github.com/MickVermeulen)
|
||||
* [averysumner](https://github.com/codeshaunted)
|
||||
* [Jon002](https://github.com/jaller200)
|
||||
* [Jonny](https://github.com/cuzitsjonny)
|
||||
* TheMachine
|
||||
* Matthew
|
||||
* [Raine](https://github.com/Rainebannister)
|
||||
* Bricknave
|
||||
|
||||
### Research and tools
|
||||
* lcdr
|
||||
* [lcdr](https://github.com/lcdr)
|
||||
* [Xiphoseer](https://github.com/Xiphoseer)
|
||||
|
||||
### Community management
|
||||
* Neal
|
||||
* [Neal](https://github.com/NealSpellman)
|
||||
|
||||
### Former contributors
|
||||
* TheMachine
|
||||
|
4
dAuthServer/CMakeLists.txt
Normal file
4
dAuthServer/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
set(DAUTHSERVER_SOURCES "AuthServer.cpp")
|
||||
|
||||
add_executable(AuthServer ${DAUTHSERVER_SOURCES})
|
||||
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
|
4
dChatFilter/CMakeLists.txt
Normal file
4
dChatFilter/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
set(DCHATFILTER_SOURCES "dChatFilter.cpp")
|
||||
|
||||
add_library(dChatFilter STATIC ${DCHATFILTER_SOURCES})
|
||||
target_link_libraries(dChatFilter dDatabase)
|
6
dChatServer/CMakeLists.txt
Normal file
6
dChatServer/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
set(DCHATSERVER_SOURCES "ChatPacketHandler.cpp"
|
||||
"ChatServer.cpp"
|
||||
"PlayerContainer.cpp")
|
||||
|
||||
add_executable(ChatServer ${DCHATSERVER_SOURCES})
|
||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter)
|
@ -8,6 +8,10 @@
|
||||
#include "dServer.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dLogger.h"
|
||||
#include "AddFriendResponseCode.h"
|
||||
#include "AddFriendResponseType.h"
|
||||
#include "RakString.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
extern PlayerContainer playerContainer;
|
||||
|
||||
@ -21,44 +25,41 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
auto player = playerContainer.GetPlayerData(playerID);
|
||||
if (!player) return;
|
||||
|
||||
//Get our friends list from the Db:
|
||||
auto stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE player_id = ? OR friend_id = ?");
|
||||
stmt->setUInt64(1, playerID);
|
||||
stmt->setUInt64(2, playerID);
|
||||
//Get our friends list from the Db. Using a derived table since the friend of a player can be in either column.
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt(
|
||||
"SELECT fr.requested_player, best_friend, ci.name FROM "
|
||||
"(SELECT CASE "
|
||||
"WHEN player_id = ? THEN friend_id "
|
||||
"WHEN friend_id = ? THEN player_id "
|
||||
"END AS requested_player, best_friend FROM friends) AS fr "
|
||||
"JOIN charinfo AS ci ON ci.id = fr.requested_player "
|
||||
"WHERE fr.requested_player IS NOT NULL;"));
|
||||
stmt->setUInt(1, static_cast<uint32_t>(playerID));
|
||||
stmt->setUInt(2, static_cast<uint32_t>(playerID));
|
||||
|
||||
std::vector<FriendData> friends;
|
||||
|
||||
auto res = stmt->executeQuery();
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
|
||||
while (res->next()) {
|
||||
FriendData fd;
|
||||
fd.isFTP = false; // not a thing in DLU
|
||||
fd.friendID = res->getInt64(1);
|
||||
if (fd.friendID == playerID) fd.friendID = res->getUInt64(2);
|
||||
fd.friendID = res->getUInt(1);
|
||||
GeneralUtils::SetBit(fd.friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
|
||||
GeneralUtils::SetBit(fd.friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
|
||||
|
||||
fd.isBestFriend = res->getInt(3) == 2; //0 = friends, 1 = requested, 2 = bffs
|
||||
|
||||
//We need to find their name as well:
|
||||
{
|
||||
auto stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE id=? limit 1");
|
||||
stmt->setInt(1, fd.friendID);
|
||||
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
fd.friendName = res->getString(1);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
}
|
||||
fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
if (fd.isBestFriend) player->countOfBestFriends+=1;
|
||||
fd.friendName = res->getString(3);
|
||||
|
||||
//Now check if they're online:
|
||||
auto fr = playerContainer.GetPlayerData(fd.friendID);
|
||||
|
||||
if (fr) {
|
||||
fd.isOnline = true;
|
||||
fd.zoneID = fr->zoneID;
|
||||
|
||||
//Since this friend is online, we need to update them on the fact that we've just logged in:
|
||||
SendFriendUpdate(fr, player, 1);
|
||||
SendFriendUpdate(fr, player, 1, fd.isBestFriend);
|
||||
}
|
||||
else {
|
||||
fd.isOnline = false;
|
||||
@ -68,9 +69,6 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
friends.push_back(fd);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//Now, we need to send the friendlist to the server they came from:
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER);
|
||||
@ -93,20 +91,150 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends");
|
||||
// If this config option doesn't exist, default to 5 which is what live used.
|
||||
auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U;
|
||||
CINSTREAM;
|
||||
LWOOBJID playerID;
|
||||
inStream.Read(playerID);
|
||||
inStream.Read(playerID);
|
||||
std::string playerName = PacketUtils::ReadString(0x14, packet, true);
|
||||
//There's another bool here to determine if it's a best friend request, but we're not handling it right now.
|
||||
LWOOBJID requestorPlayerID;
|
||||
inStream.Read(requestorPlayerID);
|
||||
inStream.Read(requestorPlayerID);
|
||||
uint32_t spacing{};
|
||||
inStream.Read(spacing);
|
||||
std::string playerName = "";
|
||||
uint16_t character;
|
||||
bool noMoreLettersInName = false;
|
||||
|
||||
//PacketUtils::SavePacket("FriendRequest.bin", (char*)inStream.GetData(), inStream.GetNumberOfBytesUsed());
|
||||
|
||||
//We need to check to see if the player is actually online or not:
|
||||
auto targetData = playerContainer.GetPlayerData(playerName);
|
||||
if (targetData) {
|
||||
SendFriendRequest(targetData, playerContainer.GetPlayerData(playerID));
|
||||
for (uint32_t j = 0; j < 33; j++) {
|
||||
inStream.Read(character);
|
||||
if (character == '\0') noMoreLettersInName = true;
|
||||
if (!noMoreLettersInName) playerName.push_back(static_cast<char>(character));
|
||||
}
|
||||
|
||||
char isBestFriendRequest{};
|
||||
inStream.Read(isBestFriendRequest);
|
||||
|
||||
auto requestor = playerContainer.GetPlayerData(requestorPlayerID);
|
||||
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName));
|
||||
|
||||
// Check if player is online first
|
||||
if (isBestFriendRequest && !requestee) {
|
||||
for (auto friendDataCandidate : requestor->friends) {
|
||||
if (friendDataCandidate.friendName == playerName) {
|
||||
requestee.reset(new PlayerData());
|
||||
// Setup the needed info since you can add a best friend offline.
|
||||
requestee->playerID = friendDataCandidate.friendID;
|
||||
requestee->playerName = friendDataCandidate.friendName;
|
||||
requestee->zoneID = LWOZONEID();
|
||||
|
||||
FriendData requesteeFriendData{};
|
||||
requesteeFriendData.friendID = requestor->playerID;
|
||||
requesteeFriendData.friendName = requestor->playerName;
|
||||
requesteeFriendData.isFTP = false;
|
||||
requesteeFriendData.isOnline = false;
|
||||
requesteeFriendData.zoneID = requestor->zoneID;
|
||||
requestee->friends.push_back(requesteeFriendData);
|
||||
requestee->sysAddr = UNASSIGNED_SYSTEM_ADDRESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If at this point we dont have a target, then they arent online and we cant send the request.
|
||||
// Send the response code that corresponds to what the error is.
|
||||
if (!requestee) {
|
||||
std::unique_ptr<sql::PreparedStatement> nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;"));
|
||||
nameQuery->setString(1, playerName);
|
||||
std::unique_ptr<sql::ResultSet> result(nameQuery->executeQuery());
|
||||
|
||||
requestee.reset(new PlayerData());
|
||||
requestee->playerName = playerName;
|
||||
|
||||
SendFriendResponse(requestor, requestee.get(), result->next() ? AddFriendResponseType::NOTONLINE : AddFriendResponseType::INVALIDCHARACTER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBestFriendRequest) {
|
||||
std::unique_ptr<sql::PreparedStatement> friendUpdate(Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
|
||||
friendUpdate->setUInt(1, static_cast<uint32_t>(requestorPlayerID));
|
||||
friendUpdate->setUInt(2, static_cast<uint32_t>(requestee->playerID));
|
||||
friendUpdate->setUInt(3, static_cast<uint32_t>(requestee->playerID));
|
||||
friendUpdate->setUInt(4, static_cast<uint32_t>(requestorPlayerID));
|
||||
std::unique_ptr<sql::ResultSet> result(friendUpdate->executeQuery());
|
||||
|
||||
LWOOBJID queryPlayerID = LWOOBJID_EMPTY;
|
||||
LWOOBJID queryFriendID = LWOOBJID_EMPTY;
|
||||
uint8_t oldBestFriendStatus{};
|
||||
uint8_t bestFriendStatus{};
|
||||
|
||||
if (result->next()) {
|
||||
// Get the IDs
|
||||
queryPlayerID = result->getInt(1);
|
||||
queryFriendID = result->getInt(2);
|
||||
oldBestFriendStatus = result->getInt(3);
|
||||
bestFriendStatus = oldBestFriendStatus;
|
||||
|
||||
// Set the bits
|
||||
GeneralUtils::SetBit(queryPlayerID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
|
||||
GeneralUtils::SetBit(queryPlayerID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
|
||||
GeneralUtils::SetBit(queryFriendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
|
||||
GeneralUtils::SetBit(queryFriendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
|
||||
|
||||
// Since this player can either be the friend of someone else or be friends with someone else
|
||||
// their column in the database determines what bit gets set. When the value hits 3, they
|
||||
// are now best friends with the other player.
|
||||
if (queryPlayerID == requestorPlayerID) {
|
||||
bestFriendStatus |= 1ULL << 0;
|
||||
} else {
|
||||
bestFriendStatus |= 1ULL << 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Only do updates if there was a change in the bff status.
|
||||
if (oldBestFriendStatus != bestFriendStatus) {
|
||||
if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
if (requestee->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
}
|
||||
if (requestor->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::YOURFRIENDSLISTFULL, false);
|
||||
}
|
||||
} else {
|
||||
// Then update the database with this new info.
|
||||
std::unique_ptr<sql::PreparedStatement> updateQuery(Database::CreatePreppedStmt("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
|
||||
updateQuery->setUInt(1, bestFriendStatus);
|
||||
updateQuery->setUInt(2, static_cast<uint32_t>(requestorPlayerID));
|
||||
updateQuery->setUInt(3, static_cast<uint32_t>(requestee->playerID));
|
||||
updateQuery->setUInt(4, static_cast<uint32_t>(requestee->playerID));
|
||||
updateQuery->setUInt(5, static_cast<uint32_t>(requestorPlayerID));
|
||||
updateQuery->executeUpdate();
|
||||
// Sent the best friend update here if the value is 3
|
||||
if (bestFriendStatus == 3U) {
|
||||
requestee->countOfBestFriends+=1;
|
||||
requestor->countOfBestFriends+=1;
|
||||
if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee.get(), requestor, AddFriendResponseType::ACCEPTED, false, true);
|
||||
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::ACCEPTED, false, true);
|
||||
for (auto& friendData : requestor->friends) {
|
||||
if (friendData.friendID == requestee->playerID) {
|
||||
friendData.isBestFriend = true;
|
||||
}
|
||||
}
|
||||
for (auto& friendData : requestee->friends) {
|
||||
if (friendData.friendID == requestor->playerID) {
|
||||
friendData.isBestFriend = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), AddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||
}
|
||||
} else {
|
||||
// Do not send this if we are requesting to be a best friend.
|
||||
SendFriendRequest(requestee.get(), requestor);
|
||||
}
|
||||
|
||||
// If the player is actually a player and not a ghost one defined above, release it from being deleted.
|
||||
if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) requestee.release();
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
|
||||
@ -114,30 +242,75 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
|
||||
LWOOBJID playerID;
|
||||
inStream.Read(playerID);
|
||||
inStream.Read(playerID);
|
||||
|
||||
uint8_t responseCode = packet->data[0x14];
|
||||
|
||||
AddFriendResponseCode clientResponseCode = static_cast<AddFriendResponseCode>(packet->data[0x14]);
|
||||
std::string friendName = PacketUtils::ReadString(0x15, packet, true);
|
||||
|
||||
Game::logger->Log("ChatPacketHandler", "Friend response code: %i\n", responseCode);
|
||||
|
||||
if (responseCode != 0) return; //If we're not accepting the request, end here, do not insert to friends table.
|
||||
|
||||
PacketUtils::SavePacket("HandleFriendResponse.bin", (char*)inStream.GetData(), inStream.GetNumberOfBytesUsed());
|
||||
|
||||
//Now to try and find both of these:
|
||||
auto goonA = playerContainer.GetPlayerData(playerID);
|
||||
auto goonB = playerContainer.GetPlayerData(friendName);
|
||||
if (!goonA || !goonB) return;
|
||||
auto requestor = playerContainer.GetPlayerData(playerID);
|
||||
auto requestee = playerContainer.GetPlayerData(friendName);
|
||||
if (!requestor || !requestee) return;
|
||||
|
||||
SendFriendResponse(goonB, goonA, responseCode);
|
||||
SendFriendResponse(goonA, goonB, responseCode); //Do we need to send it to both? I think so so both get the updated friendlist but... idk.
|
||||
AddFriendResponseType serverResponseCode{};
|
||||
uint8_t isAlreadyBestFriends = 0U;
|
||||
// We need to convert this response code to one we can actually send back to the client.
|
||||
switch (clientResponseCode) {
|
||||
case AddFriendResponseCode::ACCEPTED:
|
||||
serverResponseCode = AddFriendResponseType::ACCEPTED;
|
||||
break;
|
||||
case AddFriendResponseCode::BUSY:
|
||||
serverResponseCode = AddFriendResponseType::BUSY;
|
||||
break;
|
||||
case AddFriendResponseCode::CANCELLED:
|
||||
serverResponseCode = AddFriendResponseType::CANCELLED;
|
||||
break;
|
||||
case AddFriendResponseCode::REJECTED:
|
||||
serverResponseCode = AddFriendResponseType::DECLINED;
|
||||
break;
|
||||
}
|
||||
|
||||
auto stmt = Database::CreatePreppedStmt("INSERT INTO `friends`(`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?)");
|
||||
stmt->setUInt64(1, goonA->playerID);
|
||||
stmt->setUInt64(2, goonB->playerID);
|
||||
stmt->setInt(3, 0);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
// Now that we have handled the base cases, we need to check the other cases.
|
||||
if (serverResponseCode == AddFriendResponseType::ACCEPTED) {
|
||||
for (auto friendData : requestor->friends) {
|
||||
if (friendData.friendID == requestee->playerID) {
|
||||
serverResponseCode = AddFriendResponseType::ALREADYFRIEND;
|
||||
if (friendData.isBestFriend) {
|
||||
isAlreadyBestFriends = 1U;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This message is NOT sent for best friends and is handled differently for those requests.
|
||||
if (serverResponseCode == AddFriendResponseType::ACCEPTED) {
|
||||
// Add the each player to the others friend list.
|
||||
FriendData requestorData;
|
||||
requestorData.zoneID = requestor->zoneID;
|
||||
requestorData.friendID = requestor->playerID;
|
||||
requestorData.friendName = requestor->playerName;
|
||||
requestorData.isBestFriend = false;
|
||||
requestorData.isFTP = false;
|
||||
requestorData.isOnline = true;
|
||||
requestee->friends.push_back(requestorData);
|
||||
|
||||
FriendData requesteeData;
|
||||
requesteeData.zoneID = requestee->zoneID;
|
||||
requesteeData.friendID = requestee->playerID;
|
||||
requesteeData.friendName = requestee->playerName;
|
||||
requesteeData.isBestFriend = false;
|
||||
requesteeData.isFTP = false;
|
||||
requesteeData.isOnline = true;
|
||||
requestor->friends.push_back(requesteeData);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);"));
|
||||
statement->setUInt(1, static_cast<uint32_t>(requestor->playerID));
|
||||
statement->setUInt(2, static_cast<uint32_t>(requestee->playerID));
|
||||
statement->setInt(3, 0);
|
||||
statement->execute();
|
||||
}
|
||||
|
||||
if (serverResponseCode != AddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends);
|
||||
if (serverResponseCode != AddFriendResponseType::ALREADYFRIEND) SendFriendResponse(requestee, requestor, serverResponseCode, isAlreadyBestFriends);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
@ -145,50 +318,55 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
LWOOBJID playerID;
|
||||
inStream.Read(playerID);
|
||||
inStream.Read(playerID);
|
||||
std::string friendName = PacketUtils::ReadString(16, packet, true);
|
||||
std::string friendName = PacketUtils::ReadString(0x14, packet, true);
|
||||
|
||||
//we'll have to query the db here to find the user, since you can delete them while they're offline.
|
||||
//First, we need to find their ID:
|
||||
auto stmt = Database::CreatePreppedStmt("select id from charinfo where name=? limit 1;");
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;"));
|
||||
stmt->setString(1, friendName.c_str());
|
||||
|
||||
LWOOBJID friendID = 0;
|
||||
auto res = stmt->executeQuery();
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
|
||||
while (res->next()) {
|
||||
friendID = res->getUInt64(1);
|
||||
friendID = res->getUInt(1);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
// Convert friendID to LWOOBJID
|
||||
GeneralUtils::SetBit(friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_PERSISTENT));
|
||||
GeneralUtils::SetBit(friendID, static_cast<size_t>(eObjectBits::OBJECT_BIT_CHARACTER));
|
||||
|
||||
//Set our bits to convert to the BIG BOY objectID.
|
||||
friendID = GeneralUtils::ClearBit(friendID, OBJECT_BIT_CHARACTER);
|
||||
friendID = GeneralUtils::ClearBit(friendID, OBJECT_BIT_PERSISTENT);
|
||||
|
||||
//YEET:
|
||||
auto deletestmt = Database::CreatePreppedStmt("DELETE FROM `friends` WHERE player_id=? AND friend_id=? LIMIT 1");
|
||||
deletestmt->setUInt64(1, playerID);
|
||||
deletestmt->setUInt64(2, friendID);
|
||||
std::unique_ptr<sql::PreparedStatement> deletestmt(Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
|
||||
deletestmt->setUInt(1, static_cast<uint32_t>(playerID));
|
||||
deletestmt->setUInt(2, static_cast<uint32_t>(friendID));
|
||||
deletestmt->setUInt(3, static_cast<uint32_t>(friendID));
|
||||
deletestmt->setUInt(4, static_cast<uint32_t>(playerID));
|
||||
deletestmt->execute();
|
||||
delete deletestmt;
|
||||
|
||||
//because I'm lazy and they can be reversed:
|
||||
{
|
||||
auto deletestmt = Database::CreatePreppedStmt("DELETE FROM `friends` WHERE player_id=? AND friend_id=? LIMIT 1");
|
||||
deletestmt->setUInt64(1, friendID);
|
||||
deletestmt->setUInt64(2, playerID);
|
||||
deletestmt->execute();
|
||||
delete deletestmt;
|
||||
}
|
||||
|
||||
//Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended:
|
||||
auto goonA = playerContainer.GetPlayerData(playerID);
|
||||
if (goonA) {
|
||||
// Remove the friend from our list of friends
|
||||
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
|
||||
if ((*friendData).friendID == friendID) {
|
||||
if ((*friendData).isBestFriend) --goonA->countOfBestFriends;
|
||||
goonA->friends.erase(friendData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SendRemoveFriend(goonA, friendName, true);
|
||||
}
|
||||
|
||||
|
||||
auto goonB = playerContainer.GetPlayerData(friendID);
|
||||
if (!goonB) return;
|
||||
// Do it again for other person
|
||||
for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) {
|
||||
if ((*friendData).friendID == playerID) {
|
||||
if ((*friendData).isBestFriend) --goonB->countOfBestFriends;
|
||||
goonB->friends.erase(friendData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID));
|
||||
SendRemoveFriend(goonB, goonAName, true);
|
||||
}
|
||||
@ -206,7 +384,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet)
|
||||
|
||||
if (playerContainer.GetIsMuted(sender)) return;
|
||||
|
||||
const auto senderName = std::string(sender->playerName.C_String());
|
||||
const auto senderName = std::string(sender->playerName.c_str());
|
||||
|
||||
inStream.SetReadOffset(0x14 * 8);
|
||||
|
||||
@ -217,8 +395,6 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet)
|
||||
|
||||
Game::logger->Log("ChatPacketHandler", "Got a message from (%s) [%d]: %s\n", senderName.c_str(), channel, message.c_str());
|
||||
|
||||
//PacketUtils::SavePacket("chat.bin", reinterpret_cast<char*>(packet->data), packet->length);
|
||||
|
||||
if (channel != 8) return;
|
||||
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
@ -231,7 +407,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet)
|
||||
|
||||
if (otherMember == nullptr) return;
|
||||
|
||||
const auto otherName = std::string(otherMember->playerName.C_String());
|
||||
const auto otherName = std::string(otherMember->playerName.c_str());
|
||||
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER);
|
||||
@ -267,8 +443,8 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||
|
||||
if (playerContainer.GetIsMuted(goonA)) return;
|
||||
|
||||
std::string goonAName = goonA->playerName.C_String();
|
||||
std::string goonBName = goonB->playerName.C_String();
|
||||
std::string goonAName = goonA->playerName.c_str();
|
||||
std::string goonBName = goonB->playerName.c_str();
|
||||
|
||||
//To the sender:
|
||||
{
|
||||
@ -454,8 +630,6 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet)
|
||||
|
||||
playerContainer.RemoveMember(team, kickedId, false, true, false);
|
||||
}
|
||||
|
||||
//PacketUtils::SavePacket("kick.bin", reinterpret_cast<char*>(packet->data), packet->length);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamPromote(Packet* packet)
|
||||
@ -481,8 +655,6 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet)
|
||||
|
||||
playerContainer.PromoteMember(team, promoted->playerID);
|
||||
}
|
||||
|
||||
//PacketUtils::SavePacket("promote.bin", reinterpret_cast<char*>(packet->data), packet->length);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamLootOption(Packet* packet)
|
||||
@ -509,8 +681,6 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet)
|
||||
|
||||
playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
|
||||
//PacketUtils::SavePacket("option.bin", reinterpret_cast<char*>(packet->data), packet->length);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet)
|
||||
@ -550,7 +720,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet)
|
||||
|
||||
playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.C_String()));
|
||||
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.c_str()));
|
||||
|
||||
for (const auto memberId : team->memberIDs)
|
||||
{
|
||||
@ -560,7 +730,6 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet)
|
||||
|
||||
const auto memberName = playerContainer.GetName(memberId);
|
||||
|
||||
//ChatPacketHandler::SendTeamAddPlayer(otherMember, false, false, false, data->playerID, leaderName, data->zoneID);
|
||||
if (otherMember != nullptr)
|
||||
{
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID);
|
||||
@ -581,7 +750,7 @@ void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender)
|
||||
//portion that will get routed:
|
||||
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_TEAM_INVITE);
|
||||
|
||||
PacketUtils::WritePacketWString(sender->playerName.C_String(), 33, &bitStream);
|
||||
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
|
||||
bitStream.Write(sender->playerID);
|
||||
|
||||
SystemAddress sysAddr = receiver->sysAddr;
|
||||
@ -745,7 +914,7 @@ void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType) {
|
||||
void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend) {
|
||||
/*chat notification is displayed if log in / out and friend is updated in friends list
|
||||
[u8] - update type
|
||||
Update types
|
||||
@ -767,7 +936,7 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
|
||||
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_UPDATE_FRIEND_NOTIFY);
|
||||
bitStream.Write<uint8_t>(notifyType);
|
||||
|
||||
std::string playerName = playerData->playerName.C_String();
|
||||
std::string playerName = playerData->playerName.c_str();
|
||||
|
||||
PacketUtils::WritePacketWString(playerName, 33, &bitStream);
|
||||
|
||||
@ -783,19 +952,20 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
|
||||
bitStream.Write(playerData->zoneID.GetCloneID());
|
||||
}
|
||||
|
||||
bitStream.Write<uint8_t>(0); //isBFF
|
||||
bitStream.Write<uint8_t>(isBestFriend); //isBFF
|
||||
bitStream.Write<uint8_t>(0); //isFTP
|
||||
|
||||
SystemAddress sysAddr = friendData->sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* sender, bool isBFFReq) {
|
||||
void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* sender) {
|
||||
if (!receiver || !sender) return;
|
||||
|
||||
//Make sure people aren't requesting people that they're already friends with:
|
||||
for (auto fr : receiver->friends) {
|
||||
if (fr.friendID == sender->playerID) {
|
||||
SendFriendResponse(sender, receiver, AddFriendResponseType::ALREADYFRIEND, fr.isBestFriend);
|
||||
return; //we have this player as a friend, yeet this function so it doesn't send another request.
|
||||
}
|
||||
}
|
||||
@ -806,30 +976,34 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send
|
||||
|
||||
//portion that will get routed:
|
||||
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_ADD_FRIEND_REQUEST);
|
||||
PacketUtils::WritePacketWString(sender->playerName.C_String(), 33, &bitStream);
|
||||
bitStream.Write<uint8_t>(0);
|
||||
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
|
||||
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
|
||||
|
||||
SystemAddress sysAddr = receiver->sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sender, uint8_t responseCode) {
|
||||
void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sender, AddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
|
||||
if (!receiver || !sender) return;
|
||||
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER);
|
||||
bitStream.Write(receiver->playerID);
|
||||
|
||||
//portion that will get routed:
|
||||
// Portion that will get routed:
|
||||
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_ADD_FRIEND_RESPONSE);
|
||||
bitStream.Write<uint8_t>(responseCode);
|
||||
bitStream.Write<uint8_t>(1); //isOnline
|
||||
PacketUtils::WritePacketWString(sender->playerName.C_String(), 33, &bitStream);
|
||||
bitStream.Write(sender->playerID);
|
||||
bitStream.Write(sender->zoneID);
|
||||
bitStream.Write<uint8_t>(0); //isBFF
|
||||
bitStream.Write<uint8_t>(0); //isFTP
|
||||
|
||||
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 != AddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
|
||||
// Then write the player name
|
||||
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
|
||||
// Then if this is an acceptance code, write the following extra info.
|
||||
if (responseCode == AddFriendResponseType::ACCEPTED) {
|
||||
bitStream.Write(sender->playerID);
|
||||
bitStream.Write(sender->zoneID);
|
||||
bitStream.Write(isBestFriendRequest); //isBFF
|
||||
bitStream.Write<uint8_t>(0); //isFTP
|
||||
}
|
||||
SystemAddress sysAddr = receiver->sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "BitStream.h"
|
||||
|
||||
struct PlayerData;
|
||||
enum class AddFriendResponseType : uint8_t;
|
||||
|
||||
namespace ChatPacketHandler {
|
||||
void HandleFriendlistRequest(Packet* packet);
|
||||
@ -31,10 +32,9 @@ namespace ChatPacketHandler {
|
||||
void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
|
||||
|
||||
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
|
||||
void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType);
|
||||
void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend);
|
||||
|
||||
void SendFriendRequest(PlayerData* receiver, PlayerData* sender, bool isBFFReq = false);
|
||||
void SendFriendResponse(PlayerData* receiver, PlayerData* sender, uint8_t responseCode = 3);
|
||||
void SendFriendRequest(PlayerData* receiver, PlayerData* sender);
|
||||
void SendFriendResponse(PlayerData* receiver, PlayerData* sender, AddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
|
||||
void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful);
|
||||
};
|
||||
|
||||
|
@ -20,17 +20,25 @@ PlayerContainer::~PlayerContainer() {
|
||||
void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
CINSTREAM;
|
||||
PlayerData* data = new PlayerData();
|
||||
inStream.SetReadOffset(inStream.GetReadOffset() + 64);
|
||||
inStream.Read(data->playerID);
|
||||
inStream.Read(data->playerID);
|
||||
inStream.Read(data->playerName);
|
||||
|
||||
uint32_t len;
|
||||
inStream.Read<uint32_t>(len);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
char character; inStream.Read<char>(character);
|
||||
data->playerName += character;
|
||||
}
|
||||
|
||||
inStream.Read(data->zoneID);
|
||||
inStream.Read(data->muteExpire);
|
||||
data->sysAddr = packet->systemAddress;
|
||||
|
||||
mNames[data->playerID] = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.C_String()));
|
||||
mNames[data->playerID] = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.c_str()));
|
||||
|
||||
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i\n", data->playerName.C_String(), data->playerID, data->zoneID.GetMapID());
|
||||
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i\n", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
@ -49,26 +57,22 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
inStream.Read(playerID);
|
||||
|
||||
//Before they get kicked, we need to also send a message to their friends saying that they disconnected.
|
||||
auto player = this->GetPlayerData(playerID);
|
||||
std::unique_ptr<PlayerData> player(this->GetPlayerData(playerID));
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& fr : player->friends) {
|
||||
//if (!fr.isOnline) continue;
|
||||
|
||||
auto fd = this->GetPlayerData(fr.friendID);
|
||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0);
|
||||
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend);
|
||||
}
|
||||
|
||||
auto* team = GetTeam(playerID);
|
||||
|
||||
if (team != nullptr)
|
||||
{
|
||||
//TeamStatusUpdate(team);
|
||||
|
||||
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(player->playerName.C_String()));
|
||||
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(player->playerName.c_str()));
|
||||
|
||||
for (const auto memberId : team->memberIDs)
|
||||
{
|
||||
@ -77,7 +81,6 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
if (otherMember == nullptr) continue;
|
||||
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, {0, 0, 0});
|
||||
//ChatPacketHandler::SendTeamRemovePlayer(otherMember, false, false, true, false, team->leaderID, player->playerID, memberName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,16 +240,10 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID)
|
||||
|
||||
if (leader == nullptr || member == nullptr) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.C_String()));
|
||||
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(member->playerName.C_String()));
|
||||
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.c_str()));
|
||||
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(member->playerName.c_str()));
|
||||
|
||||
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName);
|
||||
|
||||
/*
|
||||
ChatPacketHandler::SendTeamAddPlayer(member, false, false, false, leader->playerID, leaderName, leader->zoneID);
|
||||
|
||||
Game::logger->Log("PlayerContainer", "Team invite successfully accepted, leader: %s, member: %s\n", leader->playerName.C_String(), member->playerName.C_String());
|
||||
*/
|
||||
|
||||
if (!team->local)
|
||||
{
|
||||
@ -348,7 +345,7 @@ void PlayerContainer::DisbandTeam(TeamData* team)
|
||||
|
||||
if (otherMember == nullptr) continue;
|
||||
|
||||
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(otherMember->playerName.C_String()));
|
||||
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(otherMember->playerName.c_str()));
|
||||
|
||||
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
|
||||
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName);
|
||||
@ -371,7 +368,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team)
|
||||
|
||||
if (leader == nullptr) return;
|
||||
|
||||
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.C_String()));
|
||||
const auto leaderName = GeneralUtils::ASCIIToUTF16(std::string(leader->playerName.c_str()));
|
||||
|
||||
for (const auto memberId : team->memberIDs)
|
||||
{
|
||||
@ -383,10 +380,6 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team)
|
||||
{
|
||||
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName);
|
||||
}
|
||||
else
|
||||
{
|
||||
//ChatPacketHandler::SendTeamStatus(otherMember, LWOOBJID_EMPTY, LWOZONEID(0, 0, 0), 1, 0, u"");
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTeamsOnWorld(team, false);
|
||||
|
@ -9,11 +9,12 @@
|
||||
|
||||
struct PlayerData {
|
||||
LWOOBJID playerID;
|
||||
RakNet::RakString playerName;
|
||||
std::string playerName;
|
||||
SystemAddress sysAddr;
|
||||
LWOZONEID zoneID;
|
||||
std::vector<FriendData> friends;
|
||||
time_t muteExpire;
|
||||
uint8_t countOfBestFriends = 0;
|
||||
};
|
||||
|
||||
struct TeamData {
|
||||
@ -45,7 +46,7 @@ public:
|
||||
PlayerData* GetPlayerData(const std::string& playerName) {
|
||||
for (auto player : mPlayers) {
|
||||
if (player.second) {
|
||||
std::string pn = player.second->playerName.C_String();
|
||||
std::string pn = player.second->playerName.c_str();
|
||||
if (pn == playerName) return player.second;
|
||||
}
|
||||
}
|
||||
|
15
dCommon/AddFriendResponseCode.h
Normal file
15
dCommon/AddFriendResponseCode.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __ADDFRIENDRESPONSECODE__H__
|
||||
#define __ADDFRIENDRESPONSECODE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class AddFriendResponseCode : uint8_t {
|
||||
ACCEPTED = 0,
|
||||
REJECTED,
|
||||
BUSY,
|
||||
CANCELLED
|
||||
};
|
||||
|
||||
#endif //!__ADDFRIENDRESPONSECODE__H__
|
24
dCommon/AddFriendResponseType.h
Normal file
24
dCommon/AddFriendResponseType.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __ADDFRIENDRESPONSETYPE__H__
|
||||
#define __ADDFRIENDRESPONSETYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class AddFriendResponseType : uint8_t {
|
||||
ACCEPTED = 0,
|
||||
ALREADYFRIEND,
|
||||
INVALIDCHARACTER,
|
||||
GENERALERROR,
|
||||
YOURFRIENDSLISTFULL,
|
||||
THEIRFRIENDLISTFULL,
|
||||
DECLINED,
|
||||
BUSY,
|
||||
NOTONLINE,
|
||||
WAITINGAPPROVAL,
|
||||
MYTHRAN,
|
||||
CANCELLED,
|
||||
FRIENDISFREETRIAL
|
||||
};
|
||||
|
||||
#endif //!__ADDFRIENDRESPONSETYPE__H__
|
26
dCommon/CMakeLists.txt
Normal file
26
dCommon/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
set(DCOMMON_SOURCES "AMFFormat.cpp"
|
||||
"AMFFormat_BitStream.cpp"
|
||||
"BinaryIO.cpp"
|
||||
"dConfig.cpp"
|
||||
"Diagnostics.cpp"
|
||||
"dLogger.cpp"
|
||||
"GeneralUtils.cpp"
|
||||
"LDFFormat.cpp"
|
||||
"MD5.cpp"
|
||||
"Metrics.cpp"
|
||||
"NiPoint3.cpp"
|
||||
"NiQuaternion.cpp"
|
||||
"SHA512.cpp"
|
||||
"Type.cpp"
|
||||
"ZCompression.cpp")
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
|
||||
|
||||
add_library(dCommon STATIC ${DCOMMON_SOURCES})
|
||||
|
||||
target_link_libraries(dCommon libbcrypt)
|
||||
|
||||
if (UNIX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_link_libraries(dCommon ZLIB::ZLIB)
|
||||
endif()
|
@ -188,3 +188,53 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream *inStream) {
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
std::vector<std::string> GeneralUtils::GetFileNamesFromFolder(const std::string& folder)
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
std::string search_path = folder + "/*.*";
|
||||
WIN32_FIND_DATA fd;
|
||||
HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
names.push_back(fd.cFileName);
|
||||
}
|
||||
} while (::FindNextFile(hFind, &fd));
|
||||
::FindClose(hFind);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
std::vector<std::string> GeneralUtils::GetFileNamesFromFolder(const std::string& folder) {
|
||||
std::vector<std::string> names;
|
||||
struct dirent* entry;
|
||||
DIR* dir = opendir(folder.c_str());
|
||||
if (dir == NULL) {
|
||||
return names;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
std::string value(entry->d_name, strlen(entry->d_name));
|
||||
if (value == "." || value == "..") {
|
||||
continue;
|
||||
}
|
||||
names.push_back(value);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return names;
|
||||
}
|
||||
#endif
|
@ -128,6 +128,8 @@ namespace GeneralUtils {
|
||||
|
||||
std::vector<std::string> SplitString(const std::string& str, char delimiter);
|
||||
|
||||
std::vector<std::string> GetFileNamesFromFolder(const std::string& folder);
|
||||
|
||||
template <typename T>
|
||||
T Parse(const char* value);
|
||||
|
||||
|
@ -54,7 +54,7 @@ LDFBaseData * LDFBaseData::DataFromString(const std::string& format) {
|
||||
}
|
||||
|
||||
case LDF_TYPE_S32: {
|
||||
int32_t data = static_cast<int32_t>(stol(dataArray[1]));
|
||||
int32_t data = static_cast<int32_t>(stoull(dataArray[1]));
|
||||
return new LDFData<int32_t>(key, data);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "ZCompression.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
namespace ZCompression
|
||||
@ -70,4 +72,6 @@ namespace ZCompression
|
||||
return(nRet); // -1 or len of output
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -2,6 +2,10 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dPlatforms.h"
|
||||
|
||||
#ifndef DARKFLAME_PLATFORM_WIN32
|
||||
|
||||
namespace ZCompression
|
||||
{
|
||||
int32_t GetMaxCompressedLength(int32_t nLenSrc);
|
||||
@ -10,3 +14,5 @@ namespace ZCompression
|
||||
|
||||
int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -407,14 +407,20 @@ enum eReplicaComponentType : int32_t {
|
||||
COMPONENT_TYPE_MISSION = 84, //!< The Mission Component
|
||||
COMPONENT_TYPE_ROCKET_LAUNCH_LUP = 97, //!< The LUP Launchpad Componen
|
||||
COMPONENT_TYPE_RAIL_ACTIVATOR = 104,
|
||||
COMPONENT_TYPE_POSSESSOR = 107, //!< The Component 107
|
||||
COMPONENT_TYPE_POSSESSABLE = 108, //!< The Component 108
|
||||
COMPONENT_TYPE_POSSESSABLE = 108, //!< The Possessable Component
|
||||
COMPONENT_TYPE_POSSESSOR = 110, //!< The Possessor Component
|
||||
COMPONENT_TYPE_BUILD_BORDER = 114, //!< The Build Border Component
|
||||
COMPONENT_TYPE_DESTROYABLE = 1000, //!< The Destroyable Component
|
||||
|
||||
COMPONENT_TYPE_MODEL = 5398484 //look man idk
|
||||
};
|
||||
|
||||
enum class UseItemResponse : uint32_t {
|
||||
NoImaginationForPet = 1,
|
||||
FailedPrecondition,
|
||||
MountsNotAllowed
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the different types of inventories an entity may have
|
||||
*/
|
||||
@ -434,33 +440,6 @@ enum eInventoryType : uint32_t {
|
||||
INVALID // made up, for internal use!!!
|
||||
};
|
||||
|
||||
enum eItemType : int32_t {
|
||||
ITEM_TYPE_UNKNOWN = -1, //!< An unknown item type
|
||||
ITEM_TYPE_BRICK = 1, //!< A brick
|
||||
ITEM_TYPE_HAT = 2, //!< A hat / head item
|
||||
ITEM_TYPE_HAIR = 3, //!< A hair item
|
||||
ITEM_TYPE_NECK = 4, //!< A neck item
|
||||
ITEM_TYPE_LEFT_HAND = 5, //!< A left handed item
|
||||
ITEM_TYPE_RIGHT_HAND = 6, //!< A right handed item
|
||||
ITEM_TYPE_LEGS = 7, //!< A pants item
|
||||
ITEM_TYPE_LEFT_TRINKET = 8, //!< A left handled trinket item
|
||||
ITEM_TYPE_RIGHT_TRINKET = 9, //!< A right handed trinket item
|
||||
ITEM_TYPE_BEHAVIOR = 10, //!< A behavior
|
||||
ITEM_TYPE_PROPERTY = 11, //!< A property
|
||||
ITEM_TYPE_MODEL = 12, //!< A model
|
||||
ITEM_TYPE_COLLECTIBLE = 13, //!< A collectible item
|
||||
ITEM_TYPE_CONSUMABLE = 14, //!< A consumable item
|
||||
ITEM_TYPE_CHEST = 15, //!< A chest item
|
||||
ITEM_TYPE_EGG = 16, //!< An egg
|
||||
ITEM_TYPE_PET_FOOD = 17, //!< A pet food item
|
||||
ITEM_TYPE_QUEST_OBJECT = 18, //!< A quest item
|
||||
ITEM_TYPE_PET_INVENTORY_ITEM = 19, //!< A pet inventory item
|
||||
ITEM_TYPE_PACKAGE = 20, //!< A package
|
||||
ITEM_TYPE_LOOT_MODEL = 21, //!< A loot model
|
||||
ITEM_TYPE_VEHICLE = 22, //!< A vehicle
|
||||
ITEM_TYPE_CURRENCY = 23 //!< Currency
|
||||
};
|
||||
|
||||
enum eRebuildState : uint32_t {
|
||||
REBUILD_OPEN,
|
||||
REBUILD_COMPLETED = 2,
|
||||
@ -658,7 +637,6 @@ enum ePlayerFlags {
|
||||
NJ_WU_SHOW_DAILY_CHEST = 2099
|
||||
};
|
||||
|
||||
|
||||
//======== FUNC ===========
|
||||
|
||||
template<typename T>
|
||||
|
29
dCommon/dPlatforms.h
Normal file
29
dCommon/dPlatforms.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define DARKFLAME_PLATFORM_WIN32
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
||||
#define DARKFLAME_PLATFORM_IOS
|
||||
#elif TARGET_OS_MAC
|
||||
#define DARKFLAME_PLATFORM_MACOS
|
||||
#else
|
||||
#error unknown Apple operating system
|
||||
#endif
|
||||
#elif defined(__unix__)
|
||||
#define DARKFLAME_PLATFORM_UNIX
|
||||
#if defined(__ANDROID__)
|
||||
#define DARKFLAME_PLATFORM_ANDROID
|
||||
#elif defined(__linux__)
|
||||
#define DARKFLAME_PLATFORM_LINUX
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#define DARKFLAME_PLATFORM_FREEBSD
|
||||
#elif defined(__CYGWIN__)
|
||||
#define DARKFLAME_PLATFORM_CYGWIN
|
||||
#else
|
||||
#error unknown unix operating system
|
||||
#endif
|
||||
#else
|
||||
#error unknown operating system
|
||||
#endif
|
44
dCommon/eAninmationFlags.h
Normal file
44
dCommon/eAninmationFlags.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __EANINMATIONFLAGS__H__
|
||||
#define __EANINMATIONFLAGS__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eAnimationFlags : uint32_t {
|
||||
IDLE_INVALID = 0, // made up, for internal use!!!
|
||||
IDLE_BASIC,
|
||||
IDLE_SWIM,
|
||||
IDLE_CARRY,
|
||||
IDLE_SWORD,
|
||||
IDLE_HAMMER,
|
||||
IDLE_SPEAR,
|
||||
IDLE_PISTOL,
|
||||
IDLE_BOW,
|
||||
IDLE_COMBAT,
|
||||
IDLE_JETPACK,
|
||||
IDLE_HORSE,
|
||||
IDLE_SG,
|
||||
IDLE_ORGAN,
|
||||
IDLE_SKATEBOARD,
|
||||
IDLE_DAREDEVIL,
|
||||
IDLE_SAMURAI,
|
||||
IDLE_SUMMONER,
|
||||
IDLE_BUCCANEER,
|
||||
IDLE_MISC,
|
||||
IDLE_NINJA,
|
||||
IDLE_MISC1,
|
||||
IDLE_MISC2,
|
||||
IDLE_MISC3,
|
||||
IDLE_MISC4,
|
||||
IDLE_MISC5,
|
||||
IDLE_MISC6,
|
||||
IDLE_MISC7,
|
||||
IDLE_MISC8,
|
||||
IDLE_MISC9,
|
||||
IDLE_MISC10,
|
||||
IDLE_MISC11,
|
||||
IDLE_MISC12
|
||||
};
|
||||
|
||||
#endif //!__EANINMATIONFLAGS__H__
|
36
dCommon/eItemType.h
Normal file
36
dCommon/eItemType.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __EITEMTYPE__H__
|
||||
#define __EITEMTYPE__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eItemType : int32_t {
|
||||
ITEM_TYPE_UNKNOWN = -1, //!< An unknown item type
|
||||
ITEM_TYPE_BRICK = 1, //!< A brick
|
||||
ITEM_TYPE_HAT = 2, //!< A hat / head item
|
||||
ITEM_TYPE_HAIR = 3, //!< A hair item
|
||||
ITEM_TYPE_NECK = 4, //!< A neck item
|
||||
ITEM_TYPE_LEFT_HAND = 5, //!< A left handed item
|
||||
ITEM_TYPE_RIGHT_HAND = 6, //!< A right handed item
|
||||
ITEM_TYPE_LEGS = 7, //!< A pants item
|
||||
ITEM_TYPE_LEFT_TRINKET = 8, //!< A left handled trinket item
|
||||
ITEM_TYPE_RIGHT_TRINKET = 9, //!< A right handed trinket item
|
||||
ITEM_TYPE_BEHAVIOR = 10, //!< A behavior
|
||||
ITEM_TYPE_PROPERTY = 11, //!< A property
|
||||
ITEM_TYPE_MODEL = 12, //!< A model
|
||||
ITEM_TYPE_COLLECTIBLE = 13, //!< A collectible item
|
||||
ITEM_TYPE_CONSUMABLE = 14, //!< A consumable item
|
||||
ITEM_TYPE_CHEST = 15, //!< A chest item
|
||||
ITEM_TYPE_EGG = 16, //!< An egg
|
||||
ITEM_TYPE_PET_FOOD = 17, //!< A pet food item
|
||||
ITEM_TYPE_QUEST_OBJECT = 18, //!< A quest item
|
||||
ITEM_TYPE_PET_INVENTORY_ITEM = 19, //!< A pet inventory item
|
||||
ITEM_TYPE_PACKAGE = 20, //!< A package
|
||||
ITEM_TYPE_LOOT_MODEL = 21, //!< A loot model
|
||||
ITEM_TYPE_VEHICLE = 22, //!< A vehicle
|
||||
ITEM_TYPE_CURRENCY = 23, //!< Currency
|
||||
ITEM_TYPE_MOUNT = 24 //!< A Mount
|
||||
};
|
||||
|
||||
#endif //!__EITEMTYPE__H__
|
@ -13,3 +13,8 @@ void CDClientDatabase::Connect(const std::string& filename) {
|
||||
CppSQLite3Query CDClientDatabase::ExecuteQuery(const std::string& query) {
|
||||
return conn->execQuery(query.c_str());
|
||||
}
|
||||
|
||||
//! Makes prepared statements
|
||||
CppSQLite3Statement CDClientDatabase::CreatePreppedStmt(const std::string& query) {
|
||||
return conn->compileStatement(query.c_str());
|
||||
}
|
||||
|
@ -40,4 +40,10 @@ namespace CDClientDatabase {
|
||||
*/
|
||||
CppSQLite3Query ExecuteQuery(const std::string& query);
|
||||
|
||||
//! Queries the CDClient and parses arguments
|
||||
/*!
|
||||
\param query The query with formatted arguments
|
||||
\return prepared SQLite Statement
|
||||
*/
|
||||
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ void CDClientManager::Initialize(void) {
|
||||
tables.insert(std::make_pair("ActivityRewards", new CDActivityRewardsTable()));
|
||||
UNUSED(tables.insert(std::make_pair("Animations", new CDAnimationsTable())));
|
||||
tables.insert(std::make_pair("BehaviorParameter", new CDBehaviorParameterTable()));
|
||||
UNUSED(tables.insert(std::make_pair("BehaviorTemplate", new CDBehaviorTemplateTable())));
|
||||
tables.insert(std::make_pair("BehaviorTemplate", new CDBehaviorTemplateTable()));
|
||||
tables.insert(std::make_pair("ComponentsRegistry", new CDComponentsRegistryTable()));
|
||||
tables.insert(std::make_pair("CurrencyTable", new CDCurrencyTableTable()));
|
||||
tables.insert(std::make_pair("DestructibleComponent", new CDDestructibleComponentTable()));
|
||||
|
13
dDatabase/CMakeLists.txt
Normal file
13
dDatabase/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
set(DDATABASE_SOURCES "CDClientDatabase.cpp"
|
||||
"CDClientManager.cpp"
|
||||
"Database.cpp"
|
||||
"MigrationRunner.cpp")
|
||||
|
||||
add_subdirectory(Tables)
|
||||
|
||||
foreach(file ${DDATABASE_TABLES_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
|
||||
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
|
@ -8,9 +8,10 @@ using namespace std;
|
||||
|
||||
sql::Driver * Database::driver;
|
||||
sql::Connection * Database::con;
|
||||
sql::Properties Database::props;
|
||||
std::string Database::database;
|
||||
|
||||
void Database::Connect(const string& host, const string& database, const string& username, const string& password) {
|
||||
driver = get_driver_instance();
|
||||
|
||||
//To bypass debug issues:
|
||||
std::string newHost = "tcp://" + host;
|
||||
@ -19,17 +20,33 @@ void Database::Connect(const string& host, const string& database, const string&
|
||||
const char* szUsername = username.c_str();
|
||||
const char* szPassword = password.c_str();
|
||||
|
||||
con = driver->connect(szHost, szUsername, szPassword);
|
||||
con->setSchema(szDatabase);
|
||||
driver = sql::mariadb::get_driver_instance();
|
||||
|
||||
bool myTrue = true;
|
||||
con->setClientOption("MYSQL_OPT_RECONNECT", &myTrue);
|
||||
} //Connect
|
||||
sql::Properties properties;
|
||||
properties["hostName"] = szHost;
|
||||
properties["user"] = szUsername;
|
||||
properties["password"] = szPassword;
|
||||
properties["autoReconnect"] = "true";
|
||||
|
||||
void Database::Destroy(std::string source) {
|
||||
Database::props = properties;
|
||||
Database::database = database;
|
||||
|
||||
Database::Connect();
|
||||
}
|
||||
|
||||
void Database::Connect() {
|
||||
con = driver->connect(Database::props);
|
||||
con->setSchema(Database::database);
|
||||
}
|
||||
|
||||
void Database::Destroy(std::string source, bool log) {
|
||||
if (!con) return;
|
||||
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!\n", source.c_str());
|
||||
else Game::logger->Log("Database", "Destroying MySQL connection!\n");
|
||||
|
||||
if (log) {
|
||||
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!\n", source.c_str());
|
||||
else Game::logger->Log("Database", "Destroying MySQL connection!\n");
|
||||
}
|
||||
|
||||
con->close();
|
||||
delete con;
|
||||
} //Destroy
|
||||
@ -45,13 +62,7 @@ sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
|
||||
sql::SQLString str(test, size);
|
||||
|
||||
if (!con) {
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
|
||||
Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
Connect();
|
||||
Game::logger->Log("Database", "Trying to reconnect to MySQL\n");
|
||||
}
|
||||
|
||||
@ -61,17 +72,15 @@ sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
|
||||
|
||||
con = nullptr;
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
|
||||
Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
Connect();
|
||||
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection\n");
|
||||
}
|
||||
|
||||
auto* stmt = con->prepareStatement(str);
|
||||
|
||||
return stmt;
|
||||
} //CreatePreppedStmt
|
||||
} //CreatePreppedStmt
|
||||
|
||||
void Database::Commit() {
|
||||
Database::con->commit();
|
||||
}
|
@ -1,13 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <mysql_connection.h>
|
||||
#include <cppconn/driver.h>
|
||||
#include <cppconn/exception.h>
|
||||
#include <cppconn/resultset.h>
|
||||
#include <cppconn/statement.h>
|
||||
#include <cppconn/prepared_statement.h>
|
||||
#include <cppconn/sqlstring.h>
|
||||
#include <conncpp.hpp>
|
||||
|
||||
class MySqlException : public std::runtime_error {
|
||||
public:
|
||||
@ -19,10 +13,17 @@ class Database {
|
||||
private:
|
||||
static sql::Driver *driver;
|
||||
static sql::Connection *con;
|
||||
|
||||
static sql::Properties props;
|
||||
static std::string database;
|
||||
public:
|
||||
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
|
||||
static void Destroy(std::string source="");
|
||||
static void Connect();
|
||||
static void Destroy(std::string source = "", bool log = true);
|
||||
|
||||
static sql::Statement* CreateStmt();
|
||||
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||
static void Commit();
|
||||
|
||||
static std::string GetDatabase() { return database; }
|
||||
static sql::Properties GetProperties() { return props; }
|
||||
};
|
||||
|
78
dDatabase/MigrationRunner.cpp
Normal file
78
dDatabase/MigrationRunner.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "MigrationRunner.h"
|
||||
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
void MigrationRunner::RunMigrations() {
|
||||
auto stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
|
||||
stmt->executeQuery();
|
||||
delete stmt;
|
||||
|
||||
sql::SQLString finalSQL = "";
|
||||
Migration checkMigration{};
|
||||
|
||||
for (const auto& entry : GeneralUtils::GetFileNamesFromFolder("./migrations/")) {
|
||||
auto migration = LoadMigration(entry);
|
||||
|
||||
if (migration.data.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
checkMigration = migration;
|
||||
|
||||
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
stmt->setString(1, migration.name);
|
||||
auto res = stmt->executeQuery();
|
||||
bool doExit = res->next();
|
||||
delete res;
|
||||
delete stmt;
|
||||
if (doExit) continue;
|
||||
|
||||
Game::logger->Log("MigrationRunner", "Running migration: " + migration.name + "\n");
|
||||
|
||||
finalSQL.append(migration.data);
|
||||
finalSQL.append('\n');
|
||||
|
||||
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
stmt->setString(1, entry);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
if (!finalSQL.empty()) {
|
||||
try {
|
||||
auto simpleStatement = Database::CreateStmt();
|
||||
simpleStatement->execute(finalSQL);
|
||||
delete simpleStatement;
|
||||
}
|
||||
catch (sql::SQLException e) {
|
||||
Game::logger->Log("MigrationRunner", std::string("Encountered error running migration: ") + e.what() + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Migration MigrationRunner::LoadMigration(std::string path) {
|
||||
Migration migration{};
|
||||
std::ifstream file("./migrations/" + path);
|
||||
|
||||
if (file.is_open()) {
|
||||
std::hash<std::string> hash;
|
||||
|
||||
std::string line;
|
||||
std::string total = "";
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
total += line;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
migration.name = path;
|
||||
migration.data = total;
|
||||
}
|
||||
|
||||
return migration;
|
||||
}
|
19
dDatabase/MigrationRunner.h
Normal file
19
dDatabase/MigrationRunner.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Database.h"
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include "Game.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
struct Migration {
|
||||
std::string data;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class MigrationRunner {
|
||||
public:
|
||||
static void RunMigrations();
|
||||
static Migration LoadMigration(std::string path);
|
||||
};
|
@ -3,33 +3,31 @@
|
||||
|
||||
//! Constructor
|
||||
CDBehaviorParameterTable::CDBehaviorParameterTable(void) {
|
||||
#ifdef CDCLIENT_CACHE_ALL
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
|
||||
while (!tableData.eof()) {
|
||||
CDBehaviorParameter entry;
|
||||
entry.behaviorID = tableData.getIntField(0, -1);
|
||||
entry.parameterID = tableData.getStringField(1, "");
|
||||
entry.value = tableData.getFloatField(2, -1.0f);
|
||||
|
||||
//Check if we have an entry with this ID:
|
||||
auto it = m_entries.find(entry.behaviorID);
|
||||
if (it != m_entries.end()) {
|
||||
it->second.insert(std::make_pair(entry.parameterID, entry.value));
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
|
||||
size_t hash = 0;
|
||||
while (!tableData.eof()) {
|
||||
hash = 0;
|
||||
CDBehaviorParameter entry;
|
||||
entry.behaviorID = tableData.getIntField(0, -1);
|
||||
auto candidateStringToAdd = std::string(tableData.getStringField(1, ""));
|
||||
auto parameter = m_ParametersList.find(candidateStringToAdd);
|
||||
if (parameter != m_ParametersList.end()) {
|
||||
entry.parameterID = parameter;
|
||||
} else {
|
||||
entry.parameterID = m_ParametersList.insert(candidateStringToAdd).first;
|
||||
}
|
||||
else {
|
||||
//Otherwise, insert it:
|
||||
m_entries.insert(std::make_pair(entry.behaviorID, std::map<std::string, float>()));
|
||||
auto jit = m_entries.find(entry.behaviorID);
|
||||
entry.value = tableData.getFloatField(2, -1.0f);
|
||||
|
||||
//Add our value as well:
|
||||
jit->second.insert(std::make_pair(entry.parameterID, entry.value));
|
||||
}
|
||||
GeneralUtils::hash_combine(hash, entry.behaviorID);
|
||||
GeneralUtils::hash_combine(hash, *entry.parameterID);
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
auto it = m_Entries.find(entry.behaviorID);
|
||||
m_ParametersList.insert(*entry.parameterID);
|
||||
m_Entries.insert(std::make_pair(hash, entry));
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
tableData.finalize();
|
||||
#endif
|
||||
}
|
||||
|
||||
//! Destructor
|
||||
@ -37,54 +35,36 @@ CDBehaviorParameterTable::~CDBehaviorParameterTable(void) { }
|
||||
|
||||
//! Returns the table's name
|
||||
std::string CDBehaviorParameterTable::GetName(void) const {
|
||||
return "BehaviorParameter";
|
||||
return "BehaviorParameter";
|
||||
}
|
||||
|
||||
float CDBehaviorParameterTable::GetEntry(const uint32_t behaviorID, const std::string& name, const float defaultValue)
|
||||
CDBehaviorParameter CDBehaviorParameterTable::GetEntry(const uint32_t behaviorID, const std::string& name, const float defaultValue)
|
||||
{
|
||||
CDBehaviorParameter returnValue;
|
||||
returnValue.behaviorID = 0;
|
||||
returnValue.parameterID = m_ParametersList.end();
|
||||
returnValue.value = defaultValue;
|
||||
|
||||
size_t hash = 0;
|
||||
GeneralUtils::hash_combine(hash, behaviorID);
|
||||
GeneralUtils::hash_combine(hash, name);
|
||||
|
||||
// Search for specific parameter
|
||||
const auto& it = m_Entries.find(hash);
|
||||
if (it != m_Entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Check if this behavior has already been checked
|
||||
const auto& itChecked = m_Entries.find(behaviorID);
|
||||
if (itChecked != m_Entries.end()) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
#ifndef CDCLIENT_CACHE_ALL
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT parameterID, value FROM BehaviorParameter WHERE behaviorID = " << std::to_string(behaviorID);
|
||||
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
|
||||
|
||||
m_Entries.insert_or_assign(behaviorID, 0);
|
||||
|
||||
while (!tableData.eof()) {
|
||||
const std::string parameterID = tableData.getStringField(0, "");
|
||||
const float value = tableData.getFloatField(1, 0);
|
||||
|
||||
size_t parameterHash = 0;
|
||||
GeneralUtils::hash_combine(parameterHash, behaviorID);
|
||||
GeneralUtils::hash_combine(parameterHash, parameterID);
|
||||
|
||||
m_Entries.insert_or_assign(parameterHash, value);
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
const auto& it2 = m_Entries.find(hash);
|
||||
if (it2 != m_Entries.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
#endif
|
||||
|
||||
return defaultValue;
|
||||
return it != m_Entries.end() ? it->second : returnValue;
|
||||
}
|
||||
|
||||
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
|
||||
size_t hash;
|
||||
std::map<std::string, float> returnInfo;
|
||||
for (auto parameterCandidate : m_ParametersList) {
|
||||
hash = 0;
|
||||
GeneralUtils::hash_combine(hash, behaviorID);
|
||||
GeneralUtils::hash_combine(hash, parameterCandidate);
|
||||
auto infoCandidate = m_Entries.find(hash);
|
||||
if (infoCandidate != m_Entries.end()) {
|
||||
returnInfo.insert(std::make_pair(*(infoCandidate->second.parameterID), infoCandidate->second.value));
|
||||
}
|
||||
}
|
||||
return returnInfo;
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
/*!
|
||||
\file CDBehaviorParameterTable.hpp
|
||||
@ -11,16 +12,16 @@
|
||||
|
||||
//! BehaviorParameter Entry Struct
|
||||
struct CDBehaviorParameter {
|
||||
unsigned int behaviorID; //!< The Behavior ID
|
||||
std::string parameterID; //!< The Parameter ID
|
||||
float value; //!< The value of the behavior template
|
||||
unsigned int behaviorID; //!< The Behavior ID
|
||||
std::unordered_set<std::string>::iterator parameterID; //!< The Parameter ID
|
||||
float value; //!< The value of the behavior template
|
||||
};
|
||||
|
||||
//! BehaviorParameter table
|
||||
class CDBehaviorParameterTable : public CDTable {
|
||||
private:
|
||||
std::map<size_t, float> m_Entries;
|
||||
|
||||
std::unordered_map<size_t, CDBehaviorParameter> m_Entries;
|
||||
std::unordered_set<std::string> m_ParametersList;
|
||||
public:
|
||||
|
||||
//! Constructor
|
||||
@ -35,5 +36,7 @@ public:
|
||||
*/
|
||||
std::string GetName(void) const override;
|
||||
|
||||
float GetEntry(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0);
|
||||
CDBehaviorParameter GetEntry(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0);
|
||||
|
||||
std::map<std::string, float> GetParametersByBehaviorID(uint32_t behaviorID);
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) {
|
||||
|
||||
// Reserve the size
|
||||
this->entries.reserve(size);
|
||||
|
||||
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorTemplate");
|
||||
while (!tableData.eof()) {
|
||||
@ -24,9 +24,16 @@ CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) {
|
||||
entry.behaviorID = tableData.getIntField(0, -1);
|
||||
entry.templateID = tableData.getIntField(1, -1);
|
||||
entry.effectID = tableData.getIntField(2, -1);
|
||||
entry.effectHandle = tableData.getStringField(3, "");
|
||||
auto candidateToAdd = tableData.getStringField(3, "");
|
||||
auto parameter = m_EffectHandles.find(candidateToAdd);
|
||||
if (parameter != m_EffectHandles.end()) {
|
||||
entry.effectHandle = parameter;
|
||||
} else {
|
||||
entry.effectHandle = m_EffectHandles.insert(candidateToAdd).first;
|
||||
}
|
||||
|
||||
this->entries.push_back(entry);
|
||||
this->entriesMappedByBehaviorID.insert(std::make_pair(entry.behaviorID, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
@ -55,3 +62,16 @@ std::vector<CDBehaviorTemplate> CDBehaviorTemplateTable::Query(std::function<boo
|
||||
std::vector<CDBehaviorTemplate> CDBehaviorTemplateTable::GetEntries(void) const {
|
||||
return this->entries;
|
||||
}
|
||||
|
||||
const CDBehaviorTemplate CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behaviorID) {
|
||||
auto entry = this->entriesMappedByBehaviorID.find(behaviorID);
|
||||
if (entry == this->entriesMappedByBehaviorID.end()) {
|
||||
CDBehaviorTemplate entryToReturn;
|
||||
entryToReturn.behaviorID = 0;
|
||||
entryToReturn.effectHandle = m_EffectHandles.end();
|
||||
entryToReturn.effectID = 0;
|
||||
return entryToReturn;
|
||||
} else {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
/*!
|
||||
\file CDBehaviorTemplateTable.hpp
|
||||
@ -10,10 +12,10 @@
|
||||
|
||||
//! BehaviorTemplate Entry Struct
|
||||
struct CDBehaviorTemplate {
|
||||
unsigned int behaviorID; //!< The Behavior ID
|
||||
unsigned int templateID; //!< The Template ID (LOT)
|
||||
unsigned int effectID; //!< The Effect ID attached
|
||||
std::string effectHandle; //!< The effect handle
|
||||
unsigned int behaviorID; //!< The Behavior ID
|
||||
unsigned int templateID; //!< The Template ID (LOT)
|
||||
unsigned int effectID; //!< The Effect ID attached
|
||||
std::unordered_set<std::string>::iterator effectHandle; //!< The effect handle
|
||||
};
|
||||
|
||||
|
||||
@ -21,7 +23,8 @@ struct CDBehaviorTemplate {
|
||||
class CDBehaviorTemplateTable : public CDTable {
|
||||
private:
|
||||
std::vector<CDBehaviorTemplate> entries;
|
||||
|
||||
std::unordered_map<uint32_t, CDBehaviorTemplate> entriesMappedByBehaviorID;
|
||||
std::unordered_set<std::string> m_EffectHandles;
|
||||
public:
|
||||
|
||||
//! Constructor
|
||||
@ -47,5 +50,6 @@ public:
|
||||
\return The entries
|
||||
*/
|
||||
std::vector<CDBehaviorTemplate> GetEntries(void) const;
|
||||
|
||||
|
||||
const CDBehaviorTemplate GetByBehaviorID(uint32_t behaviorID);
|
||||
};
|
||||
|
@ -168,7 +168,7 @@ std::map<LOT, uint32_t> CDItemComponentTable::ParseCraftingCurrencies(const CDIt
|
||||
// Checking for 2 here, not sure what to do when there's more stuff than expected
|
||||
if (amountSplit.size() == 2) {
|
||||
currencies.insert({
|
||||
std::stol(amountSplit[0]),
|
||||
std::stoull(amountSplit[0]),
|
||||
std::stoi(amountSplit[1])
|
||||
});
|
||||
}
|
||||
|
38
dDatabase/Tables/CMakeLists.txt
Normal file
38
dDatabase/Tables/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp"
|
||||
"CDActivityRewardsTable.cpp"
|
||||
"CDAnimationsTable.cpp"
|
||||
"CDBehaviorParameterTable.cpp"
|
||||
"CDBehaviorTemplateTable.cpp"
|
||||
"CDBrickIDTableTable.cpp"
|
||||
"CDComponentsRegistryTable.cpp"
|
||||
"CDCurrencyTableTable.cpp"
|
||||
"CDDestructibleComponentTable.cpp"
|
||||
"CDEmoteTable.cpp"
|
||||
"CDFeatureGatingTable.cpp"
|
||||
"CDInventoryComponentTable.cpp"
|
||||
"CDItemComponentTable.cpp"
|
||||
"CDItemSetSkillsTable.cpp"
|
||||
"CDItemSetsTable.cpp"
|
||||
"CDLevelProgressionLookupTable.cpp"
|
||||
"CDLootMatrixTable.cpp"
|
||||
"CDLootTableTable.cpp"
|
||||
"CDMissionEmailTable.cpp"
|
||||
"CDMissionNPCComponentTable.cpp"
|
||||
"CDMissionsTable.cpp"
|
||||
"CDMissionTasksTable.cpp"
|
||||
"CDMovementAIComponentTable.cpp"
|
||||
"CDObjectSkillsTable.cpp"
|
||||
"CDObjectsTable.cpp"
|
||||
"CDPackageComponentTable.cpp"
|
||||
"CDPhysicsComponentTable.cpp"
|
||||
"CDPropertyEntranceComponentTable.cpp"
|
||||
"CDPropertyTemplateTable.cpp"
|
||||
"CDProximityMonitorComponentTable.cpp"
|
||||
"CDRailActivatorComponent.cpp"
|
||||
"CDRarityTableTable.cpp"
|
||||
"CDRebuildComponentTable.cpp"
|
||||
"CDRewardsTable.cpp"
|
||||
"CDScriptComponentTable.cpp"
|
||||
"CDSkillBehaviorTable.cpp"
|
||||
"CDVendorComponentTable.cpp"
|
||||
"CDZoneTableTable.cpp" PARENT_SCOPE)
|
59
dGame/CMakeLists.txt
Normal file
59
dGame/CMakeLists.txt
Normal file
@ -0,0 +1,59 @@
|
||||
set(DGAME_SOURCES "Character.cpp"
|
||||
"Entity.cpp"
|
||||
"EntityManager.cpp"
|
||||
"LeaderboardManager.cpp"
|
||||
"Player.cpp"
|
||||
"TeamManager.cpp"
|
||||
"TradingManager.cpp"
|
||||
"User.cpp"
|
||||
"UserManager.cpp")
|
||||
|
||||
add_subdirectory(dBehaviors)
|
||||
|
||||
foreach(file ${DGAME_DBEHAVIORS_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dBehaviors/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dComponents)
|
||||
|
||||
foreach(file ${DGAME_DCOMPONENTS_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dComponents/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dEntity)
|
||||
|
||||
foreach(file ${DGAME_DENTITY_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dEntity/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dGameMessages)
|
||||
|
||||
foreach(file ${DGAME_DGAMEMESSAGES_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dGameMessages/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dInventory)
|
||||
|
||||
foreach(file ${DGAME_DINVENTORY_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dInventory/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dMission)
|
||||
|
||||
foreach(file ${DGAME_DMISSION_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dMission/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(dUtilities)
|
||||
|
||||
foreach(file ${DGAME_DUTILITIES_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "dUtilities/${file}")
|
||||
endforeach()
|
||||
|
||||
foreach(file ${DSCRIPT_SOURCES})
|
||||
set(DGAME_SOURCES ${DGAME_SOURCES} "${PROJECT_SOURCE_DIR}/dScripts/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dGame STATIC ${DGAME_SOURCES})
|
||||
|
||||
target_link_libraries(dGame dDatabase)
|
120
dGame/Entity.cpp
120
dGame/Entity.cpp
@ -79,6 +79,7 @@ Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity)
|
||||
m_Components = {};
|
||||
m_DieCallbacks = {};
|
||||
m_PhantomCollisionCallbacks = {};
|
||||
m_IsParentChildDirty = true;
|
||||
|
||||
m_Settings = info.settings;
|
||||
m_NetworkSettings = info.networkSettings;
|
||||
@ -98,22 +99,6 @@ Entity::~Entity() {
|
||||
m_Character->SaveXMLToDatabase();
|
||||
}
|
||||
|
||||
if (IsPlayer()) {
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerExit(zoneControl, this);
|
||||
}
|
||||
|
||||
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CancelAllTimers();
|
||||
CancelCallbackTimers();
|
||||
|
||||
@ -124,6 +109,14 @@ Entity::~Entity() {
|
||||
|
||||
m_Components.erase(pair.first);
|
||||
}
|
||||
|
||||
for (auto child : m_ChildEntities) {
|
||||
if (child) child->RemoveParent();
|
||||
}
|
||||
|
||||
if (m_ParentEntity) {
|
||||
m_ParentEntity->RemoveChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::Initialize()
|
||||
@ -220,8 +213,9 @@ void Entity::Initialize()
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_ZONE_CONTROL, nullptr));
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_POSSESSABLE) > 0) {
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_POSSESSABLE, new PossessableComponent(this)));
|
||||
uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_POSSESSABLE);
|
||||
if (possessableComponentId > 0) {
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_POSSESSABLE, new PossessableComponent(this, possessableComponentId)));
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_MODULE_ASSEMBLY) > 0) {
|
||||
@ -447,6 +441,8 @@ void Entity::Initialize()
|
||||
}*/
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_CHARACTER) > 0 || m_Character) {
|
||||
// Character Component always has a possessor component
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_POSSESSOR, new PossessorComponent(this)));
|
||||
CharacterComponent* comp = new CharacterComponent(this, m_Character);
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_CHARACTER, comp));
|
||||
}
|
||||
@ -561,19 +557,6 @@ void Entity::Initialize()
|
||||
comp->SetPostImaginationCost(rebCompData[0].post_imagination_cost);
|
||||
comp->SetTimeBeforeSmash(rebCompData[0].time_before_smash);
|
||||
|
||||
const auto rebuildActivatorValue = GetVarAsString(u"rebuild_activators");
|
||||
|
||||
if (!rebuildActivatorValue.empty()) {
|
||||
std::vector<std::string> split = GeneralUtils::SplitString(rebuildActivatorValue, 0x1f);
|
||||
NiPoint3 pos;
|
||||
|
||||
pos.x = std::stof(split[0]);
|
||||
pos.y = std::stof(split[1]);
|
||||
pos.z = std::stof(split[2]);
|
||||
|
||||
comp->SetActivatorPosition(pos);
|
||||
}
|
||||
|
||||
const auto rebuildResetTime = GetVar<float>(u"rebuild_reset_time");
|
||||
|
||||
if (rebuildResetTime != 0.0f) {
|
||||
@ -631,10 +614,6 @@ void Entity::Initialize()
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_RENDER, render));
|
||||
}
|
||||
|
||||
if ((compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_POSSESSOR) > 0) || m_Character) {
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_POSSESSOR, new PossessorComponent(this)));
|
||||
}
|
||||
|
||||
if ((compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_MISSION_OFFER) > 0) || m_Character) {
|
||||
m_Components.insert(std::make_pair(COMPONENT_TYPE_MISSION_OFFER, new MissionOfferComponent(this, m_TemplateID)));
|
||||
}
|
||||
@ -974,8 +953,11 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
|
||||
}
|
||||
else outBitStream->Write0(); //No GM Level
|
||||
}
|
||||
outBitStream->Write((m_ParentEntity != nullptr || m_ChildEntities.size() > 0));
|
||||
if (m_ParentEntity || m_ChildEntities.size() > 0) {
|
||||
|
||||
// Only serialize parent / child info should the info be dirty (changed) or if this is the construction of the entity.
|
||||
outBitStream->Write(m_IsParentChildDirty || packetType == PACKET_TYPE_CONSTRUCTION);
|
||||
if (m_IsParentChildDirty || packetType == PACKET_TYPE_CONSTRUCTION) {
|
||||
m_IsParentChildDirty = false;
|
||||
outBitStream->Write(m_ParentEntity != nullptr);
|
||||
if (m_ParentEntity) {
|
||||
outBitStream->Write(m_ParentEntity->GetObjectID());
|
||||
@ -1079,8 +1061,15 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
}
|
||||
|
||||
CharacterComponent* characterComponent;
|
||||
if (TryGetComponent(COMPONENT_TYPE_CHARACTER, characterComponent))
|
||||
{
|
||||
if (TryGetComponent(COMPONENT_TYPE_CHARACTER, characterComponent)) {
|
||||
|
||||
PossessorComponent* possessorComponent;
|
||||
if (TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessorComponent)) {
|
||||
possessorComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
|
||||
} else {
|
||||
// Should never happen, but just to be safe
|
||||
outBitStream->Write0();
|
||||
}
|
||||
characterComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
|
||||
}
|
||||
|
||||
@ -1186,11 +1175,10 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
outBitStream->Write<uint32_t>(0x40000000);
|
||||
}
|
||||
|
||||
PossessorComponent* possessorComponent;
|
||||
if (TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessorComponent))
|
||||
{
|
||||
possessorComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
|
||||
}
|
||||
// BBB Component, unused currently
|
||||
// Need to to write0 so that is serlaizese correctly
|
||||
// TODO: Implement BBB Component
|
||||
outBitStream->Write0();
|
||||
|
||||
/*
|
||||
if (m_Trigger != nullptr)
|
||||
@ -1218,17 +1206,21 @@ void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
void Entity::Update(const float deltaTime) {
|
||||
for (int i = 0; i < m_Timers.size(); i++) {
|
||||
m_Timers[i]->Update(deltaTime);
|
||||
if (m_Timers[i]->GetTime() <= 0) {
|
||||
const auto timerName = m_Timers[i]->GetName();
|
||||
uint32_t timerPosition;
|
||||
timerPosition = 0;
|
||||
while (timerPosition < m_Timers.size()) {
|
||||
m_Timers[timerPosition]->Update(deltaTime);
|
||||
if (m_Timers[timerPosition]->GetTime() <= 0) {
|
||||
const auto timerName = m_Timers[timerPosition]->GetName();
|
||||
|
||||
delete m_Timers[i];
|
||||
m_Timers.erase(m_Timers.begin() + i);
|
||||
delete m_Timers[timerPosition];
|
||||
m_Timers.erase(m_Timers.begin() + timerPosition);
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
|
||||
script->OnTimerDone(this, timerName);
|
||||
}
|
||||
} else {
|
||||
timerPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1240,6 +1232,14 @@ void Entity::Update(const float deltaTime) {
|
||||
m_CallbackTimers.erase(m_CallbackTimers.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Add pending timers to the list of timers so they start next tick.
|
||||
if (m_PendingTimers.size() > 0) {
|
||||
for (auto namedTimer : m_PendingTimers) {
|
||||
m_Timers.push_back(namedTimer);
|
||||
}
|
||||
m_PendingTimers.clear();
|
||||
}
|
||||
|
||||
if (IsSleeping())
|
||||
{
|
||||
@ -1661,12 +1661,30 @@ void Entity::RegisterCoinDrop(uint64_t count) {
|
||||
}
|
||||
|
||||
void Entity::AddChild(Entity* child) {
|
||||
m_IsParentChildDirty = true;
|
||||
m_ChildEntities.push_back(child);
|
||||
}
|
||||
|
||||
void Entity::RemoveChild(Entity* child) {
|
||||
if (!child) return;
|
||||
uint32_t entityPosition = 0;
|
||||
while (entityPosition < m_ChildEntities.size()) {
|
||||
if (!m_ChildEntities[entityPosition] || (m_ChildEntities[entityPosition])->GetObjectID() == child->GetObjectID()) {
|
||||
m_IsParentChildDirty = true;
|
||||
m_ChildEntities.erase(m_ChildEntities.begin() + entityPosition);
|
||||
} else {
|
||||
entityPosition++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Entity::RemoveParent() {
|
||||
this->m_ParentEntity = nullptr;
|
||||
}
|
||||
|
||||
void Entity::AddTimer(std::string name, float time) {
|
||||
EntityTimer* timer = new EntityTimer(name, time);
|
||||
m_Timers.push_back(timer);
|
||||
m_PendingTimers.push_back(timer);
|
||||
}
|
||||
|
||||
void Entity::AddCallbackTimer(float time, std::function<void()> callback) {
|
||||
|
@ -143,6 +143,8 @@ public:
|
||||
void SetProximityRadius(dpEntity* entity, std::string name);
|
||||
|
||||
void AddChild(Entity* child);
|
||||
void RemoveChild(Entity* child);
|
||||
void RemoveParent();
|
||||
void AddTimer(std::string name, float time);
|
||||
void AddCallbackTimer(float time, std::function<void()> callback);
|
||||
bool HasTimer(const std::string& name);
|
||||
@ -308,6 +310,7 @@ protected:
|
||||
|
||||
std::unordered_map<int32_t, Component*> m_Components; //The int is the ID of the component
|
||||
std::vector<EntityTimer*> m_Timers;
|
||||
std::vector<EntityTimer*> m_PendingTimers;
|
||||
std::vector<EntityCallbackTimer*> m_CallbackTimers;
|
||||
|
||||
bool m_ShouldDestroyAfterUpdate = false;
|
||||
@ -322,6 +325,8 @@ protected:
|
||||
|
||||
int8_t m_Observers = 0;
|
||||
|
||||
bool m_IsParentChildDirty = true;
|
||||
|
||||
/*
|
||||
* Collision
|
||||
*/
|
||||
|
@ -217,37 +217,42 @@ void EntityManager::UpdateEntities(const float deltaTime) {
|
||||
|
||||
m_EntitiesToKill.clear();
|
||||
|
||||
for (const auto& entry : m_EntitiesToDelete)
|
||||
for (const auto entry : m_EntitiesToDelete)
|
||||
{
|
||||
auto* entity = GetEntity(entry);
|
||||
// Get all this info first before we delete the player.
|
||||
auto entityToDelete = GetEntity(entry);
|
||||
|
||||
m_Entities.erase(entry);
|
||||
auto networkIdToErase = entityToDelete->GetNetworkId();
|
||||
|
||||
const auto& iter = std::find(m_EntitiesToGhost.begin(), m_EntitiesToGhost.end(), entity);
|
||||
const auto& ghostingToDelete = std::find(m_EntitiesToGhost.begin(), m_EntitiesToGhost.end(), entityToDelete);
|
||||
|
||||
if (iter != m_EntitiesToGhost.end())
|
||||
if (entityToDelete)
|
||||
{
|
||||
m_EntitiesToGhost.erase(iter);
|
||||
}
|
||||
|
||||
if (entity != nullptr)
|
||||
{
|
||||
if (entity->GetNetworkId() != 0)
|
||||
// If we are a player run through the player destructor.
|
||||
if (entityToDelete->IsPlayer())
|
||||
{
|
||||
m_LostNetworkIds.push(entity->GetNetworkId());
|
||||
}
|
||||
|
||||
if (entity->IsPlayer())
|
||||
{
|
||||
delete dynamic_cast<Player*>(entity);
|
||||
delete dynamic_cast<Player*>(entityToDelete);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete entity;
|
||||
delete entityToDelete;
|
||||
}
|
||||
|
||||
entity = nullptr;
|
||||
entityToDelete = nullptr;
|
||||
|
||||
if (networkIdToErase != 0)
|
||||
{
|
||||
m_LostNetworkIds.push(networkIdToErase);
|
||||
}
|
||||
}
|
||||
|
||||
if (ghostingToDelete != m_EntitiesToGhost.end())
|
||||
{
|
||||
m_EntitiesToGhost.erase(ghostingToDelete);
|
||||
}
|
||||
|
||||
m_Entities.erase(entry);
|
||||
|
||||
}
|
||||
|
||||
m_EntitiesToDelete.clear();
|
||||
@ -396,7 +401,7 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
|
||||
Game::server->Send(&stream, sysAddr, false);
|
||||
}
|
||||
|
||||
PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed());
|
||||
// PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed());
|
||||
|
||||
if (entity->IsPlayer())
|
||||
{
|
||||
|
@ -3,8 +3,10 @@
|
||||
#include "Database.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Character.h"
|
||||
#include "Game.h"
|
||||
#include "GameMessages.h"
|
||||
#include "dLogger.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
Leaderboard::Leaderboard(uint32_t gameID, uint32_t infoType, bool weekly, std::vector<LeaderboardEntry> entries,
|
||||
LWOOBJID relatedPlayer, LeaderboardType leaderboardType) {
|
||||
@ -90,6 +92,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
||||
const auto storedTime = result->getInt(1);
|
||||
const auto storedScore = result->getInt(2);
|
||||
auto highscore = true;
|
||||
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
||||
|
||||
switch (leaderboardType) {
|
||||
case ShootingGallery:
|
||||
@ -97,8 +100,11 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
||||
highscore = false;
|
||||
break;
|
||||
case Racing:
|
||||
if (time >= storedTime)
|
||||
highscore = false;
|
||||
break;
|
||||
case MonumentRace:
|
||||
if (time > storedTime)
|
||||
if (time >= storedTime)
|
||||
highscore = false;
|
||||
break;
|
||||
case FootRace:
|
||||
@ -106,8 +112,18 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
||||
highscore = false;
|
||||
break;
|
||||
case Survival:
|
||||
if (classicSurvivalScoring) {
|
||||
if (time <= storedTime) { // Based on time (LU live)
|
||||
highscore = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (score <= storedScore) // Based on score (DLU)
|
||||
highscore = false;
|
||||
}
|
||||
break;
|
||||
case SurvivalNS:
|
||||
if (score < storedScore || time >= storedTime)
|
||||
if (!(score > storedScore || (time < storedTime && score >= storedScore)))
|
||||
highscore = false;
|
||||
break;
|
||||
default:
|
||||
@ -125,7 +141,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
||||
delete result;
|
||||
|
||||
if (any) {
|
||||
auto* statement = Database::CreatePreppedStmt("UPDATE leaderboard SET time = ?, score = ? WHERE character_id = ? AND game_id = ?");
|
||||
auto* statement = Database::CreatePreppedStmt("UPDATE leaderboard SET time = ?, score = ?, last_played=SYSDATE() WHERE character_id = ? AND game_id = ?;");
|
||||
statement->setInt(1, time);
|
||||
statement->setInt(2, score);
|
||||
statement->setUInt64(3, character->GetID());
|
||||
@ -134,6 +150,7 @@ void LeaderboardManager::SaveScore(LWOOBJID playerID, uint32_t gameID, uint32_t
|
||||
|
||||
delete statement;
|
||||
} else {
|
||||
// Note: last_played will be set to SYSDATE() by default when inserting into leaderboard
|
||||
auto* statement = Database::CreatePreppedStmt("INSERT INTO leaderboard (character_id, game_id, time, score) VALUES (?, ?, ?, ?);");
|
||||
statement->setUInt64(1, character->GetID());
|
||||
statement->setInt(2, gameID);
|
||||
@ -149,15 +166,62 @@ Leaderboard *LeaderboardManager::GetLeaderboard(uint32_t gameID, InfoType infoTy
|
||||
auto leaderboardType = GetLeaderboardType(gameID);
|
||||
|
||||
std::string query;
|
||||
bool classicSurvivalScoring = Game::config->GetValue("classic_survival_scoring") == "1";
|
||||
switch (infoType) {
|
||||
case InfoType::Standings:
|
||||
query = leaderboardType == MonumentRace ? standingsQueryAsc : standingsQuery;
|
||||
switch (leaderboardType) {
|
||||
case ShootingGallery:
|
||||
query = standingsScoreQuery; // Shooting gallery is based on the highest score.
|
||||
break;
|
||||
case FootRace:
|
||||
query = standingsTimeQuery; // The higher your time, the better for FootRace.
|
||||
break;
|
||||
case Survival:
|
||||
query = classicSurvivalScoring ? standingsTimeQuery : standingsScoreQuery;
|
||||
break;
|
||||
case SurvivalNS:
|
||||
query = standingsScoreQueryAsc; // BoNS is scored by highest wave (score) first, then time.
|
||||
break;
|
||||
default:
|
||||
query = standingsTimeQueryAsc; // MonumentRace and Racing are based on the shortest time.
|
||||
}
|
||||
break;
|
||||
case InfoType::Friends:
|
||||
query = leaderboardType == MonumentRace ? friendsQueryAsc : friendsQuery;
|
||||
switch (leaderboardType) {
|
||||
case ShootingGallery:
|
||||
query = friendsScoreQuery; // Shooting gallery is based on the highest score.
|
||||
break;
|
||||
case FootRace:
|
||||
query = friendsTimeQuery; // The higher your time, the better for FootRace.
|
||||
break;
|
||||
case Survival:
|
||||
query = classicSurvivalScoring ? friendsTimeQuery : friendsScoreQuery;
|
||||
break;
|
||||
case SurvivalNS:
|
||||
query = friendsScoreQueryAsc; // BoNS is scored by highest wave (score) first, then time.
|
||||
break;
|
||||
default:
|
||||
query = friendsTimeQueryAsc; // MonumentRace and Racing are based on the shortest time.
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
query = leaderboardType == MonumentRace ? topPlayersQueryAsc : topPlayersQuery;
|
||||
switch (leaderboardType) {
|
||||
case ShootingGallery:
|
||||
query = topPlayersScoreQuery; // Shooting gallery is based on the highest score.
|
||||
break;
|
||||
case FootRace:
|
||||
query = topPlayersTimeQuery; // The higher your time, the better for FootRace.
|
||||
break;
|
||||
case Survival:
|
||||
query = classicSurvivalScoring ? topPlayersTimeQuery : topPlayersScoreQuery;
|
||||
break;
|
||||
case SurvivalNS:
|
||||
query = topPlayersScoreQueryAsc; // BoNS is scored by highest wave (score) first, then time.
|
||||
break;
|
||||
default:
|
||||
query = topPlayersTimeQueryAsc; // MonumentRace and Racing are based on the shortest time.
|
||||
}
|
||||
}
|
||||
|
||||
auto* statement = Database::CreatePreppedStmt(query);
|
||||
@ -183,14 +247,15 @@ Leaderboard *LeaderboardManager::GetLeaderboard(uint32_t gameID, InfoType infoTy
|
||||
|
||||
uint32_t index = 0;
|
||||
while (res->next()) {
|
||||
entries.push_back({
|
||||
res->getUInt64(4),
|
||||
res->getString(5),
|
||||
res->getUInt(1),
|
||||
res->getUInt(2),
|
||||
res->getUInt(3),
|
||||
res->getUInt(6)
|
||||
});
|
||||
LeaderboardEntry entry;
|
||||
entry.playerID = res->getUInt64(4);
|
||||
entry.playerName = res->getString(5);
|
||||
entry.time = res->getUInt(1);
|
||||
entry.score = res->getUInt(2);
|
||||
entry.placement = res->getUInt(3);
|
||||
entry.lastPlayed = res->getUInt(6);
|
||||
|
||||
entries.push_back(entry);
|
||||
index++;
|
||||
}
|
||||
|
||||
@ -220,7 +285,7 @@ LeaderboardType LeaderboardManager::GetLeaderboardType(uint32_t gameID) {
|
||||
return LeaderboardType::None;
|
||||
}
|
||||
|
||||
const std::string LeaderboardManager::topPlayersQuery =
|
||||
const std::string LeaderboardManager::topPlayersScoreQuery =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
"RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank "
|
||||
@ -231,7 +296,7 @@ const std::string LeaderboardManager::topPlayersQuery =
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales LIMIT 11;";
|
||||
|
||||
const std::string LeaderboardManager::friendsQuery =
|
||||
const std::string LeaderboardManager::friendsScoreQuery =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
|
||||
" RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank "
|
||||
@ -249,7 +314,7 @@ const std::string LeaderboardManager::friendsQuery =
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
|
||||
|
||||
const std::string LeaderboardManager::standingsQuery =
|
||||
const std::string LeaderboardManager::standingsScoreQuery =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
" RANK() OVER ( ORDER BY l.score DESC, l.time DESC, last_played ) leaderboard_rank "
|
||||
@ -265,7 +330,7 @@ const std::string LeaderboardManager::standingsQuery =
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
|
||||
|
||||
const std::string LeaderboardManager::topPlayersQueryAsc =
|
||||
const std::string LeaderboardManager::topPlayersScoreQueryAsc =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
"RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank "
|
||||
@ -276,7 +341,7 @@ const std::string LeaderboardManager::topPlayersQueryAsc =
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales LIMIT 11;";
|
||||
|
||||
const std::string LeaderboardManager::friendsQueryAsc =
|
||||
const std::string LeaderboardManager::friendsScoreQueryAsc =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
|
||||
" RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank "
|
||||
@ -294,7 +359,7 @@ const std::string LeaderboardManager::friendsQueryAsc =
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
|
||||
|
||||
const std::string LeaderboardManager::standingsQueryAsc =
|
||||
const std::string LeaderboardManager::standingsScoreQueryAsc =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
" RANK() OVER ( ORDER BY l.score DESC, l.time ASC, last_played ) leaderboard_rank "
|
||||
@ -309,3 +374,93 @@ const std::string LeaderboardManager::standingsQueryAsc =
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
|
||||
|
||||
const std::string LeaderboardManager::topPlayersTimeQuery =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
"RANK() OVER ( ORDER BY l.time DESC, l.score DESC, last_played ) leaderboard_rank "
|
||||
" FROM leaderboard l "
|
||||
"INNER JOIN charinfo c ON l.character_id = c.id "
|
||||
"WHERE l.game_id = ? "
|
||||
"ORDER BY leaderboard_rank) "
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales LIMIT 11;";
|
||||
|
||||
const std::string LeaderboardManager::friendsTimeQuery =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
|
||||
" RANK() OVER ( ORDER BY l.time DESC, l.score DESC, last_played ) leaderboard_rank "
|
||||
" FROM leaderboard l "
|
||||
" INNER JOIN charinfo c ON l.character_id = c.id "
|
||||
" INNER JOIN friends f ON f.player_id = c.id "
|
||||
" WHERE l.game_id = ? "
|
||||
" ORDER BY leaderboard_rank), "
|
||||
" personal_values AS ( "
|
||||
" SELECT id as related_player_id, "
|
||||
" GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
|
||||
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
|
||||
" FROM leaderboard_vales WHERE leaderboard_vales.id = ? LIMIT 1) "
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
|
||||
|
||||
const std::string LeaderboardManager::standingsTimeQuery =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
" RANK() OVER ( ORDER BY l.time DESC, l.score DESC, last_played ) leaderboard_rank "
|
||||
" FROM leaderboard l "
|
||||
" INNER JOIN charinfo c ON l.character_id = c.id "
|
||||
" WHERE l.game_id = ? "
|
||||
" ORDER BY leaderboard_rank), "
|
||||
"personal_values AS ( "
|
||||
" SELECT GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
|
||||
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
|
||||
" FROM leaderboard_vales WHERE id = ? LIMIT 1) "
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
|
||||
|
||||
const std::string LeaderboardManager::topPlayersTimeQueryAsc =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
"RANK() OVER ( ORDER BY l.time ASC, l.score DESC, last_played ) leaderboard_rank "
|
||||
" FROM leaderboard l "
|
||||
"INNER JOIN charinfo c ON l.character_id = c.id "
|
||||
"WHERE l.game_id = ? "
|
||||
"ORDER BY leaderboard_rank) "
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales LIMIT 11;";
|
||||
|
||||
const std::string LeaderboardManager::friendsTimeQueryAsc =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, f.friend_id, f.player_id, "
|
||||
" RANK() OVER ( ORDER BY l.time ASC, l.score DESC, last_played ) leaderboard_rank "
|
||||
" FROM leaderboard l "
|
||||
" INNER JOIN charinfo c ON l.character_id = c.id "
|
||||
" INNER JOIN friends f ON f.player_id = c.id "
|
||||
" WHERE l.game_id = ? "
|
||||
" ORDER BY leaderboard_rank), "
|
||||
" personal_values AS ( "
|
||||
" SELECT id as related_player_id, "
|
||||
" GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
|
||||
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
|
||||
" FROM leaderboard_vales WHERE leaderboard_vales.id = ? LIMIT 1) "
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank AND (player_id = related_player_id OR friend_id = related_player_id);";
|
||||
|
||||
const std::string LeaderboardManager::standingsTimeQueryAsc =
|
||||
"WITH leaderboard_vales AS ( "
|
||||
" SELECT l.time, l.score, UNIX_TIMESTAMP(l.last_played) last_played, c.name, c.id, "
|
||||
" RANK() OVER ( ORDER BY l.time ASC, l.score DESC, last_played ) leaderboard_rank "
|
||||
" FROM leaderboard l "
|
||||
" INNER JOIN charinfo c ON l.character_id = c.id "
|
||||
" WHERE l.game_id = ? "
|
||||
" ORDER BY leaderboard_rank), "
|
||||
"personal_values AS ( "
|
||||
" SELECT GREATEST(CAST(leaderboard_rank AS SIGNED) - 5, 1) AS min_rank, "
|
||||
" GREATEST(leaderboard_rank + 5, 11) AS max_rank "
|
||||
" FROM leaderboard_vales WHERE id = ? LIMIT 1) "
|
||||
"SELECT time, score, leaderboard_rank, id, name, last_played "
|
||||
"FROM leaderboard_vales, personal_values "
|
||||
"WHERE leaderboard_rank BETWEEN min_rank AND max_rank;";
|
||||
|
@ -60,11 +60,21 @@ public:
|
||||
static LeaderboardType GetLeaderboardType(uint32_t gameID);
|
||||
private:
|
||||
static LeaderboardManager* address;
|
||||
static const std::string topPlayersQuery;
|
||||
static const std::string friendsQuery;
|
||||
static const std::string standingsQuery;
|
||||
static const std::string topPlayersQueryAsc;
|
||||
static const std::string friendsQueryAsc;
|
||||
static const std::string standingsQueryAsc;
|
||||
|
||||
// Modified 12/12/2021: Existing queries were renamed to be more descriptive.
|
||||
static const std::string topPlayersScoreQuery;
|
||||
static const std::string friendsScoreQuery;
|
||||
static const std::string standingsScoreQuery;
|
||||
static const std::string topPlayersScoreQueryAsc;
|
||||
static const std::string friendsScoreQueryAsc;
|
||||
static const std::string standingsScoreQueryAsc;
|
||||
|
||||
// Added 12/12/2021: Queries dictated by time are needed for certain minigames.
|
||||
static const std::string topPlayersTimeQuery;
|
||||
static const std::string friendsTimeQuery;
|
||||
static const std::string standingsTimeQuery;
|
||||
static const std::string topPlayersTimeQueryAsc;
|
||||
static const std::string friendsTimeQueryAsc;
|
||||
static const std::string standingsTimeQueryAsc;
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "dZoneManager.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "Mail.h"
|
||||
#include "CppScripts.h"
|
||||
|
||||
std::vector<Player*> Player::m_Players = {};
|
||||
|
||||
@ -275,5 +276,21 @@ Player::~Player()
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPlayer()) {
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerExit(zoneControl, this);
|
||||
}
|
||||
|
||||
std::vector<Entity*> scriptedActs = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_SCRIPTED_ACTIVITY);
|
||||
for (Entity* scriptEntity : scriptedActs) {
|
||||
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
|
||||
script->OnPlayerExit(scriptEntity, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Players.erase(iter);
|
||||
}
|
||||
|
@ -369,10 +369,8 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
}
|
||||
|
||||
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
|
||||
objectID = GeneralUtils::ClearBit(objectID, OBJECT_BIT_CHARACTER);
|
||||
objectID = GeneralUtils::ClearBit(objectID, OBJECT_BIT_PERSISTENT);
|
||||
|
||||
uint32_t charID = static_cast<uint32_t>(objectID);
|
||||
uint32_t charID = static_cast<uint32_t>(objectID);
|
||||
|
||||
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)\n", objectID, charID);
|
||||
|
||||
//Check if this user has this character:
|
||||
@ -402,10 +400,14 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->setUInt64(2, charID);
|
||||
stmt->setUInt(1, charID);
|
||||
stmt->setUInt(2, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION);
|
||||
bitStream.Write(objectID);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;");
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "dLogger.h"
|
||||
#include "BehaviorTemplates.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include <unordered_map>
|
||||
|
||||
/*
|
||||
* Behavior includes
|
||||
@ -45,6 +46,7 @@
|
||||
#include "InterruptBehavior.h"
|
||||
#include "PlayEffectBehavior.h"
|
||||
#include "DamageAbsorptionBehavior.h"
|
||||
#include "VentureVisionBehavior.h"
|
||||
#include "BlockBehavior.h"
|
||||
#include "ClearTargetBehavior.h"
|
||||
#include "PullToPointBehavior.h"
|
||||
@ -68,7 +70,7 @@
|
||||
#include "RenderComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
std::map<uint32_t, Behavior*> Behavior::Cache = {};
|
||||
std::unordered_map<uint32_t, Behavior*> Behavior::Cache = {};
|
||||
CDBehaviorParameterTable* Behavior::BehaviorParameterTable = nullptr;
|
||||
|
||||
Behavior* Behavior::GetBehavior(const uint32_t behaviorId)
|
||||
@ -91,21 +93,21 @@ Behavior* Behavior::GetBehavior(const uint32_t behaviorId)
|
||||
Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
|
||||
{
|
||||
auto* cached = GetBehavior(behaviorId);
|
||||
|
||||
|
||||
if (cached != nullptr)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
|
||||
if (behaviorId == 0)
|
||||
{
|
||||
return new EmptyBehavior(0);
|
||||
}
|
||||
|
||||
|
||||
const auto templateId = GetBehaviorTemplate(behaviorId);
|
||||
|
||||
Behavior* behavior = nullptr;
|
||||
|
||||
|
||||
switch (templateId)
|
||||
{
|
||||
case BehaviorTemplates::BEHAVIOR_EMPTY: break;
|
||||
@ -176,7 +178,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
|
||||
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:
|
||||
behavior = new LootBuffBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: break;
|
||||
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION:
|
||||
behavior = new VentureVisionBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT:
|
||||
behavior = new SpawnBehavior(behaviorId);
|
||||
break;
|
||||
@ -191,8 +195,8 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
|
||||
behavior = new JetPackBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_EVENT:
|
||||
behavior = new SkillEventBehavior(behaviorId);
|
||||
break;
|
||||
behavior = new SkillEventBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: break;
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED:
|
||||
behavior = new SkillCastFailedBehavior(behaviorId);
|
||||
@ -272,7 +276,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
|
||||
if (behavior == nullptr)
|
||||
{
|
||||
//Game::logger->Log("Behavior", "Failed to load unimplemented template id (%i)!\n", templateId);
|
||||
|
||||
|
||||
behavior = new EmptyBehavior(behaviorId);
|
||||
}
|
||||
|
||||
@ -281,30 +285,23 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
|
||||
return behavior;
|
||||
}
|
||||
|
||||
BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId)
|
||||
{
|
||||
std::stringstream query;
|
||||
BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
|
||||
auto behaviorTemplateTable = CDClientManager::Instance()->GetTable<CDBehaviorTemplateTable>("BehaviorTemplate");
|
||||
|
||||
query << "SELECT templateID FROM BehaviorTemplate WHERE behaviorID = " << std::to_string(behaviorId);
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
|
||||
// Make sure we do not proceed if we are trying to load an invalid behavior
|
||||
if (result.eof())
|
||||
{
|
||||
if (behaviorId != 0)
|
||||
{
|
||||
Game::logger->Log("Behavior::GetBehaviorTemplate", "Failed to load behavior template with id (%i)!\n", behaviorId);
|
||||
BehaviorTemplates templateID = BehaviorTemplates::BEHAVIOR_EMPTY;
|
||||
// Find behavior template by its behavior id. Default to 0.
|
||||
if (behaviorTemplateTable) {
|
||||
auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId);
|
||||
if (templateEntry.behaviorID == behaviorId) {
|
||||
templateID = static_cast<BehaviorTemplates>(templateEntry.templateID);
|
||||
}
|
||||
|
||||
return BehaviorTemplates::BEHAVIOR_EMPTY;
|
||||
}
|
||||
|
||||
const auto id = static_cast<BehaviorTemplates>(result.getIntField(0));
|
||||
|
||||
result.finalize();
|
||||
if (templateID == BehaviorTemplates::BEHAVIOR_EMPTY && behaviorId != 0) {
|
||||
Game::logger->Log("Behavior", "Failed to load behavior template with id (%i)!\n", behaviorId);
|
||||
}
|
||||
|
||||
return id;
|
||||
return templateID;
|
||||
}
|
||||
|
||||
// For use with enemies, to display the correct damage animations on the players
|
||||
@ -325,7 +322,7 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto* renderComponent = targetEntity->GetComponent<RenderComponent>();
|
||||
|
||||
const auto typeString = GeneralUtils::UTF16ToWTF8(type);
|
||||
@ -348,28 +345,35 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
if (renderComponent == nullptr)
|
||||
{
|
||||
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, pair->second, secondary, 1, 1, true);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
renderComponent->PlayEffect(effectId, type, pair->second, secondary);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream query;
|
||||
|
||||
if (!type.empty())
|
||||
{
|
||||
query << "SELECT effectName FROM BehaviorEffect WHERE effectType = '" << typeString << "' AND effectID = " << std::to_string(effectId) << ";";
|
||||
}
|
||||
else
|
||||
{
|
||||
query << "SELECT effectName, effectType FROM BehaviorEffect WHERE effectID = " << std::to_string(effectId) << ";";
|
||||
}
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
// The SQlite result object becomes invalid if the query object leaves scope.
|
||||
// So both queries are defined before the if statement
|
||||
CppSQLite3Query result;
|
||||
auto typeQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT effectName FROM BehaviorEffect WHERE effectType = ? AND effectID = ?;");
|
||||
|
||||
auto idQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT effectName, effectType FROM BehaviorEffect WHERE effectID = ?;");
|
||||
|
||||
if (!type.empty()) {
|
||||
typeQuery.bind(1, typeString.c_str());
|
||||
typeQuery.bind(2, (int) effectId);
|
||||
|
||||
result = typeQuery.execQuery();
|
||||
} else {
|
||||
idQuery.bind(1, (int) effectId);
|
||||
|
||||
result = idQuery.execQuery();
|
||||
}
|
||||
|
||||
if (result.eof() || result.fieldIsNull(0))
|
||||
{
|
||||
@ -381,7 +385,7 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
if (type.empty())
|
||||
{
|
||||
const auto typeResult = result.getStringField(1);
|
||||
|
||||
|
||||
type = GeneralUtils::ASCIIToUTF16(typeResult);
|
||||
|
||||
m_effectType = new std::string(typeResult);
|
||||
@ -394,7 +398,7 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
if (renderComponent == nullptr)
|
||||
{
|
||||
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, name, secondary, 1, 1, true);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -403,6 +407,17 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
|
||||
|
||||
Behavior::Behavior(const uint32_t behaviorId)
|
||||
{
|
||||
auto behaviorTemplateTable = CDClientManager::Instance()->GetTable<CDBehaviorTemplateTable>("BehaviorTemplate");
|
||||
|
||||
CDBehaviorTemplate templateInDatabase{};
|
||||
|
||||
if (behaviorTemplateTable) {
|
||||
auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId);
|
||||
if (templateEntry.behaviorID == behaviorId) {
|
||||
templateInDatabase = templateEntry;
|
||||
}
|
||||
}
|
||||
|
||||
this->m_behaviorId = behaviorId;
|
||||
|
||||
// Add to cache
|
||||
@ -414,18 +429,8 @@ Behavior::Behavior(const uint32_t behaviorId)
|
||||
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get standard info
|
||||
*/
|
||||
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT templateID, effectID, effectHandle FROM BehaviorTemplate WHERE behaviorID = " << std::to_string(behaviorId);
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
|
||||
// Make sure we do not proceed if we are trying to load an invalid behavior
|
||||
if (result.eof())
|
||||
if (templateInDatabase.behaviorID == 0)
|
||||
{
|
||||
Game::logger->Log("Behavior", "Failed to load behavior with id (%i)!\n", behaviorId);
|
||||
|
||||
@ -436,34 +441,19 @@ Behavior::Behavior(const uint32_t behaviorId)
|
||||
return;
|
||||
}
|
||||
|
||||
this->m_templateId = static_cast<BehaviorTemplates>(result.getIntField(0));
|
||||
|
||||
this->m_effectId = result.getIntField(1);
|
||||
this->m_templateId = static_cast<BehaviorTemplates>(templateInDatabase.templateID);
|
||||
|
||||
if (!result.fieldIsNull(2))
|
||||
{
|
||||
const std::string effectHandle = result.getStringField(2);
|
||||
if (effectHandle == "")
|
||||
{
|
||||
this->m_effectHandle = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->m_effectHandle = new std::string(effectHandle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->m_effectHandle = nullptr;
|
||||
}
|
||||
this->m_effectId = templateInDatabase.effectID;
|
||||
|
||||
result.finalize();
|
||||
this->m_effectHandle = *templateInDatabase.effectHandle != "" ? new std::string(*templateInDatabase.effectHandle) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
float Behavior::GetFloat(const std::string& name, const float defaultValue) const
|
||||
{
|
||||
return BehaviorParameterTable->GetEntry(this->m_behaviorId, name, defaultValue);
|
||||
// Get the behavior parameter entry and return its value.
|
||||
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance()->GetTable<CDBehaviorParameterTable>("BehaviorParameter");
|
||||
return BehaviorParameterTable->GetEntry(this->m_behaviorId, name, defaultValue).value;
|
||||
}
|
||||
|
||||
|
||||
@ -493,24 +483,14 @@ Behavior* Behavior::GetAction(float value) const
|
||||
|
||||
std::map<std::string, float> Behavior::GetParameterNames() const
|
||||
{
|
||||
std::map<std::string, float> parameters;
|
||||
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT parameterID, value FROM BehaviorParameter WHERE behaviorID = " << std::to_string(this->m_behaviorId);
|
||||
|
||||
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
|
||||
|
||||
while (!tableData.eof())
|
||||
{
|
||||
parameters.insert_or_assign(tableData.getStringField(0, ""), tableData.getFloatField(1, 0));
|
||||
|
||||
tableData.nextRow();
|
||||
std::map<std::string, float> templatesInDatabase;
|
||||
// Find behavior template by its behavior id.
|
||||
if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance()->GetTable<CDBehaviorParameterTable>("BehaviorParameter");
|
||||
if (BehaviorParameterTable) {
|
||||
templatesInDatabase = BehaviorParameterTable->GetParametersByBehaviorID(this->m_behaviorId);
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
|
||||
return parameters;
|
||||
return templatesInDatabase;
|
||||
}
|
||||
|
||||
void Behavior::Load()
|
||||
|
@ -19,7 +19,7 @@ public:
|
||||
/*
|
||||
* Static
|
||||
*/
|
||||
static std::map<uint32_t, Behavior*> Cache;
|
||||
static std::unordered_map<uint32_t, Behavior*> Cache;
|
||||
static CDBehaviorParameterTable* BehaviorParameterTable;
|
||||
|
||||
static Behavior* GetBehavior(uint32_t behaviorId);
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef BEHAVIORSLOT_H
|
||||
#define BEHAVIORSLOT_H
|
||||
|
||||
enum class BehaviorSlot
|
||||
{
|
||||
Invalid = -1,
|
||||
@ -9,3 +12,5 @@ enum class BehaviorSlot
|
||||
Head,
|
||||
Consumable
|
||||
};
|
||||
|
||||
#endif
|
||||
|
51
dGame/dBehaviors/CMakeLists.txt
Normal file
51
dGame/dBehaviors/CMakeLists.txt
Normal file
@ -0,0 +1,51 @@
|
||||
set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
|
||||
"AndBehavior.cpp"
|
||||
"ApplyBuffBehavior.cpp"
|
||||
"AreaOfEffectBehavior.cpp"
|
||||
"AttackDelayBehavior.cpp"
|
||||
"BasicAttackBehavior.cpp"
|
||||
"Behavior.cpp"
|
||||
"BehaviorBranchContext.cpp"
|
||||
"BehaviorContext.cpp"
|
||||
"BehaviorTemplates.cpp"
|
||||
"BlockBehavior.cpp"
|
||||
"BuffBehavior.cpp"
|
||||
"CarBoostBehavior.cpp"
|
||||
"ChainBehavior.cpp"
|
||||
"ChangeOrientationBehavior.cpp"
|
||||
"ChargeUpBehavior.cpp"
|
||||
"ClearTargetBehavior.cpp"
|
||||
"DamageAbsorptionBehavior.cpp"
|
||||
"DamageReductionBehavior.cpp"
|
||||
"DurationBehavior.cpp"
|
||||
"EmptyBehavior.cpp"
|
||||
"EndBehavior.cpp"
|
||||
"ForceMovementBehavior.cpp"
|
||||
"HealBehavior.cpp"
|
||||
"ImaginationBehavior.cpp"
|
||||
"ImmunityBehavior.cpp"
|
||||
"InterruptBehavior.cpp"
|
||||
"JetPackBehavior.cpp"
|
||||
"KnockbackBehavior.cpp"
|
||||
"LootBuffBehavior.cpp"
|
||||
"MovementSwitchBehavior.cpp"
|
||||
"NpcCombatSkillBehavior.cpp"
|
||||
"OverTimeBehavior.cpp"
|
||||
"PlayEffectBehavior.cpp"
|
||||
"ProjectileAttackBehavior.cpp"
|
||||
"PullToPointBehavior.cpp"
|
||||
"RepairBehavior.cpp"
|
||||
"SkillCastFailedBehavior.cpp"
|
||||
"SkillEventBehavior.cpp"
|
||||
"SpawnBehavior.cpp"
|
||||
"SpawnQuickbuildBehavior.cpp"
|
||||
"SpeedBehavior.cpp"
|
||||
"StartBehavior.cpp"
|
||||
"StunBehavior.cpp"
|
||||
"SwitchBehavior.cpp"
|
||||
"SwitchMultipleBehavior.cpp"
|
||||
"TacArcBehavior.cpp"
|
||||
"TargetCasterBehavior.cpp"
|
||||
"TauntBehavior.cpp"
|
||||
"VentureVisionBehavior.cpp"
|
||||
"VerifyBehavior.cpp" PARENT_SCOPE)
|
@ -39,15 +39,15 @@ void SwitchMultipleBehavior::Calculate(BehaviorContext* context, RakNet::BitStre
|
||||
// TODO
|
||||
}
|
||||
|
||||
void SwitchMultipleBehavior::Load()
|
||||
{
|
||||
const auto b = std::to_string(this->m_behaviorId);
|
||||
std::stringstream query;
|
||||
query << "SELECT replace(bP1.parameterID, 'behavior ', '') as key, bP1.value as behavior, "
|
||||
<< "(select bP2.value FROM BehaviorParameter bP2 WHERE bP2.behaviorID = " << b << " AND bP2.parameterID LIKE 'value %' "
|
||||
<< "AND replace(bP1.parameterID, 'behavior ', '') = replace(bP2.parameterID, 'value ', '')) as value "
|
||||
<< "FROM BehaviorParameter bP1 WHERE bP1.behaviorID = " << b << " AND bP1.parameterID LIKE 'behavior %'";
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
void SwitchMultipleBehavior::Load() {
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT replace(bP1.parameterID, 'behavior ', '') as key, bP1.value as behavior, "
|
||||
"(select bP2.value FROM BehaviorParameter bP2 WHERE bP2.behaviorID = ?1 AND bP2.parameterID LIKE 'value %' "
|
||||
"AND replace(bP1.parameterID, 'behavior ', '') = replace(bP2.parameterID, 'value ', '')) as value "
|
||||
"FROM BehaviorParameter bP1 WHERE bP1.behaviorID = ?1 AND bP1.parameterID LIKE 'behavior %';");
|
||||
query.bind(1, (int) this->m_behaviorId);
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
while (!result.eof()) {
|
||||
const auto behavior_id = static_cast<uint32_t>(result.getFloatField(1));
|
||||
|
47
dGame/dBehaviors/VentureVisionBehavior.cpp
Normal file
47
dGame/dBehaviors/VentureVisionBehavior.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "VentureVisionBehavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "BehaviorContext.h"
|
||||
|
||||
void VentureVisionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch){
|
||||
|
||||
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (targetEntity) {
|
||||
auto characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent) {
|
||||
if (m_show_collectibles) characterComponent->AddVentureVisionEffect(m_ShowCollectibles);
|
||||
if (m_show_minibosses) characterComponent->AddVentureVisionEffect(m_ShowMiniBosses);
|
||||
if (m_show_pet_digs) characterComponent->AddVentureVisionEffect(m_ShowPetDigs);
|
||||
}
|
||||
|
||||
if (branch.duration > 0) context->RegisterTimerBehavior(this, branch);
|
||||
}
|
||||
}
|
||||
|
||||
void VentureVisionBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
const auto targetEntity = EntityManager::Instance()->GetEntity(branch.target);
|
||||
|
||||
if (targetEntity) {
|
||||
auto characterComponent = targetEntity->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent) {
|
||||
if (m_show_collectibles) characterComponent->RemoveVentureVisionEffect(m_ShowCollectibles);
|
||||
if (m_show_minibosses) characterComponent->RemoveVentureVisionEffect(m_ShowMiniBosses);
|
||||
if (m_show_pet_digs) characterComponent->RemoveVentureVisionEffect(m_ShowPetDigs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VentureVisionBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||
UnCast(context, branch);
|
||||
}
|
||||
|
||||
void VentureVisionBehavior::Load(){
|
||||
this->m_show_pet_digs = GetBoolean("show_pet_digs");
|
||||
|
||||
this->m_show_minibosses = GetBoolean("show_minibosses");
|
||||
|
||||
this->m_show_collectibles = GetBoolean("show_collectibles");
|
||||
}
|
41
dGame/dBehaviors/VentureVisionBehavior.h
Normal file
41
dGame/dBehaviors/VentureVisionBehavior.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __VENTUREVISIONBEHAVIOR__H__
|
||||
#define __VENTUREVISIONBEHAVIOR__H__
|
||||
|
||||
#include "Behavior.h"
|
||||
|
||||
class VentureVisionBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
bool m_show_pet_digs;
|
||||
|
||||
bool m_show_minibosses;
|
||||
|
||||
bool m_show_collectibles;
|
||||
|
||||
const std::string m_ShowCollectibles = "bShowCollectibles";
|
||||
|
||||
const std::string m_ShowMiniBosses = "bShowMiniBosses";
|
||||
|
||||
const std::string m_ShowPetDigs = "bShowPetDigs";
|
||||
|
||||
|
||||
/*
|
||||
* Inherited
|
||||
*/
|
||||
|
||||
explicit VentureVisionBehavior(const uint32_t behaviorId) : Behavior(behaviorId)
|
||||
{
|
||||
}
|
||||
|
||||
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
|
||||
|
||||
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
|
||||
|
||||
void Load() override;
|
||||
};
|
||||
|
||||
#endif //!__VENTUREVISIONBEHAVIOR__H__
|
@ -35,11 +35,11 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
|
||||
m_SoftTimer = 5.0f;
|
||||
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
std::stringstream componentQuery;
|
||||
|
||||
componentQuery << "SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = " << std::to_string(id);
|
||||
|
||||
auto componentResult = CDClientDatabase::ExecuteQuery(componentQuery.str());
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
||||
componentQuery.bind(1, (int) id);
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
|
||||
if (!componentResult.eof())
|
||||
{
|
||||
@ -64,12 +64,11 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id)
|
||||
/*
|
||||
* Find skills
|
||||
*/
|
||||
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = " << std::to_string(parent->GetLOT()) << " )";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
skillQuery.bind(1, (int) parent->GetLOT());
|
||||
|
||||
auto result = skillQuery.execQuery();
|
||||
|
||||
while (!result.eof()) {
|
||||
const auto skillId = static_cast<uint32_t>(result.getIntField(0));
|
||||
|
@ -371,12 +371,12 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
std::stringstream query;
|
||||
|
||||
query << "SELECT * FROM BuffParameters WHERE BuffID = " << std::to_string(buffId) << ";";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT * FROM BuffParameters WHERE BuffID = ?;");
|
||||
query.bind(1, (int) buffId);
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
std::vector<BuffParameter> parameters {};
|
||||
|
||||
while (!result.eof())
|
||||
|
@ -42,6 +42,8 @@ void BuildBorderComponent::OnUse(Entity* originator) {
|
||||
return;
|
||||
}
|
||||
|
||||
inventoryComponent->PushEquippedItems();
|
||||
|
||||
Game::logger->Log("BuildBorderComponent", "Starting with %llu\n", buildArea);
|
||||
|
||||
if (PropertyManagementComponent::Instance() != nullptr) {
|
||||
|
40
dGame/dComponents/CMakeLists.txt
Normal file
40
dGame/dComponents/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
|
||||
"BouncerComponent.cpp"
|
||||
"BuffComponent.cpp"
|
||||
"BuildBorderComponent.cpp"
|
||||
"CharacterComponent.cpp"
|
||||
"Component.cpp"
|
||||
"ControllablePhysicsComponent.cpp"
|
||||
"DestroyableComponent.cpp"
|
||||
"InventoryComponent.cpp"
|
||||
"LUPExhibitComponent.cpp"
|
||||
"MissionComponent.cpp"
|
||||
"MissionOfferComponent.cpp"
|
||||
"ModelComponent.cpp"
|
||||
"ModuleAssemblyComponent.cpp"
|
||||
"MovementAIComponent.cpp"
|
||||
"MovingPlatformComponent.cpp"
|
||||
"PetComponent.cpp"
|
||||
"PhantomPhysicsComponent.cpp"
|
||||
"PossessableComponent.cpp"
|
||||
"PossessorComponent.cpp"
|
||||
"PropertyComponent.cpp"
|
||||
"PropertyEntranceComponent.cpp"
|
||||
"PropertyManagementComponent.cpp"
|
||||
"PropertyVendorComponent.cpp"
|
||||
"ProximityMonitorComponent.cpp"
|
||||
"RacingControlComponent.cpp"
|
||||
"RailActivatorComponent.cpp"
|
||||
"RebuildComponent.cpp"
|
||||
"RenderComponent.cpp"
|
||||
"RigidbodyPhantomPhysicsComponent.cpp"
|
||||
"RocketLaunchLupComponent.cpp"
|
||||
"RocketLaunchpadControlComponent.cpp"
|
||||
"ScriptedActivityComponent.cpp"
|
||||
"ShootingGalleryComponent.cpp"
|
||||
"SimplePhysicsComponent.cpp"
|
||||
"SkillComponent.cpp"
|
||||
"SoundTriggerComponent.cpp"
|
||||
"SwitchComponent.cpp"
|
||||
"VehiclePhysicsComponent.cpp"
|
||||
"VendorComponent.cpp" PARENT_SCOPE)
|
@ -10,7 +10,6 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Item.h"
|
||||
@ -81,13 +80,6 @@ CharacterComponent::~CharacterComponent() {
|
||||
}
|
||||
|
||||
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_IsRacing);
|
||||
if (m_IsRacing) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_VehicleObjectID);
|
||||
outBitStream->Write<uint8_t>(0);
|
||||
}
|
||||
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_Level);
|
||||
outBitStream->Write0();
|
||||
@ -793,3 +785,32 @@ ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) {
|
||||
m_ZoneStatistics.insert({ mapID, {0, 0, 0, 0, 0 } });
|
||||
return m_ZoneStatistics.at(mapID);
|
||||
}
|
||||
|
||||
void CharacterComponent::AddVentureVisionEffect(std::string ventureVisionType) {
|
||||
const auto ventureVisionTypeIterator = m_ActiveVentureVisionEffects.find(ventureVisionType);
|
||||
|
||||
if (ventureVisionTypeIterator != m_ActiveVentureVisionEffects.end()) {
|
||||
ventureVisionTypeIterator->second = ++ventureVisionTypeIterator->second;
|
||||
} else {
|
||||
// If the effect it not found, insert it into the active effects.
|
||||
m_ActiveVentureVisionEffects.insert(std::make_pair(ventureVisionType, 1U));
|
||||
}
|
||||
|
||||
UpdateClientMinimap(true, ventureVisionType);
|
||||
}
|
||||
|
||||
void CharacterComponent::RemoveVentureVisionEffect(std::string ventureVisionType) {
|
||||
const auto ventureVisionTypeIterator = m_ActiveVentureVisionEffects.find(ventureVisionType);
|
||||
|
||||
if (ventureVisionTypeIterator != m_ActiveVentureVisionEffects.end()) {
|
||||
ventureVisionTypeIterator->second = --ventureVisionTypeIterator->second;
|
||||
UpdateClientMinimap(ventureVisionTypeIterator->second != 0U, ventureVisionType);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const {
|
||||
if (!m_Parent) return;
|
||||
AMFArrayValue arrayToSend;
|
||||
arrayToSend.InsertValue(ventureVisionType, showFaction ? static_cast<AMFValue*>(new AMFTrueValue()) : static_cast<AMFValue*>(new AMFFalseValue()));
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", &arrayToSend);
|
||||
}
|
||||
|
@ -143,24 +143,6 @@ public:
|
||||
*/
|
||||
void SetIsRacing(bool isRacing) { m_IsRacing = isRacing; }
|
||||
|
||||
/**
|
||||
* Gets the (optional) object ID of the vehicle the character is currently in
|
||||
* @return the object ID of the vehilce the character is in
|
||||
*/
|
||||
const LWOOBJID GetVehicleObjectID() const { return m_VehicleObjectID; }
|
||||
|
||||
/**
|
||||
* Sets the (optional) object ID of the vehicle the character is currently in
|
||||
* @param vehicleObjectID the ID of the vehicle the character is in
|
||||
*/
|
||||
void SetVehicleObjectID(LWOOBJID vehicleObjectID) { m_VehicleObjectID = vehicleObjectID; }
|
||||
|
||||
/**
|
||||
* Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0
|
||||
* @param value the possesible type to set
|
||||
*/
|
||||
void SetPossessableType(uint8_t value) { m_PossessableType = value; }
|
||||
|
||||
/**
|
||||
* Gets whether this character has PvP enabled, allowing combat between players
|
||||
* @return
|
||||
@ -197,6 +179,18 @@ public:
|
||||
*/
|
||||
void SetLastRocketItemID(LWOOBJID lastRocketItemID) { m_LastRocketItemID = lastRocketItemID; }
|
||||
|
||||
/**
|
||||
* Gets the object ID of the mount item that is being used
|
||||
* @return the object ID of the mount item that is being used
|
||||
*/
|
||||
LWOOBJID GetMountItemID() const { return m_MountItemID; }
|
||||
|
||||
/**
|
||||
* Sets the object ID of the mount item that is being used
|
||||
* @param m_MountItemID the object ID of the mount item that is being used
|
||||
*/
|
||||
void SetMountItemID(LWOOBJID mountItemID) { m_MountItemID = mountItemID; }
|
||||
|
||||
/**
|
||||
* Gives the player rewards for the last level that they leveled up from
|
||||
*/
|
||||
@ -292,23 +286,38 @@ public:
|
||||
*/
|
||||
void UpdatePlayerStatistic(StatisticID updateID, uint64_t updateValue = 1);
|
||||
|
||||
/**
|
||||
* Add a venture vision effect to the player minimap.
|
||||
*/
|
||||
void AddVentureVisionEffect(std::string ventureVisionType);
|
||||
|
||||
/**
|
||||
* Remove a venture vision effect from the player minimap.
|
||||
* When an effect hits 0 active effects, it is deactivated.
|
||||
*/
|
||||
void RemoveVentureVisionEffect(std::string ventureVisionType);
|
||||
|
||||
/**
|
||||
* Update the client minimap to reveal the specified factions
|
||||
*/
|
||||
void UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const;
|
||||
|
||||
/**
|
||||
* Character info regarding this character, including clothing styles, etc.
|
||||
*/
|
||||
Character* m_Character;
|
||||
private:
|
||||
|
||||
/**
|
||||
* The map of active venture vision effects
|
||||
*/
|
||||
std::map<std::string, uint32_t> m_ActiveVentureVisionEffects;
|
||||
|
||||
/**
|
||||
* Whether this character is racing
|
||||
*/
|
||||
bool m_IsRacing;
|
||||
|
||||
/**
|
||||
* The object ID of the vehicle the character is currently in
|
||||
*/
|
||||
LWOOBJID m_VehicleObjectID;
|
||||
|
||||
/**
|
||||
* Possessible type, used by the shooting gallery
|
||||
*/
|
||||
@ -582,6 +591,11 @@ private:
|
||||
* ID of the last rocket used
|
||||
*/
|
||||
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
|
||||
|
||||
/**
|
||||
* Mount Item ID
|
||||
*/
|
||||
LWOOBJID m_MountItemID = LWOOBJID_EMPTY;
|
||||
};
|
||||
|
||||
#endif // CHARACTERCOMPONENT_H
|
||||
|
@ -106,7 +106,7 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
outBitStream->Write0(); //Contains info about immunities this object has, but it's left out for now.
|
||||
}
|
||||
|
||||
outBitStream->Write(m_DirtyHealth || bIsInitialUpdate);
|
||||
outBitStream->Write(m_DirtyHealth || bIsInitialUpdate);
|
||||
if (m_DirtyHealth || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_iHealth);
|
||||
outBitStream->Write(m_fMaxHealth);
|
||||
@ -114,12 +114,12 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
outBitStream->Write(m_fMaxArmor);
|
||||
outBitStream->Write(m_iImagination);
|
||||
outBitStream->Write(m_fMaxImagination);
|
||||
|
||||
|
||||
outBitStream->Write(m_DamageToAbsorb);
|
||||
outBitStream->Write(IsImmune());
|
||||
outBitStream->Write(m_IsGMImmune);
|
||||
outBitStream->Write(m_IsShielded);
|
||||
|
||||
|
||||
outBitStream->Write(m_fMaxHealth);
|
||||
outBitStream->Write(m_fMaxArmor);
|
||||
outBitStream->Write(m_fMaxImagination);
|
||||
@ -130,14 +130,14 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
}
|
||||
|
||||
outBitStream->Write(m_IsSmashable);
|
||||
|
||||
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write(m_IsDead);
|
||||
outBitStream->Write(m_IsSmashed);
|
||||
|
||||
|
||||
if (m_IsSmashable) {
|
||||
outBitStream->Write(m_HasBricks);
|
||||
|
||||
|
||||
if (m_ExplodeFactor != 1.0f) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_ExplodeFactor);
|
||||
@ -146,10 +146,10 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_DirtyHealth = false;
|
||||
}
|
||||
|
||||
|
||||
if (m_DirtyThreatList || bIsInitialUpdate) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_HasThreats);
|
||||
@ -167,7 +167,7 @@ void DestroyableComponent::LoadFromXML(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->LoadFromXML(doc);
|
||||
}
|
||||
@ -187,9 +187,9 @@ void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
Game::logger->Log("DestroyableComponent", "Failed to find dest tag!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto* buffComponent = m_Parent->GetComponent<BuffComponent>();
|
||||
|
||||
|
||||
if (buffComponent != nullptr) {
|
||||
buffComponent->UpdateXml(doc);
|
||||
}
|
||||
@ -235,7 +235,7 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
|
||||
|
||||
delete amount;
|
||||
delete type;
|
||||
}
|
||||
@ -283,7 +283,7 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
|
||||
args.InsertValue("type", type);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
|
||||
|
||||
delete amount;
|
||||
delete type;
|
||||
}
|
||||
@ -329,7 +329,7 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
|
||||
|
||||
delete amount;
|
||||
delete type;
|
||||
}
|
||||
@ -342,7 +342,7 @@ void DestroyableComponent::SetDamageToAbsorb(int32_t value)
|
||||
m_DamageToAbsorb = value;
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetDamageReduction(int32_t value)
|
||||
void DestroyableComponent::SetDamageReduction(int32_t value)
|
||||
{
|
||||
m_DirtyHealth = true;
|
||||
m_DamageReduction = value;
|
||||
@ -354,7 +354,7 @@ void DestroyableComponent::SetIsImmune(bool value)
|
||||
m_ImmuneStacks = value ? 1 : 0;
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetIsGMImmune(bool value)
|
||||
void DestroyableComponent::SetIsGMImmune(bool value)
|
||||
{
|
||||
m_DirtyHealth = true;
|
||||
m_IsGMImmune = value;
|
||||
@ -375,11 +375,11 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
||||
m_FactionIDs.push_back(factionID);
|
||||
m_DirtyHealth = true;
|
||||
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT enemyList FROM Factions WHERE faction = ?;");
|
||||
query.bind(1, (int) factionID);
|
||||
|
||||
query << "SELECT enemyList FROM Factions WHERE faction = " << std::to_string(factionID);
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof()) return;
|
||||
|
||||
@ -389,10 +389,10 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
||||
|
||||
std::stringstream ss(list_string);
|
||||
std::string token;
|
||||
|
||||
|
||||
while (std::getline(ss, token, ',')) {
|
||||
if (token.empty()) continue;
|
||||
|
||||
|
||||
auto id = std::stoi(token);
|
||||
|
||||
auto exclude = std::find(m_FactionIDs.begin(), m_FactionIDs.end(), id) != m_FactionIDs.end();
|
||||
@ -406,7 +406,7 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
AddEnemyFaction(id);
|
||||
}
|
||||
|
||||
@ -522,7 +522,7 @@ bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignor
|
||||
if (targetQuickbuild != nullptr)
|
||||
{
|
||||
const auto state = targetQuickbuild->GetState();
|
||||
|
||||
|
||||
if (state != REBUILD_COMPLETED)
|
||||
{
|
||||
return false;
|
||||
@ -562,7 +562,7 @@ void DestroyableComponent::Imagine(const int32_t deltaImagination)
|
||||
{
|
||||
auto current = static_cast<int32_t>(GetImagination());
|
||||
const auto max = static_cast<int32_t>(GetMaxImagination());
|
||||
|
||||
|
||||
current += deltaImagination;
|
||||
|
||||
current = std::min(current, max);
|
||||
@ -635,7 +635,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
|
||||
damage -= absorbDamage;
|
||||
absorb -= absorbDamage;
|
||||
|
||||
|
||||
const auto armorDamage = std::min(damage, armor);
|
||||
|
||||
damage -= armorDamage;
|
||||
@ -657,7 +657,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
{
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
|
||||
auto* attacker = EntityManager::Instance()->GetEntity(source);
|
||||
m_Parent->OnHit(attacker);
|
||||
m_Parent->OnHitOrHealResult(attacker, sourceDamage);
|
||||
@ -696,10 +696,10 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
if (owner != nullptr)
|
||||
{
|
||||
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
|
||||
|
||||
owner = owner->GetOwner(); // If the owner is overwritten, we collect that here
|
||||
|
||||
auto* team = TeamManager::Instance()->GetTeam(owner->GetObjectID());
|
||||
|
||||
const auto isEnemy = m_Parent->GetComponent<BaseCombatAIComponent>() != nullptr;
|
||||
|
||||
auto* inventoryComponent = owner->GetComponent<InventoryComponent>();
|
||||
@ -736,7 +736,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const auto isPlayer = m_Parent->IsPlayer();
|
||||
|
||||
GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
@ -765,14 +765,14 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
auto* member = EntityManager::Instance()->GetEntity(specificOwner);
|
||||
|
||||
if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const auto memberId : team->members) { // Free for all
|
||||
auto* member = EntityManager::Instance()->GetEntity(memberId);
|
||||
|
||||
if (member == nullptr) continue;
|
||||
|
||||
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -846,7 +846,7 @@ void DestroyableComponent::PopImmunity(int32_t stacks)
|
||||
m_ImmuneStacks -= stacks;
|
||||
}
|
||||
|
||||
void DestroyableComponent::FixStats()
|
||||
void DestroyableComponent::FixStats()
|
||||
{
|
||||
auto* entity = GetParent();
|
||||
|
||||
@ -904,7 +904,7 @@ void DestroyableComponent::FixStats()
|
||||
|
||||
// Add the stats
|
||||
const auto& info = mission->GetClientInfo();
|
||||
|
||||
|
||||
maxHealth += info.reward_maxhealth;
|
||||
maxImagination += info.reward_maximagination;
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "dZoneManager.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "dConfig.h"
|
||||
#include "eItemType.h"
|
||||
|
||||
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent)
|
||||
{
|
||||
@ -51,18 +53,18 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
|
||||
auto items = inventoryComponentTable->Query([=](const CDInventoryComponent entry) { return entry.id == componentId; });
|
||||
|
||||
auto slot = 0u;
|
||||
|
||||
|
||||
for (const auto& item : items)
|
||||
{
|
||||
if (!item.equip || !Inventory::IsValidItem(item.itemid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
|
||||
const auto& info = Inventory::FindItemComponent(item.itemid);
|
||||
|
||||
|
||||
UpdateSlot(info.equipLocation, { id, static_cast<LOT>(item.itemid), item.count, slot++ });
|
||||
}
|
||||
}
|
||||
@ -178,7 +180,7 @@ void InventoryComponent::AddItem(
|
||||
auto* missions = static_cast<MissionComponent*>(this->m_Parent->GetComponent(COMPONENT_TYPE_MISSION));
|
||||
|
||||
auto* inventory = GetInventory(inventoryType);
|
||||
|
||||
|
||||
if (!config.empty() || bound)
|
||||
{
|
||||
const auto slot = preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot) ? preferredSlot : inventory->FindEmptySlot();
|
||||
@ -189,6 +191,7 @@ void InventoryComponent::AddItem(
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* item = new Item(lot, inventory, slot, count, config, parent, showFlyingLoot, isModMoveAndEquip, subKey, bound, lootSourceType);
|
||||
|
||||
if (missions != nullptr && !IsTransferInventory(inventoryType))
|
||||
@ -200,14 +203,15 @@ void InventoryComponent::AddItem(
|
||||
}
|
||||
|
||||
const auto info = Inventory::FindItemComponent(lot);
|
||||
|
||||
|
||||
auto left = count;
|
||||
|
||||
int32_t outOfSpace = 0;
|
||||
|
||||
auto stack = static_cast<uint32_t>(info.stackSize);
|
||||
|
||||
if (inventoryType == eInventoryType::BRICKS)
|
||||
// info.itemType of 1 is item type brick
|
||||
if (inventoryType == eInventoryType::BRICKS || (stack == 0 && info.itemType == 1))
|
||||
{
|
||||
stack = 999;
|
||||
}
|
||||
@ -215,7 +219,7 @@ void InventoryComponent::AddItem(
|
||||
{
|
||||
stack = 1;
|
||||
}
|
||||
|
||||
|
||||
auto* existing = FindItemByLot(lot, inventoryType);
|
||||
|
||||
if (existing != nullptr)
|
||||
@ -239,7 +243,7 @@ void InventoryComponent::AddItem(
|
||||
const auto size = std::min(left, stack);
|
||||
|
||||
left -= size;
|
||||
|
||||
|
||||
int32_t slot;
|
||||
|
||||
if (preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot))
|
||||
@ -252,7 +256,7 @@ void InventoryComponent::AddItem(
|
||||
{
|
||||
slot = inventory->FindEmptySlot();
|
||||
}
|
||||
|
||||
|
||||
if (slot == -1)
|
||||
{
|
||||
auto* player = dynamic_cast<Player*>(GetParent());
|
||||
@ -275,9 +279,9 @@ void InventoryComponent::AddItem(
|
||||
{
|
||||
GameMessages::SendDropClientLoot(this->m_Parent, this->m_Parent->GetObjectID(), lot, 0, this->m_Parent->GetPosition(), 1);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -326,7 +330,7 @@ void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInvent
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
const auto delta = std::min<uint32_t>(left, item->GetCount());
|
||||
|
||||
item->SetCount(item->GetCount() - delta);
|
||||
@ -341,19 +345,17 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto* origin = item->GetInventory();
|
||||
|
||||
|
||||
const auto lot = item->GetLot();
|
||||
|
||||
|
||||
if (item->GetConfig().empty() && !item->GetBound() || (item->GetBound() && item->GetInfo().isBOP))
|
||||
{
|
||||
auto left = std::min<uint32_t>(count, origin->GetLotCount(lot));
|
||||
|
||||
while (left > 0)
|
||||
{
|
||||
item = origin->FindItemByLot(lot, ignoreEquipped);
|
||||
|
||||
if (item == nullptr)
|
||||
{
|
||||
item = origin->FindItemByLot(lot, false);
|
||||
@ -383,7 +385,7 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
|
||||
{
|
||||
config.push_back(data->Copy());
|
||||
}
|
||||
|
||||
|
||||
const auto delta = std::min<uint32_t>(item->GetCount(), count);
|
||||
|
||||
AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, item->GetBound(), preferredSlot);
|
||||
@ -441,7 +443,7 @@ Item* InventoryComponent::FindItemByLot(const LOT lot, eInventoryType inventoryT
|
||||
return inventory->FindItemByLot(lot, ignoreEquipped, ignoreBound);
|
||||
}
|
||||
|
||||
Item* InventoryComponent::FindItemBySubKey(LWOOBJID id, eInventoryType inventoryType)
|
||||
Item* InventoryComponent::FindItemBySubKey(LWOOBJID id, eInventoryType inventoryType)
|
||||
{
|
||||
if (inventoryType == INVALID)
|
||||
{
|
||||
@ -600,7 +602,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document)
|
||||
unsigned int count;
|
||||
bool bound;
|
||||
LWOOBJID subKey = LWOOBJID_EMPTY;
|
||||
|
||||
|
||||
itemElement->QueryAttribute("id", &id);
|
||||
itemElement->QueryAttribute("l", &lot);
|
||||
itemElement->QueryAttribute("eq", &equipped);
|
||||
@ -611,23 +613,23 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document)
|
||||
|
||||
// Begin custom xml
|
||||
auto parent = LWOOBJID_EMPTY;
|
||||
|
||||
|
||||
itemElement->QueryAttribute("parent", &parent);
|
||||
// End custom xml
|
||||
|
||||
std::vector<LDFBaseData*> config;
|
||||
|
||||
auto* extraInfo = itemElement->FirstChildElement("x");
|
||||
|
||||
|
||||
if (extraInfo)
|
||||
{
|
||||
std::string modInfo = extraInfo->Attribute("ma");
|
||||
|
||||
|
||||
LDFBaseData* moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1)));
|
||||
|
||||
|
||||
config.push_back(moduleAssembly);
|
||||
}
|
||||
|
||||
|
||||
const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey);
|
||||
|
||||
if (equipped)
|
||||
@ -705,7 +707,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document)
|
||||
|
||||
bags->LinkEndChild(bag);
|
||||
}
|
||||
|
||||
|
||||
auto* items = inventoryElement->FirstChildElement("items");
|
||||
|
||||
if (items == nullptr)
|
||||
@ -759,10 +761,10 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document)
|
||||
|
||||
itemElement->LinkEndChild(extraInfo);
|
||||
}
|
||||
|
||||
|
||||
bagElement->LinkEndChild(itemElement);
|
||||
}
|
||||
|
||||
|
||||
items->LinkEndChild(bagElement);
|
||||
}
|
||||
}
|
||||
@ -772,13 +774,13 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
|
||||
if (bIsInitialUpdate || m_Dirty)
|
||||
{
|
||||
outBitStream->Write(true);
|
||||
|
||||
|
||||
outBitStream->Write<uint32_t>(m_Equipped.size());
|
||||
|
||||
for (const auto& pair : m_Equipped)
|
||||
{
|
||||
const auto item = pair.second;
|
||||
|
||||
|
||||
if (bIsInitialUpdate)
|
||||
{
|
||||
AddItemSkills(item.lot);
|
||||
@ -786,12 +788,12 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
|
||||
|
||||
outBitStream->Write(item.id);
|
||||
outBitStream->Write(item.lot);
|
||||
|
||||
|
||||
outBitStream->Write0();
|
||||
|
||||
|
||||
outBitStream->Write(item.count > 0);
|
||||
if (item.count > 0) outBitStream->Write(item.count);
|
||||
|
||||
|
||||
outBitStream->Write(item.slot != 0);
|
||||
if (item.slot != 0) outBitStream->Write<uint16_t>(item.slot);
|
||||
|
||||
@ -818,7 +820,7 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
|
||||
outBitStream->Write(ldfStream);
|
||||
}
|
||||
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write1();
|
||||
}
|
||||
|
||||
m_Dirty = false;
|
||||
@ -827,7 +829,7 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
|
||||
{
|
||||
outBitStream->Write(false);
|
||||
}
|
||||
|
||||
|
||||
outBitStream->Write(false);
|
||||
}
|
||||
|
||||
@ -836,7 +838,7 @@ void InventoryComponent::ResetFlags()
|
||||
m_Dirty = false;
|
||||
}
|
||||
|
||||
void InventoryComponent::Update(float deltaTime)
|
||||
void InventoryComponent::Update(float deltaTime)
|
||||
{
|
||||
for (auto* set : m_Itemsets)
|
||||
{
|
||||
@ -865,7 +867,7 @@ void InventoryComponent::UpdateSlot(const std::string& location, EquippedItem it
|
||||
UnEquipItem(old);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_Equipped.insert_or_assign(location, item);
|
||||
|
||||
m_Dirty = true;
|
||||
@ -877,14 +879,14 @@ void InventoryComponent::RemoveSlot(const std::string& location)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_Equipped.erase(location);
|
||||
|
||||
m_Dirty = true;
|
||||
}
|
||||
|
||||
void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
{
|
||||
{
|
||||
if (!Inventory::IsValidItem(item->GetLot()))
|
||||
{
|
||||
return;
|
||||
@ -985,19 +987,11 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
// #107
|
||||
auto* possessorComponent = m_Parent->GetComponent<PossessorComponent>();
|
||||
|
||||
if (possessorComponent != nullptr)
|
||||
{
|
||||
previousPossessorID = possessorComponent->GetPossessable();
|
||||
possessorComponent->SetPossessable(carEntity->GetObjectID());
|
||||
}
|
||||
if (possessorComponent) possessorComponent->SetPossessable(carEntity->GetObjectID());
|
||||
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
|
||||
if (characterComponent != nullptr)
|
||||
{
|
||||
characterComponent->SetIsRacing(true);
|
||||
characterComponent->SetVehicleObjectID(carEntity->GetObjectID());
|
||||
}
|
||||
if (characterComponent) characterComponent->SetIsRacing(true);
|
||||
|
||||
EntityManager::Instance()->ConstructEntity(carEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
@ -1030,14 +1024,14 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == ITEM_TYPE_LOOT_MODEL || type == ITEM_TYPE_VEHICLE)
|
||||
|
||||
if (type == eItemType::ITEM_TYPE_LOOT_MODEL || type == eItemType::ITEM_TYPE_VEHICLE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type != ITEM_TYPE_LOOT_MODEL && type != ITEM_TYPE_MODEL)
|
||||
if (type != eItemType::ITEM_TYPE_LOOT_MODEL && type != eItemType::ITEM_TYPE_MODEL)
|
||||
{
|
||||
if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent))
|
||||
{
|
||||
@ -1049,7 +1043,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
const auto lot = item->GetLot();
|
||||
|
||||
CheckItemSet(lot);
|
||||
|
||||
|
||||
for (auto* set : m_Itemsets)
|
||||
{
|
||||
set->OnEquip(lot);
|
||||
@ -1079,7 +1073,7 @@ void InventoryComponent::UnEquipItem(Item* item)
|
||||
}
|
||||
|
||||
const auto lot = item->GetLot();
|
||||
|
||||
|
||||
if (!Inventory::IsValidItem(lot))
|
||||
{
|
||||
return;
|
||||
@ -1097,7 +1091,7 @@ void InventoryComponent::UnEquipItem(Item* item)
|
||||
RemoveItemSkills(item->GetLot());
|
||||
|
||||
RemoveSlot(item->GetInfo().equipLocation);
|
||||
|
||||
|
||||
PurgeProxies(item);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
@ -1115,7 +1109,7 @@ void InventoryComponent::ApplyBuff(Item* item) const
|
||||
const auto buffs = FindBuffs(item, true);
|
||||
|
||||
for (const auto buff : buffs)
|
||||
{
|
||||
{
|
||||
SkillComponent::HandleUnmanaged(buff, m_Parent->GetObjectID());
|
||||
}
|
||||
}
|
||||
@ -1165,6 +1159,18 @@ void InventoryComponent::PopEquippedItems()
|
||||
item->Equip();
|
||||
}
|
||||
|
||||
m_Pushed.clear();
|
||||
|
||||
auto destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
// Reset stats to full
|
||||
if (destroyableComponent) {
|
||||
destroyableComponent->SetHealth(static_cast<int32_t>(destroyableComponent->GetMaxHealth()));
|
||||
destroyableComponent->SetArmor(static_cast<int32_t>(destroyableComponent->GetMaxArmor()));
|
||||
destroyableComponent->SetImagination(static_cast<int32_t>(destroyableComponent->GetMaxImagination()));
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
m_Dirty = true;
|
||||
}
|
||||
|
||||
@ -1182,22 +1188,21 @@ bool InventoryComponent::IsEquipped(const LOT lot) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void InventoryComponent::CheckItemSet(const LOT lot)
|
||||
{
|
||||
void InventoryComponent::CheckItemSet(const LOT lot) {
|
||||
// Check if the lot is in the item set cache
|
||||
if (std::find(m_ItemSetsChecked.begin(), m_ItemSetsChecked.end(), lot) != m_ItemSetsChecked.end())
|
||||
{
|
||||
if (std::find(m_ItemSetsChecked.begin(), m_ItemSetsChecked.end(), lot) != m_ItemSetsChecked.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream query;
|
||||
const std::string lot_query = "%" + std::to_string(lot) + "%";
|
||||
|
||||
query << "SELECT setID FROM ItemSets WHERE itemIDs LIKE '%" << std::to_string(lot) << "%'";
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT setID FROM ItemSets WHERE itemIDs LIKE ?;");
|
||||
query.bind(1, lot_query.c_str());
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
while (!result.eof())
|
||||
{
|
||||
while (!result.eof()) {
|
||||
const auto id = result.getIntField(0);
|
||||
|
||||
bool found = false;
|
||||
@ -1223,11 +1228,11 @@ void InventoryComponent::CheckItemSet(const LOT lot)
|
||||
}
|
||||
|
||||
m_ItemSetsChecked.push_back(lot);
|
||||
|
||||
|
||||
result.finalize();
|
||||
}
|
||||
|
||||
void InventoryComponent::SetConsumable(LOT lot)
|
||||
void InventoryComponent::SetConsumable(LOT lot)
|
||||
{
|
||||
m_Consumable = lot;
|
||||
}
|
||||
@ -1247,7 +1252,7 @@ void InventoryComponent::AddItemSkills(const LOT lot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const auto index = m_Skills.find(slot);
|
||||
|
||||
const auto skill = FindSkill(lot);
|
||||
@ -1272,14 +1277,14 @@ void InventoryComponent::AddItemSkills(const LOT lot)
|
||||
void InventoryComponent::RemoveItemSkills(const LOT lot)
|
||||
{
|
||||
const auto info = Inventory::FindItemComponent(lot);
|
||||
|
||||
|
||||
const auto slot = FindBehaviorSlot(static_cast<eItemType>(info.itemType));
|
||||
|
||||
|
||||
if (slot == BehaviorSlot::Invalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const auto index = m_Skills.find(slot);
|
||||
|
||||
if (index == m_Skills.end())
|
||||
@ -1292,7 +1297,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot)
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
|
||||
m_Skills.erase(slot);
|
||||
|
||||
|
||||
if (slot == BehaviorSlot::Primary)
|
||||
{
|
||||
m_Skills.insert_or_assign(BehaviorSlot::Primary, 1);
|
||||
@ -1301,7 +1306,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot)
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::TriggerPassiveAbility(PassiveAbilityTrigger trigger)
|
||||
void InventoryComponent::TriggerPassiveAbility(PassiveAbilityTrigger trigger)
|
||||
{
|
||||
for (auto* set : m_Itemsets)
|
||||
{
|
||||
@ -1328,7 +1333,7 @@ bool InventoryComponent::HasAnyPassive(const std::vector<ItemSetPassiveAbilityID
|
||||
return false;
|
||||
}
|
||||
|
||||
void InventoryComponent::DespawnPet()
|
||||
void InventoryComponent::DespawnPet()
|
||||
{
|
||||
auto* current = PetComponent::GetActivePet(m_Parent->GetObjectID());
|
||||
|
||||
@ -1338,7 +1343,7 @@ void InventoryComponent::DespawnPet()
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::SpawnPet(Item* item)
|
||||
void InventoryComponent::SpawnPet(Item* item)
|
||||
{
|
||||
auto* current = PetComponent::GetActivePet(m_Parent->GetObjectID());
|
||||
|
||||
@ -1352,16 +1357,24 @@ void InventoryComponent::SpawnPet(Item* item)
|
||||
}
|
||||
}
|
||||
|
||||
// First check if we can summon the pet. You need 1 imagination to do so.
|
||||
auto destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (Game::config->GetValue("pets_take_imagination") == "1" && destroyableComponent && destroyableComponent->GetImagination() <= 0) {
|
||||
GameMessages::SendUseItemRequirementsResponse(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), UseItemResponse::NoImaginationForPet);
|
||||
return;
|
||||
}
|
||||
|
||||
EntityInfo info {};
|
||||
info.lot = item->GetLot();
|
||||
info.pos = m_Parent->GetPosition();
|
||||
info.rot = NiQuaternion::IDENTITY;
|
||||
info.spawnerID = m_Parent->GetObjectID();
|
||||
|
||||
|
||||
auto* pet = EntityManager::Instance()->CreateEntity(info);
|
||||
|
||||
auto* petComponent = pet->GetComponent<PetComponent>();
|
||||
|
||||
|
||||
if (petComponent != nullptr)
|
||||
{
|
||||
petComponent->Activate(item);
|
||||
@ -1370,7 +1383,7 @@ void InventoryComponent::SpawnPet(Item* item)
|
||||
EntityManager::Instance()->ConstructEntity(pet);
|
||||
}
|
||||
|
||||
void InventoryComponent::SetDatabasePet(LWOOBJID id, const DatabasePet& data)
|
||||
void InventoryComponent::SetDatabasePet(LWOOBJID id, const DatabasePet& data)
|
||||
{
|
||||
m_Pets.insert_or_assign(id, data);
|
||||
}
|
||||
@ -1391,7 +1404,7 @@ bool InventoryComponent::IsPet(LWOOBJID id) const
|
||||
return pair != m_Pets.end();
|
||||
}
|
||||
|
||||
void InventoryComponent::RemoveDatabasePet(LWOOBJID id)
|
||||
void InventoryComponent::RemoveDatabasePet(LWOOBJID id)
|
||||
{
|
||||
m_Pets.erase(id);
|
||||
}
|
||||
@ -1399,22 +1412,22 @@ void InventoryComponent::RemoveDatabasePet(LWOOBJID id)
|
||||
BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type)
|
||||
{
|
||||
switch (type) {
|
||||
case ITEM_TYPE_HAT:
|
||||
case eItemType::ITEM_TYPE_HAT:
|
||||
return BehaviorSlot::Head;
|
||||
case ITEM_TYPE_NECK:
|
||||
case eItemType::ITEM_TYPE_NECK:
|
||||
return BehaviorSlot::Neck;
|
||||
case ITEM_TYPE_LEFT_HAND:
|
||||
case eItemType::ITEM_TYPE_LEFT_HAND:
|
||||
return BehaviorSlot::Offhand;
|
||||
case ITEM_TYPE_RIGHT_HAND:
|
||||
case eItemType::ITEM_TYPE_RIGHT_HAND:
|
||||
return BehaviorSlot::Primary;
|
||||
case ITEM_TYPE_CONSUMABLE:
|
||||
case eItemType::ITEM_TYPE_CONSUMABLE:
|
||||
return BehaviorSlot::Consumable;
|
||||
default:
|
||||
return BehaviorSlot::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
bool InventoryComponent::IsTransferInventory(eInventoryType type)
|
||||
bool InventoryComponent::IsTransferInventory(eInventoryType type)
|
||||
{
|
||||
return type == VENDOR_BUYBACK || type == VAULT_ITEMS || type == VAULT_MODELS || type == TEMP_ITEMS || type == TEMP_MODELS;
|
||||
}
|
||||
@ -1465,11 +1478,12 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (missions != nullptr && castOnEquip)
|
||||
{
|
||||
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, result.skillID);
|
||||
}
|
||||
|
||||
// If item is not a proxy, add its buff to the added buffs.
|
||||
if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry.behaviorID));
|
||||
}
|
||||
@ -1478,18 +1492,18 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
|
||||
return buffs;
|
||||
}
|
||||
|
||||
void InventoryComponent::SetNPCItems(const std::vector<LOT>& items)
|
||||
void InventoryComponent::SetNPCItems(const std::vector<LOT>& items)
|
||||
{
|
||||
m_Equipped.clear();
|
||||
|
||||
auto slot = 0u;
|
||||
|
||||
|
||||
for (const auto& item : items)
|
||||
{
|
||||
const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
|
||||
const auto& info = Inventory::FindItemComponent(item);
|
||||
|
||||
|
||||
UpdateSlot(info.equipLocation, { id, static_cast<LOT>(item), 1, slot++ }, true);
|
||||
}
|
||||
|
||||
@ -1524,9 +1538,9 @@ std::vector<Item*> InventoryComponent::GenerateProxies(Item* parent)
|
||||
{
|
||||
return proxies;
|
||||
}
|
||||
|
||||
|
||||
subItems.erase(std::remove_if(subItems.begin(), subItems.end(), ::isspace), subItems.end());
|
||||
|
||||
|
||||
std::stringstream stream(subItems);
|
||||
std::string segment;
|
||||
std::vector<uint32_t> lots;
|
||||
@ -1541,7 +1555,7 @@ std::vector<Item*> InventoryComponent::GenerateProxies(Item* parent)
|
||||
{
|
||||
Game::logger->Log("InventoryComponent", "Failed to parse proxy (%s): (%s)!\n", segment.c_str(), exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto lot : lots)
|
||||
{
|
||||
@ -1558,7 +1572,7 @@ std::vector<Item*> InventoryComponent::GenerateProxies(Item* parent)
|
||||
|
||||
proxies.push_back(proxy);
|
||||
}
|
||||
|
||||
|
||||
return proxies;
|
||||
}
|
||||
|
||||
@ -1567,7 +1581,7 @@ std::vector<Item*> InventoryComponent::FindProxies(const LWOOBJID parent)
|
||||
auto* inventory = GetInventory(ITEM_SETS);
|
||||
|
||||
std::vector<Item*> proxies;
|
||||
|
||||
|
||||
for (const auto& pair : inventory->GetItems())
|
||||
{
|
||||
auto* item = pair.second;
|
||||
@ -1609,7 +1623,7 @@ bool InventoryComponent::IsParentValid(Item* root)
|
||||
}
|
||||
|
||||
const auto id = root->GetId();
|
||||
|
||||
|
||||
for (const auto& pair : m_Inventories)
|
||||
{
|
||||
const auto items = pair.second->GetItems();
|
||||
@ -1631,7 +1645,7 @@ bool InventoryComponent::IsParentValid(Item* root)
|
||||
void InventoryComponent::CheckProxyIntegrity()
|
||||
{
|
||||
std::vector<Item*> dead;
|
||||
|
||||
|
||||
for (const auto& pair : m_Inventories)
|
||||
{
|
||||
const auto& items = pair.second->GetItems();
|
||||
@ -1646,7 +1660,7 @@ void InventoryComponent::CheckProxyIntegrity()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (IsValidProxy(parent))
|
||||
{
|
||||
continue;
|
||||
@ -1671,7 +1685,7 @@ void InventoryComponent::CheckProxyIntegrity()
|
||||
for (const auto& candidate : items)
|
||||
{
|
||||
auto* item = candidate.second;
|
||||
|
||||
|
||||
const auto parent = item->GetParent();
|
||||
|
||||
if (parent != LWOOBJID_EMPTY)
|
||||
@ -1703,7 +1717,7 @@ void InventoryComponent::CheckProxyIntegrity()
|
||||
void InventoryComponent::PurgeProxies(Item* item)
|
||||
{
|
||||
const auto root = item->GetParent();
|
||||
|
||||
|
||||
if (root != LWOOBJID_EMPTY)
|
||||
{
|
||||
item = FindItemById(root);
|
||||
@ -1712,7 +1726,7 @@ void InventoryComponent::PurgeProxies(Item* item)
|
||||
{
|
||||
UnEquipItem(item);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1726,7 +1740,7 @@ void InventoryComponent::PurgeProxies(Item* item)
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document)
|
||||
void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document)
|
||||
{
|
||||
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet");
|
||||
|
||||
@ -1744,7 +1758,7 @@ void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document)
|
||||
LWOOBJID id;
|
||||
LOT lot;
|
||||
int32_t moderationStatus;
|
||||
|
||||
|
||||
petElement->QueryAttribute("id", &id);
|
||||
petElement->QueryAttribute("l", &lot);
|
||||
petElement->QueryAttribute("m", &moderationStatus);
|
||||
@ -1761,7 +1775,7 @@ void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document)
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document)
|
||||
void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document)
|
||||
{
|
||||
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet");
|
||||
|
||||
@ -1783,8 +1797,7 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document)
|
||||
petElement->SetAttribute("m", pet.second.moderationState);
|
||||
petElement->SetAttribute("n", pet.second.name.c_str());
|
||||
petElement->SetAttribute("t", 0);
|
||||
|
||||
|
||||
petInventoryElement->LinkEndChild(petElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifndef INVENTORYCOMPONENT_H
|
||||
#define INVENTORYCOMPONENT_H
|
||||
|
||||
#include <map>
|
||||
#include <stack>
|
||||
@ -22,6 +25,8 @@ class ItemSet;
|
||||
|
||||
typedef std::map<std::string, EquippedItem> EquipmentMap;
|
||||
|
||||
enum class eItemType : int32_t;
|
||||
|
||||
/**
|
||||
* Handles the inventory of entity, including the items they possess and have equipped. An entity can have inventories
|
||||
* of different types, each type representing a different group of items, see `eInventoryType` for a list of
|
||||
@ -445,3 +450,5 @@ private:
|
||||
*/
|
||||
void UpdatePetXml(tinyxml2::XMLDocument* document);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -450,11 +450,11 @@ const std::vector<uint32_t>& MissionComponent::QueryAchievements(MissionTaskType
|
||||
}
|
||||
|
||||
bool MissionComponent::RequiresItem(const LOT lot) {
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT type FROM Objects WHERE id = ?;");
|
||||
query.bind(1, (int) lot);
|
||||
|
||||
query << "SELECT type FROM Objects WHERE id = " << std::to_string(lot);
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof()) {
|
||||
return false;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "dChatFilter.h"
|
||||
#include "Database.h"
|
||||
|
||||
@ -81,6 +82,20 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(par
|
||||
if (!checkPreconditions.empty()) {
|
||||
SetPreconditions(checkPreconditions);
|
||||
}
|
||||
// Get the imagination drain rate from the CDClient
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT imaginationDrainRate FROM PetComponent WHERE id = ?;");
|
||||
|
||||
query.bind(1, static_cast<int>(componentId));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
// Should a result not exist for this pet default to 60 seconds.
|
||||
if (!result.eof() && !result.fieldIsNull(0)) {
|
||||
imaginationDrainRate = result.getFloatField(0, 60.0f);
|
||||
} else {
|
||||
imaginationDrainRate = 60.0f;
|
||||
}
|
||||
result.finalize();
|
||||
}
|
||||
|
||||
void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags)
|
||||
@ -172,13 +187,12 @@ void PetComponent::OnUse(Entity* originator)
|
||||
|
||||
std::string buildFile;
|
||||
|
||||
if (cached == buildCache.end())
|
||||
{
|
||||
std::stringstream query;
|
||||
if (cached == buildCache.end()) {
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;");
|
||||
query.bind(1, (int) m_Parent->GetLOT());
|
||||
|
||||
query << "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = " << std::to_string(m_Parent->GetLOT()) << ";";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof())
|
||||
{
|
||||
@ -637,7 +651,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
|
||||
|
||||
inventoryComponent->SetDatabasePet(petSubKey, databasePet);
|
||||
|
||||
Activate(item, false);
|
||||
Activate(item, false, true);
|
||||
|
||||
m_Timer = 0;
|
||||
|
||||
@ -898,8 +912,10 @@ void PetComponent::Wander()
|
||||
m_Timer += (m_MovementAI->GetCurrentPosition().x - destination.x) / info.wanderSpeed;
|
||||
}
|
||||
|
||||
void PetComponent::Activate(Item* item, bool registerPet)
|
||||
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming)
|
||||
{
|
||||
AddDrainImaginationTimer(item, fromTaming);
|
||||
|
||||
m_ItemId = item->GetId();
|
||||
m_DatabaseId = item->GetSubKey();
|
||||
|
||||
@ -969,6 +985,44 @@ void PetComponent::Activate(Item* item, bool registerPet)
|
||||
GameMessages::SendShowPetActionButton(m_Owner, 3, true, owner->GetSystemAddress());
|
||||
}
|
||||
|
||||
void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
|
||||
if (Game::config->GetValue("pets_take_imagination") != "1") return;
|
||||
|
||||
auto playerInventory = item->GetInventory();
|
||||
if (!playerInventory) return;
|
||||
|
||||
auto playerInventoryComponent = playerInventory->GetComponent();
|
||||
if (!playerInventoryComponent) return;
|
||||
|
||||
auto playerEntity = playerInventoryComponent->GetParent();
|
||||
if (!playerEntity) return;
|
||||
|
||||
auto playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>();
|
||||
if (!playerDestroyableComponent) return;
|
||||
|
||||
// Drain by 1 when you summon pet or when this method is called, but not when we have just tamed this pet.
|
||||
if (!fromTaming) playerDestroyableComponent->Imagine(-1);
|
||||
|
||||
// Set this to a variable so when this is called back from the player the timer doesn't fire off.
|
||||
m_Parent->AddCallbackTimer(imaginationDrainRate, [playerDestroyableComponent, this, item](){
|
||||
if (!playerDestroyableComponent) {
|
||||
Game::logger->Log("PetComponent", "No petComponent and/or no playerDestroyableComponent\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are out of imagination despawn the pet.
|
||||
if (playerDestroyableComponent->GetImagination() == 0) {
|
||||
this->Deactivate();
|
||||
auto playerEntity = playerDestroyableComponent->GetParent();
|
||||
if (!playerEntity) return;
|
||||
|
||||
GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), UseItemResponse::NoImaginationForPet);
|
||||
}
|
||||
|
||||
this->AddDrainImaginationTimer(item);
|
||||
});
|
||||
}
|
||||
|
||||
void PetComponent::Deactivate()
|
||||
{
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true);
|
||||
|
@ -82,7 +82,7 @@ public:
|
||||
* @param item the item to create the pet from
|
||||
* @param registerPet notifies the client that the pet was spawned, not necessary if this pet is being tamed
|
||||
*/
|
||||
void Activate(Item* item, bool registerPet = true);
|
||||
void Activate(Item* item, bool registerPet = true, bool fromTaming = false);
|
||||
|
||||
/**
|
||||
* Despawns the pet
|
||||
@ -203,6 +203,14 @@ public:
|
||||
*/
|
||||
static PetComponent* GetActivePet(LWOOBJID owner);
|
||||
|
||||
/**
|
||||
* Adds the timer to the owner of this pet to drain imagination at the rate
|
||||
* specified by the parameter imaginationDrainRate
|
||||
*
|
||||
* @param item The item that represents this pet in the inventory.
|
||||
*/
|
||||
void AddDrainImaginationTimer(Item* item, bool fromTaming = false);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@ -346,4 +354,9 @@ private:
|
||||
* Preconditions that need to be met before an entity can tame this pet
|
||||
*/
|
||||
PreconditionExpression* m_Preconditions;
|
||||
|
||||
/**
|
||||
* The rate at which imagination is drained from the user for having the pet out.
|
||||
*/
|
||||
float imaginationDrainRate;
|
||||
};
|
@ -1,51 +1,46 @@
|
||||
#include "PossessableComponent.h"
|
||||
|
||||
#include "PossessorComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Inventory.h"
|
||||
#include "Item.h"
|
||||
|
||||
PossessableComponent::PossessableComponent(Entity* parent) : Component(parent)
|
||||
{
|
||||
m_Possessor = LWOOBJID_EMPTY;
|
||||
PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) : Component(parent){
|
||||
m_Possessor = LWOOBJID_EMPTY;
|
||||
CDItemComponent item = Inventory::FindItemComponent(m_Parent->GetLOT());
|
||||
m_AnimationFlag = static_cast<eAnimationFlags>(item.animationFlag);
|
||||
|
||||
// Get the possession Type from the CDClient
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT possessionType, depossessOnHit FROM PossessableComponent WHERE id = ?;");
|
||||
|
||||
query.bind(1, static_cast<int>(componentId));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
// Should a result not exist for this default to attached visible
|
||||
if (!result.eof()) {
|
||||
m_PossessionType = static_cast<ePossessionType>(result.getIntField(0, 0));
|
||||
m_DepossessOnHit = static_cast<bool>(result.getIntField(1, 0));
|
||||
} else {
|
||||
m_PossessionType = ePossessionType::ATTACHED_VISIBLE;
|
||||
m_DepossessOnHit = false;
|
||||
}
|
||||
result.finalize();
|
||||
}
|
||||
|
||||
PossessableComponent::~PossessableComponent()
|
||||
{
|
||||
|
||||
}
|
||||
void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate);
|
||||
if (m_DirtyPossessable || bIsInitialUpdate) {
|
||||
m_DirtyPossessable = false;
|
||||
outBitStream->Write(m_Possessor != LWOOBJID_EMPTY);
|
||||
if (m_Possessor != LWOOBJID_EMPTY) outBitStream->Write(m_Possessor);
|
||||
|
||||
void PossessableComponent::SetPossessor(LWOOBJID value)
|
||||
{
|
||||
m_Possessor = value;
|
||||
}
|
||||
outBitStream->Write(m_AnimationFlag != eAnimationFlags::IDLE_INVALID);
|
||||
if(m_AnimationFlag != eAnimationFlags::IDLE_INVALID) outBitStream->Write(m_AnimationFlag);
|
||||
|
||||
LWOOBJID PossessableComponent::GetPossessor() const
|
||||
{
|
||||
return m_Possessor;
|
||||
}
|
||||
|
||||
void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags)
|
||||
{
|
||||
outBitStream->Write(m_Possessor != LWOOBJID_EMPTY);
|
||||
if (m_Possessor != LWOOBJID_EMPTY)
|
||||
{
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write(m_Possessor);
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
}
|
||||
}
|
||||
|
||||
void PossessableComponent::Update(float deltaTime)
|
||||
{
|
||||
|
||||
outBitStream->Write(m_ImmediatelyDepossess);
|
||||
}
|
||||
}
|
||||
|
||||
void PossessableComponent::OnUse(Entity* originator) {
|
||||
PossessorComponent* possessorComponent;
|
||||
if (originator->TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessorComponent)) {
|
||||
SetPossessor(originator->GetObjectID());
|
||||
possessorComponent->SetPossessable(m_Parent->GetObjectID());
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
EntityManager::Instance()->SerializeEntity(originator);
|
||||
}
|
||||
}
|
||||
// TODO: Implement this
|
||||
}
|
@ -3,44 +3,113 @@
|
||||
#include "BitStream.h"
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
#include "Item.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "eAninmationFlags.h"
|
||||
|
||||
/**
|
||||
* Represents an entity that can be controlled by some other entity, generally used by cars to indicate that some
|
||||
* player is controlling it.
|
||||
*/
|
||||
class PossessableComponent : public Component {
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_POSSESSABLE;
|
||||
|
||||
PossessableComponent(Entity* parentEntity);
|
||||
~PossessableComponent() override;
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_POSSESSABLE;
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void Update(float deltaTime) override;
|
||||
PossessableComponent(Entity* parentEntity, uint32_t componentId);
|
||||
|
||||
/**
|
||||
* Sets the possessor of this entity
|
||||
* @param value the ID of the possessor to set
|
||||
*/
|
||||
void SetPossessor(LWOOBJID value);
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
/**
|
||||
* Returns the possessor of this entity
|
||||
* @return the possessor of this entitythe
|
||||
*/
|
||||
LWOOBJID GetPossessor() const;
|
||||
/**
|
||||
* Sets the possessor of this entity
|
||||
* @param value the ID of the possessor to set
|
||||
*/
|
||||
void SetPossessor(LWOOBJID value) { m_Possessor = value; m_DirtyPossessable = true;};
|
||||
|
||||
/**
|
||||
* Handles an OnUsed event by some other entity, if said entity has a PossessorComponent it becomes the possessor
|
||||
* of this entity
|
||||
* @param originator the entity that caused the event to trigger
|
||||
*/
|
||||
void OnUse(Entity* originator) override;
|
||||
/**
|
||||
* Returns the possessor of this entity
|
||||
* @return the possessor of this entity
|
||||
*/
|
||||
LWOOBJID GetPossessor() const { return m_Possessor; };
|
||||
|
||||
private:
|
||||
/**
|
||||
* Sets the animation Flag of the possessable
|
||||
* @param value the animation flag to set to
|
||||
*/
|
||||
void SetAnimationFlag(eAnimationFlags value) { m_AnimationFlag = value; m_DirtyPossessable = true;};
|
||||
|
||||
/**
|
||||
* The possessor of this entity, e.g. the entity that controls this entity
|
||||
*/
|
||||
LWOOBJID m_Possessor;
|
||||
/**
|
||||
* Returns the possession type of this entity
|
||||
* @return the possession type of this entity
|
||||
*/
|
||||
ePossessionType GetPossessionType() const { return m_PossessionType; };
|
||||
|
||||
/**
|
||||
* Returns if the entity should deposses on hit
|
||||
* @return if the entity should deposses on hit
|
||||
*/
|
||||
bool GetDepossessOnHit() const { return m_DepossessOnHit; };
|
||||
|
||||
/**
|
||||
* Forcibly depossess the entity
|
||||
*/
|
||||
void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true;};
|
||||
|
||||
/**
|
||||
* Set if the parent entity was spawned from an item
|
||||
* @param value if the parent entity was spawned from an item
|
||||
*/
|
||||
void SetItemSpawned(bool value) { m_ItemSpawned = value;};
|
||||
|
||||
/**
|
||||
* Returns if the parent entity was spawned from an item
|
||||
* @return if the parent entity was spawned from an item
|
||||
*/
|
||||
LWOOBJID GetItemSpawned() const { return m_ItemSpawned; };
|
||||
|
||||
/**
|
||||
* Handles an OnUsed event by some other entity, if said entity has a Possessor it becomes the possessor
|
||||
* of this entity
|
||||
* @param originator the entity that caused the event to trigger
|
||||
*/
|
||||
void OnUse(Entity* originator) override;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief Whether the possessor is dirty
|
||||
*/
|
||||
bool m_DirtyPossessable = true;
|
||||
|
||||
/**
|
||||
* @brief The possessor of this entity, e.g. the entity that controls this entity
|
||||
*/
|
||||
LWOOBJID m_Possessor = LWOOBJID_EMPTY;
|
||||
|
||||
/**
|
||||
* @brief The type of possesstion to use on this entity
|
||||
*/
|
||||
ePossessionType m_PossessionType = ePossessionType::NO_POSSESSION;
|
||||
|
||||
/**
|
||||
* @brief Should the possessable be dismount on hit
|
||||
*/
|
||||
bool m_DepossessOnHit = false;
|
||||
|
||||
/**
|
||||
* @brief What animaiton flag to use
|
||||
*
|
||||
*/
|
||||
eAnimationFlags m_AnimationFlag = eAnimationFlags::IDLE_INVALID;
|
||||
|
||||
/**
|
||||
* @brief Should this be immediately depossessed
|
||||
*
|
||||
*/
|
||||
bool m_ImmediatelyDepossess = false;
|
||||
|
||||
/**
|
||||
* @brief Whether the parent entity was spawned from an item
|
||||
*
|
||||
*/
|
||||
bool m_ItemSpawned = false;
|
||||
};
|
||||
|
@ -1,35 +1,21 @@
|
||||
#include "PossessorComponent.h"
|
||||
|
||||
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent)
|
||||
{
|
||||
m_Possessable = LWOOBJID_EMPTY;
|
||||
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) {
|
||||
m_Possessable = LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
PossessorComponent::~PossessorComponent()
|
||||
{
|
||||
|
||||
}
|
||||
PossessorComponent::~PossessorComponent() {}
|
||||
|
||||
void PossessorComponent::SetPossessable(LWOOBJID value)
|
||||
{
|
||||
m_Possessable = value;
|
||||
}
|
||||
|
||||
LWOOBJID PossessorComponent::GetPossessable() const
|
||||
{
|
||||
return m_Possessable;
|
||||
}
|
||||
|
||||
void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags)
|
||||
{
|
||||
outBitStream->Write(m_Possessable != LWOOBJID_EMPTY);
|
||||
if (m_Possessable != LWOOBJID_EMPTY)
|
||||
{
|
||||
outBitStream->Write(m_Possessable);
|
||||
}
|
||||
}
|
||||
|
||||
void PossessorComponent::Update(float deltaTime)
|
||||
{
|
||||
|
||||
void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate);
|
||||
if (m_DirtyPossesor || bIsInitialUpdate) {
|
||||
m_DirtyPossesor = false;
|
||||
outBitStream->Write(m_Possessable != LWOOBJID_EMPTY);
|
||||
if (m_Possessable != LWOOBJID_EMPTY) {
|
||||
outBitStream->Write(m_Possessable);
|
||||
}
|
||||
outBitStream->Write(m_PossessableType);
|
||||
}
|
||||
}
|
||||
|
@ -4,35 +4,78 @@
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
|
||||
// possession types
|
||||
enum class ePossessionType : uint8_t {
|
||||
NO_POSSESSION = 0,
|
||||
ATTACHED_VISIBLE,
|
||||
NOT_ATTACHED_VISIBLE,
|
||||
NOT_ATTACHED_NOT_VISIBLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an entity that can posess other entities. Generally used by players to drive a car.
|
||||
*/
|
||||
class PossessorComponent : public Component {
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_POSSESSOR;
|
||||
|
||||
PossessorComponent(Entity* parent);
|
||||
~PossessorComponent() override;
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_POSSESSOR;
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void Update(float deltaTime) override;
|
||||
PossessorComponent(Entity* parent);
|
||||
~PossessorComponent() override;
|
||||
|
||||
/**
|
||||
* Sets the entity that this entity is possessing
|
||||
* @param value the ID of the entity this ID should posess
|
||||
*/
|
||||
void SetPossessable(LWOOBJID value);
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
/**
|
||||
* Returns the entity that this entity is currently posessing
|
||||
* @return the entity that this entity is currently posessing
|
||||
*/
|
||||
LWOOBJID GetPossessable() const;
|
||||
/**
|
||||
* Sets the entity that this entity is possessing
|
||||
* @param value the ID of the entity this ID should posess
|
||||
*/
|
||||
void SetPossessable(LWOOBJID value) { m_Possessable = value; m_DirtyPossesor = true; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the entity that this entity is currently posessing
|
||||
* @return the entity that this entity is currently posessing
|
||||
*/
|
||||
LWOOBJID GetPossessable() const { return m_Possessable; }
|
||||
|
||||
/**
|
||||
* The ID of the entity this entity is possessing (e.g. the ID of a car)
|
||||
*/
|
||||
LWOOBJID m_Possessable;
|
||||
/**
|
||||
* Sets if we are busy mounting or dismounting
|
||||
* @param value if we are busy mounting or dismounting
|
||||
*/
|
||||
void SetIsBusy(bool value) { m_IsBusy = value; }
|
||||
|
||||
/**
|
||||
* Returns if we are busy mounting or dismounting
|
||||
* @return if we are busy mounting or dismounting
|
||||
*/
|
||||
bool GetIsBusy() const { return m_IsBusy; }
|
||||
|
||||
/**
|
||||
* Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0
|
||||
* @param value the possesible type to set
|
||||
*/
|
||||
void SetPossessableType(ePossessionType value) { m_PossessableType = value; m_DirtyPossesor = true; }
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* The ID of the entity this entity is possessing (e.g. the ID of a car)
|
||||
*/
|
||||
LWOOBJID m_Possessable = LWOOBJID_EMPTY;
|
||||
|
||||
/**
|
||||
* @brief possessable type
|
||||
*
|
||||
*/
|
||||
ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION;
|
||||
|
||||
/**
|
||||
* @brief if the possessor is dirty
|
||||
*
|
||||
*/
|
||||
bool m_DirtyPossesor = false;
|
||||
|
||||
/**
|
||||
* @brief if the possessor is busy mounting or dismounting
|
||||
*
|
||||
*/
|
||||
bool m_IsBusy = false;
|
||||
};
|
||||
|
@ -102,7 +102,7 @@ PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(Propert
|
||||
return property;
|
||||
}
|
||||
|
||||
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery, bool wantLimits) {
|
||||
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery, bool wantLimits) {
|
||||
std::string base;
|
||||
if (customQuery == "") {
|
||||
base = baseQueryForProperties;
|
||||
@ -115,15 +115,13 @@ std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMe
|
||||
|
||||
auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;");
|
||||
|
||||
friendsListQuery->setInt64(1, entity->GetObjectID());
|
||||
friendsListQuery->setInt64(2, entity->GetObjectID());
|
||||
friendsListQuery->setUInt(1, character->GetID());
|
||||
friendsListQuery->setUInt(2, character->GetID());
|
||||
|
||||
auto friendsListQueryResult = friendsListQuery->executeQuery();
|
||||
|
||||
while (friendsListQueryResult->next()) {
|
||||
auto playerIDToConvert = friendsListQueryResult->getInt64(1);
|
||||
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_CHARACTER);
|
||||
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_PERSISTENT);
|
||||
auto playerIDToConvert = friendsListQueryResult->getInt(1);
|
||||
friendsList = friendsList + std::to_string(playerIDToConvert) + ",";
|
||||
}
|
||||
// Replace trailing comma with the closing parenthesis.
|
||||
@ -172,8 +170,8 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
// If the player has a property this query will have a single result.
|
||||
if (playerPropertyLookupResults->next()) {
|
||||
const auto cloneId = playerPropertyLookupResults->getUInt64(4);
|
||||
const auto propertyName = playerPropertyLookupResults->getString(5).asStdString();
|
||||
const auto propertyDescription = playerPropertyLookupResults->getString(6).asStdString();
|
||||
const auto propertyName = std::string(playerPropertyLookupResults->getString(5).c_str());
|
||||
const auto propertyDescription = std::string(playerPropertyLookupResults->getString(6).c_str());
|
||||
const auto privacyOption = playerPropertyLookupResults->getInt(9);
|
||||
const auto modApproved = playerPropertyLookupResults->getBoolean(10);
|
||||
const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11);
|
||||
@ -193,7 +191,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
|
||||
entries.push_back(playerEntry);
|
||||
|
||||
const auto query = BuildQuery(entity, sortMethod);
|
||||
const auto query = BuildQuery(entity, sortMethod, character);
|
||||
|
||||
auto propertyLookup = Database::CreatePreppedStmt(query);
|
||||
|
||||
@ -212,8 +210,8 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
const auto propertyId = propertyEntry->getUInt64(1);
|
||||
const auto owner = propertyEntry->getInt(2);
|
||||
const auto cloneId = propertyEntry->getUInt64(4);
|
||||
const auto propertyNameFromDb = propertyEntry->getString(5).asStdString();
|
||||
const auto propertyDescriptionFromDb = propertyEntry->getString(6).asStdString();
|
||||
const auto propertyNameFromDb = std::string(propertyEntry->getString(5).c_str());
|
||||
const auto propertyDescriptionFromDb = std::string(propertyEntry->getString(6).c_str());
|
||||
const auto privacyOption = propertyEntry->getInt(9);
|
||||
const auto modApproved = propertyEntry->getBoolean(10);
|
||||
const auto dateLastUpdated = propertyEntry->getInt(11);
|
||||
@ -239,7 +237,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
continue;
|
||||
} else {
|
||||
isOwned = cloneId == character->GetPropertyCloneID();
|
||||
ownerName = nameResult->getString(1).asStdString();
|
||||
ownerName = std::string(nameResult->getString(1).c_str());
|
||||
}
|
||||
|
||||
delete nameResult;
|
||||
@ -262,17 +260,17 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
// Query to get friend and best friend fields
|
||||
auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)");
|
||||
|
||||
friendCheck->setInt64(1, entity->GetObjectID());
|
||||
friendCheck->setInt64(2, ownerObjId);
|
||||
friendCheck->setInt64(3, ownerObjId);
|
||||
friendCheck->setInt64(4, entity->GetObjectID());
|
||||
friendCheck->setUInt(1, character->GetID());
|
||||
friendCheck->setUInt(2, ownerObjId);
|
||||
friendCheck->setUInt(3, ownerObjId);
|
||||
friendCheck->setUInt(4, character->GetID());
|
||||
|
||||
auto friendResult = friendCheck->executeQuery();
|
||||
|
||||
// If we got a result than the two players are friends.
|
||||
if (friendResult->next()) {
|
||||
isFriend = true;
|
||||
if (friendResult->getInt(1) == 2) {
|
||||
if (friendResult->getInt(1) == 3) {
|
||||
isBestFriend = true;
|
||||
}
|
||||
}
|
||||
@ -326,7 +324,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
// Query here is to figure out whether or not to display the button to go to the next page or not.
|
||||
int32_t numberOfProperties = 0;
|
||||
|
||||
auto buttonQuery = BuildQuery(entity, sortMethod, "SELECT COUNT(*) 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 >= ? ", false);
|
||||
auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) 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 >= ? ", false);
|
||||
auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery);
|
||||
|
||||
propertiesLeft->setUInt(1, this->m_MapID);
|
||||
@ -346,4 +344,4 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl
|
||||
propertiesLeft = nullptr;
|
||||
|
||||
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class PropertyEntranceComponent : public Component {
|
||||
|
||||
PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f);
|
||||
|
||||
std::string BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery = "", bool wantLimits = true);
|
||||
std::string BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery = "", bool wantLimits = true);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "MissionComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "PropertyDataMessage.h"
|
||||
#include "UserManager.h"
|
||||
@ -40,11 +41,11 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
|
||||
const auto zoneId = worldId.GetMapID();
|
||||
const auto cloneId = worldId.GetCloneID();
|
||||
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT id FROM PropertyTemplate WHERE mapID = ?;");
|
||||
query.bind(1, (int) zoneId);
|
||||
|
||||
query << "SELECT id FROM PropertyTemplate WHERE mapID = " << std::to_string(zoneId) << ";";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof() || result.fieldIsNull(0))
|
||||
{
|
||||
@ -75,7 +76,7 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
|
||||
this->moderatorRequested = propertyEntry->getInt(10) == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public;
|
||||
this->LastUpdatedTime = propertyEntry->getUInt64(11);
|
||||
this->claimedTime = propertyEntry->getUInt64(12);
|
||||
this->rejectionReason = propertyEntry->getString(13).asStdString();
|
||||
this->rejectionReason = std::string(propertyEntry->getString(13).c_str());
|
||||
this->reputation = propertyEntry->getUInt(14);
|
||||
|
||||
Load();
|
||||
@ -102,12 +103,12 @@ void PropertyManagementComponent::SetOwner(Entity* value)
|
||||
std::vector<NiPoint3> PropertyManagementComponent::GetPaths() const
|
||||
{
|
||||
const auto zoneId = dZoneManager::Instance()->GetZone()->GetWorldID();
|
||||
|
||||
std::stringstream query {};
|
||||
|
||||
query << "SELECT path FROM PropertyTemplate WHERE mapID = " << std::to_string(zoneId) << ";";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT path FROM PropertyTemplate WHERE mapID = ?;");
|
||||
query.bind(1, (int) zoneId);
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
std::vector<NiPoint3> paths {};
|
||||
|
||||
@ -285,6 +286,10 @@ void PropertyManagementComponent::OnStartBuilding()
|
||||
|
||||
player->SendToZone(zoneId);
|
||||
}
|
||||
auto inventoryComponent = ownerEntity->GetComponent<InventoryComponent>();
|
||||
|
||||
// Push equipped items
|
||||
if (inventoryComponent) inventoryComponent->PushEquippedItems();
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::OnFinishBuilding()
|
||||
@ -865,7 +870,7 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const
|
||||
|
||||
result->next();
|
||||
|
||||
const auto reason = result->getString(1).asStdString();;
|
||||
const auto reason = std::string(result->getString(1).c_str());
|
||||
const auto modApproved = result->getInt(2);
|
||||
if (reason != "") {
|
||||
moderatorRequested = false;
|
||||
|
@ -212,6 +212,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity *player,
|
||||
|
||||
if (possessorComponent != nullptr) {
|
||||
possessorComponent->SetPossessable(carEntity->GetObjectID());
|
||||
possessorComponent->SetPossessableType(ePossessionType::ATTACHED_VISIBLE); // for racing it's always Attached_Visible
|
||||
}
|
||||
|
||||
// Set the player's current activity as racing.
|
||||
@ -219,7 +220,6 @@ void RacingControlComponent::LoadPlayerVehicle(Entity *player,
|
||||
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->SetIsRacing(true);
|
||||
characterComponent->SetVehicleObjectID(carEntity->GetObjectID());
|
||||
}
|
||||
|
||||
// Init the player's racing entry.
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include "Game.h"
|
||||
#include "dLogger.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "MissionTaskType.h"
|
||||
|
||||
#include "dServer.h"
|
||||
#include "PacketUtils.h"
|
||||
@ -22,6 +24,20 @@ RebuildComponent::RebuildComponent(Entity* entity) : Component(entity) {
|
||||
{
|
||||
m_Precondition = new PreconditionExpression(GeneralUtils::UTF16ToWTF8(checkPreconditions));
|
||||
}
|
||||
|
||||
// Should a setting that has the build activator position exist, fetch that setting here and parse it for position.
|
||||
// It is assumed that the user who sets this setting uses the correct character delimiter (character 31 or in hex 0x1F)
|
||||
auto positionAsVector = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rebuild_activators"), 0x1F);
|
||||
if (positionAsVector.size() == 3 &&
|
||||
GeneralUtils::TryParse(positionAsVector[0], m_ActivatorPosition.x) &&
|
||||
GeneralUtils::TryParse(positionAsVector[1], m_ActivatorPosition.y) &&
|
||||
GeneralUtils::TryParse(positionAsVector[2], m_ActivatorPosition.z)) {
|
||||
} else {
|
||||
Game::logger->Log("RebuildComponent", "Failed to find activator position for lot %i. Defaulting to parents position.\n", m_Parent->GetLOT());
|
||||
m_ActivatorPosition = m_Parent->GetPosition();
|
||||
}
|
||||
|
||||
SpawnActivator();
|
||||
}
|
||||
|
||||
RebuildComponent::~RebuildComponent() {
|
||||
|
@ -198,14 +198,16 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream query;
|
||||
const std::string effectType_str = GeneralUtils::UTF16ToWTF8(effectType);
|
||||
|
||||
query << "SELECT animation_length FROM Animations WHERE animation_type IN (SELECT animationName FROM BehaviorEffect WHERE effectID = " << std::to_string(effectId) << " AND effectType = '" << GeneralUtils::UTF16ToWTF8(effectType) << "');";
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT animation_length FROM Animations WHERE animation_type IN (SELECT animationName FROM BehaviorEffect WHERE effectID = ? AND effectType = ?);");
|
||||
query.bind(1, effectId);
|
||||
query.bind(2, effectType_str.c_str());
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof() || result.fieldIsNull(0))
|
||||
{
|
||||
if (result.eof() || result.fieldIsNull(0)) {
|
||||
result.finalize();
|
||||
|
||||
m_DurationCache[effectId] = 0;
|
||||
@ -214,7 +216,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
effect->time = static_cast<float>(result.getFloatField(0));
|
||||
|
||||
result.finalize();
|
||||
|
@ -1,21 +1,17 @@
|
||||
#include "RocketLaunchLupComponent.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "RocketLaunchpadControlComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
|
||||
RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) {
|
||||
m_Parent = parent;
|
||||
|
||||
// get the lup worlds from the cdclient
|
||||
std::string query = "SELECT * FROM LUPZoneIDs;";
|
||||
auto results = CDClientDatabase::ExecuteQuery(query);
|
||||
while (!results.eof()) {
|
||||
// fallback to 1600 incase there is an issue
|
||||
m_LUPWorlds.push_back(results.getIntField(0, 1600));
|
||||
results.nextRow();
|
||||
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
|
||||
std::stringstream ss(zoneString);
|
||||
for (int i; ss >> i;) {
|
||||
m_LUPWorlds.push_back(i);
|
||||
if (ss.peek() == ';')
|
||||
ss.ignore();
|
||||
}
|
||||
results.finalize();
|
||||
}
|
||||
|
||||
RocketLaunchLupComponent::~RocketLaunchLupComponent() {}
|
||||
@ -29,11 +25,7 @@ void RocketLaunchLupComponent::OnUse(Entity* originator) {
|
||||
}
|
||||
|
||||
void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) {
|
||||
// Add one to index because the actual LUP worlds start at index 1.
|
||||
index++;
|
||||
|
||||
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
||||
|
||||
if (!rocketLaunchpadControlComponent) return;
|
||||
|
||||
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
|
||||
|
@ -19,11 +19,11 @@
|
||||
#include "PacketUtils.h"
|
||||
|
||||
RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent, int rocketId) : Component(parent) {
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT targetZone, defaultZoneID, targetScene, altLandingPrecondition, altLandingSpawnPointName FROM RocketLaunchpadControlComponent WHERE id = ?;");
|
||||
query.bind(1, rocketId);
|
||||
|
||||
query << "SELECT targetZone, defaultZoneID, targetScene, altLandingPrecondition, altLandingSpawnPointName FROM RocketLaunchpadControlComponent WHERE id = " << std::to_string(rocketId);
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (!result.eof() && !result.fieldIsNull(0))
|
||||
{
|
||||
@ -48,7 +48,7 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId,
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready
|
||||
auto* characterComponent = originator->GetComponent<CharacterComponent>();
|
||||
auto* character = originator->GetCharacter();
|
||||
@ -77,11 +77,11 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId,
|
||||
character->SaveXMLToDatabase();
|
||||
|
||||
SetSelectedMapId(originator->GetObjectID(), zone);
|
||||
|
||||
GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID());
|
||||
|
||||
GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID());
|
||||
|
||||
GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(originator);
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ void RocketLaunchpadControlComponent::OnProximityUpdate(Entity* entering, std::s
|
||||
// Proximity rockets are handled by item equipment
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::SetSelectedMapId(LWOOBJID player, LWOMAPID mapID)
|
||||
void RocketLaunchpadControlComponent::SetSelectedMapId(LWOOBJID player, LWOMAPID mapID)
|
||||
{
|
||||
m_SelectedMapIds[player] = mapID;
|
||||
}
|
||||
@ -126,7 +126,7 @@ LWOMAPID RocketLaunchpadControlComponent::GetSelectedMapId(LWOOBJID player) cons
|
||||
return index->second;
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::SetSelectedCloneId(LWOOBJID player, LWOCLONEID cloneId)
|
||||
void RocketLaunchpadControlComponent::SetSelectedCloneId(LWOOBJID player, LWOCLONEID cloneId)
|
||||
{
|
||||
m_SelectedCloneIds[player] = cloneId;
|
||||
}
|
||||
|
@ -487,22 +487,24 @@ void ActivityInstance::StartZone() {
|
||||
return;
|
||||
|
||||
auto* leader = participants[0];
|
||||
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_CREATE_TEAM);
|
||||
|
||||
bitStream.Write(leader->GetObjectID());
|
||||
bitStream.Write(m_Participants.size());
|
||||
|
||||
for (const auto& participant : m_Participants) {
|
||||
bitStream.Write(participant);
|
||||
}
|
||||
|
||||
LWOZONEID zoneId = LWOZONEID(m_ActivityInfo.instanceMapID, 0, leader->GetCharacter()->GetPropertyCloneID());
|
||||
|
||||
bitStream.Write(zoneId);
|
||||
// only make a team if we have more than one participant
|
||||
if (participants.size() > 1){
|
||||
CBITSTREAM;
|
||||
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_CREATE_TEAM);
|
||||
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
bitStream.Write(leader->GetObjectID());
|
||||
bitStream.Write(m_Participants.size());
|
||||
|
||||
for (const auto& participant : m_Participants) {
|
||||
bitStream.Write(participant);
|
||||
}
|
||||
|
||||
bitStream.Write(zoneId);
|
||||
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
|
||||
const auto cloneId = GeneralUtils::GenerateRandomNumber<uint32_t>(1, UINT32_MAX);
|
||||
for (Entity* player : participants) {
|
||||
|
@ -17,6 +17,17 @@ SimplePhysicsComponent::SimplePhysicsComponent(uint32_t componentID, Entity* par
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
m_Rotation = m_Parent->GetDefaultRotation();
|
||||
m_IsDirty = true;
|
||||
|
||||
const auto& climbable_type = m_Parent->GetVar<std::u16string>(u"climbable");
|
||||
if (climbable_type == u"wall") {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL);
|
||||
} else if (climbable_type == u"ladder") {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_LADDER);
|
||||
} else if (climbable_type == u"wallstick") {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL_STICK);
|
||||
} else {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_NOT);
|
||||
}
|
||||
}
|
||||
|
||||
SimplePhysicsComponent::~SimplePhysicsComponent() {
|
||||
@ -24,10 +35,10 @@ SimplePhysicsComponent::~SimplePhysicsComponent() {
|
||||
|
||||
void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write0(); // climbable
|
||||
outBitStream->Write<int32_t>(0); // climbableType
|
||||
outBitStream->Write(m_ClimbableType != eClimbableType::CLIMBABLE_TYPE_NOT);
|
||||
outBitStream->Write(m_ClimbableType);
|
||||
}
|
||||
|
||||
|
||||
outBitStream->Write(m_DirtyVelocity || bIsInitialUpdate);
|
||||
if (m_DirtyVelocity || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_Velocity);
|
||||
@ -46,7 +57,7 @@ void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIs
|
||||
{
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
|
||||
outBitStream->Write(m_IsDirty || bIsInitialUpdate);
|
||||
if (m_IsDirty || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_Position.x);
|
||||
@ -66,7 +77,7 @@ uint32_t SimplePhysicsComponent::GetPhysicsMotionState() const
|
||||
return m_PhysicsMotionState;
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::SetPhysicsMotionState(uint32_t value)
|
||||
void SimplePhysicsComponent::SetPhysicsMotionState(uint32_t value)
|
||||
{
|
||||
m_PhysicsMotionState = value;
|
||||
}
|
||||
|
@ -14,16 +14,24 @@
|
||||
|
||||
class Entity;
|
||||
|
||||
enum class eClimbableType : int32_t {
|
||||
CLIMBABLE_TYPE_NOT = 0,
|
||||
CLIMBABLE_TYPE_LADDER,
|
||||
CLIMBABLE_TYPE_WALL,
|
||||
CLIMBABLE_TYPE_WALL_STICK
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Component that serializes locations of entities to the client
|
||||
*/
|
||||
class SimplePhysicsComponent : public Component {
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_SIMPLE_PHYSICS;
|
||||
|
||||
|
||||
SimplePhysicsComponent(uint32_t componentID, Entity* parent);
|
||||
~SimplePhysicsComponent() override;
|
||||
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
/**
|
||||
@ -86,6 +94,18 @@ public:
|
||||
*/
|
||||
void SetPhysicsMotionState(uint32_t value);
|
||||
|
||||
/**
|
||||
* Returns the ClimbableType of this entity
|
||||
* @return the ClimbableType of this entity
|
||||
*/
|
||||
const eClimbableType& GetClimabbleType() { return m_ClimbableType; }
|
||||
|
||||
/**
|
||||
* Sets the ClimbableType of this entity
|
||||
* @param value the ClimbableType to set
|
||||
*/
|
||||
void SetClimbableType(const eClimbableType& value) { m_ClimbableType = value; }
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@ -122,6 +142,11 @@ private:
|
||||
* The current physics motion state
|
||||
*/
|
||||
uint32_t m_PhysicsMotionState = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the entity is climbable
|
||||
*/
|
||||
eClimbableType m_ClimbableType = eClimbableType::CLIMBABLE_TYPE_NOT;
|
||||
};
|
||||
|
||||
#endif // SIMPLEPHYSICSCOMPONENT_H
|
||||
|
@ -38,11 +38,11 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
const auto branch = BehaviorBranchContext(target, 0);
|
||||
|
||||
|
||||
behavior->Handle(context, bitStream, branch);
|
||||
|
||||
context->ExecuteUpdates();
|
||||
|
||||
|
||||
return !context->failed;
|
||||
}
|
||||
|
||||
@ -78,24 +78,23 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
Game::logger->Log("SkillComponent", "Failed to find projectile id (%llu)!\n", projectileId);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto sync_entry = this->m_managedProjectiles.at(index);
|
||||
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT behaviorID FROM SkillBehavior WHERE skillID = (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
query.bind(1, (int) sync_entry.lot);
|
||||
|
||||
query << "SELECT behaviorID FROM SkillBehavior WHERE skillID = (SELECT skillID FROM ObjectSkills WHERE objectTemplate = " << std::to_string(sync_entry.lot) << ")";
|
||||
auto result = query.execQuery();
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
|
||||
if (result.eof())
|
||||
{
|
||||
if (result.eof()) {
|
||||
Game::logger->Log("SkillComponent", "Failed to find skill id for (%i)!\n", sync_entry.lot);
|
||||
|
||||
return;
|
||||
@ -115,7 +114,7 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B
|
||||
{
|
||||
branch.target = target;
|
||||
}
|
||||
|
||||
|
||||
behavior->Handle(sync_entry.context, bitStream, branch);
|
||||
|
||||
this->m_managedProjectiles.erase(this->m_managedProjectiles.begin() + index);
|
||||
@ -129,7 +128,7 @@ void SkillComponent::RegisterPlayerProjectile(const LWOOBJID projectileId, Behav
|
||||
entry.branchContext = branch;
|
||||
entry.lot = lot;
|
||||
entry.id = projectileId;
|
||||
|
||||
|
||||
this->m_managedProjectiles.push_back(entry);
|
||||
}
|
||||
|
||||
@ -141,7 +140,7 @@ void SkillComponent::Update(const float deltaTime)
|
||||
}
|
||||
|
||||
std::map<uint32_t, BehaviorContext*> keep {};
|
||||
|
||||
|
||||
for (const auto& pair : this->m_managedBehaviors)
|
||||
{
|
||||
auto* context = pair.second;
|
||||
@ -150,7 +149,7 @@ void SkillComponent::Update(const float deltaTime)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (context->clientInitalized)
|
||||
{
|
||||
context->CalculateUpdate(deltaTime);
|
||||
@ -164,7 +163,7 @@ void SkillComponent::Update(const float deltaTime)
|
||||
if (context->syncEntries.empty() && context->timerEntries.empty())
|
||||
{
|
||||
auto any = false;
|
||||
|
||||
|
||||
for (const auto& projectile : this->m_managedProjectiles)
|
||||
{
|
||||
if (projectile.context == context)
|
||||
@ -180,13 +179,13 @@ void SkillComponent::Update(const float deltaTime)
|
||||
context->Reset();
|
||||
|
||||
delete context;
|
||||
|
||||
|
||||
context = nullptr;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
keep.insert_or_assign(pair.first, context);
|
||||
}
|
||||
|
||||
@ -254,9 +253,9 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c
|
||||
context->caster = m_Parent->GetObjectID();
|
||||
|
||||
context->clientInitalized = clientInitalized;
|
||||
|
||||
|
||||
context->foundTarget = target != LWOOBJID_EMPTY || ignoreTarget || clientInitalized;
|
||||
|
||||
|
||||
behavior->Calculate(context, bitStream, { target, 0});
|
||||
|
||||
for (auto* script : CppScripts::GetEntityScripts(m_Parent)) {
|
||||
@ -278,7 +277,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c
|
||||
{
|
||||
// Echo start skill
|
||||
GameMessages::EchoStartSkill start;
|
||||
|
||||
|
||||
start.iCastType = 0;
|
||||
start.skillID = skillId;
|
||||
start.uiSkillHandle = context->skillUId;
|
||||
@ -353,7 +352,7 @@ void SkillComponent::CalculateUpdate(const float deltaTime)
|
||||
const auto targetPosition = target->GetPosition();
|
||||
|
||||
const auto closestPoint = Vector3::ClosestPointOnLine(entry.lastPosition, position, targetPosition);
|
||||
|
||||
|
||||
const auto distance = Vector3::DistanceSquared(targetPosition, closestPoint);
|
||||
|
||||
if (distance > 3 * 3)
|
||||
@ -399,12 +398,12 @@ void SkillComponent::CalculateUpdate(const float deltaTime)
|
||||
}
|
||||
|
||||
entry.lastPosition = position;
|
||||
|
||||
|
||||
managedProjectile = entry;
|
||||
}
|
||||
|
||||
|
||||
std::vector<ProjectileSyncEntry> valid;
|
||||
|
||||
|
||||
for (auto& entry : this->m_managedProjectiles)
|
||||
{
|
||||
if (entry.calculation)
|
||||
@ -412,7 +411,7 @@ void SkillComponent::CalculateUpdate(const float deltaTime)
|
||||
if (entry.time >= entry.maxTime)
|
||||
{
|
||||
entry.branchContext.target = LWOOBJID_EMPTY;
|
||||
|
||||
|
||||
SyncProjectileCalculation(entry);
|
||||
|
||||
continue;
|
||||
@ -430,8 +429,7 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
|
||||
{
|
||||
auto* other = EntityManager::Instance()->GetEntity(entry.branchContext.target);
|
||||
|
||||
if (other == nullptr)
|
||||
{
|
||||
if (other == nullptr) {
|
||||
if (entry.branchContext.target != LWOOBJID_EMPTY)
|
||||
{
|
||||
Game::logger->Log("SkillComponent", "Invalid projectile target (%llu)!\n", entry.branchContext.target);
|
||||
@ -440,14 +438,12 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT behaviorID FROM SkillBehavior WHERE skillID = (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
query.bind(1, (int) entry.lot);
|
||||
auto result = query.execQuery();
|
||||
|
||||
query << "SELECT behaviorID FROM SkillBehavior WHERE skillID = (SELECT skillID FROM ObjectSkills WHERE objectTemplate = " << std::to_string(entry.lot) << ")";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
|
||||
if (result.eof())
|
||||
{
|
||||
if (result.eof()) {
|
||||
Game::logger->Log("SkillComponent", "Failed to find skill id for (%i)!\n", entry.lot);
|
||||
|
||||
return;
|
||||
@ -456,13 +452,13 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
|
||||
const auto behaviorId = static_cast<uint32_t>(result.getIntField(0));
|
||||
|
||||
result.finalize();
|
||||
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
auto* bitStream = new RakNet::BitStream();
|
||||
|
||||
behavior->Calculate(entry.context, bitStream, entry.branchContext);
|
||||
|
||||
|
||||
GameMessages::DoClientProjectileImpact projectileImpact;
|
||||
|
||||
projectileImpact.sBitStream.assign((char*) bitStream->GetData(), bitStream->GetNumberOfBytesUsed());
|
||||
@ -498,19 +494,19 @@ void SkillComponent::HandleUnmanaged(const uint32_t behaviorId, const LWOOBJID t
|
||||
|
||||
delete bitStream;
|
||||
|
||||
delete context;
|
||||
delete context;
|
||||
}
|
||||
|
||||
void SkillComponent::HandleUnCast(const uint32_t behaviorId, const LWOOBJID target)
|
||||
{
|
||||
auto* context = new BehaviorContext(target);
|
||||
|
||||
|
||||
context->caster = target;
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
||||
behavior->UnCast(context, { target });
|
||||
|
||||
|
||||
delete context;
|
||||
}
|
||||
|
||||
|
2
dGame/dEntity/CMakeLists.txt
Normal file
2
dGame/dEntity/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
set(DGAME_DENTITY_SOURCES "EntityCallbackTimer.cpp"
|
||||
"EntityTimer.cpp" PARENT_SCOPE)
|
4
dGame/dGameMessages/CMakeLists.txt
Normal file
4
dGame/dGameMessages/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
set(DGAME_DGAMEMESSAGES_SOURCES "GameMessageHandler.cpp"
|
||||
"GameMessages.cpp"
|
||||
"PropertyDataMessage.cpp"
|
||||
"PropertySelectQueryProperty.cpp" PARENT_SCOPE)
|
File diff suppressed because it is too large
Load Diff
@ -328,6 +328,34 @@ namespace GameMessages {
|
||||
|
||||
void SendDisplayChatBubble(LWOOBJID objectId, const std::u16string& text, const SystemAddress& sysAddr);
|
||||
|
||||
// Mounts
|
||||
/**
|
||||
* @brief Set the Inventory LWOOBJID of the mount
|
||||
*
|
||||
* @param entity The entity that is mounting
|
||||
* @param sysAddr the system address to send game message responses to
|
||||
* @param objectID LWOOBJID of the item in inventory that is being used
|
||||
*/
|
||||
void SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr);
|
||||
|
||||
/**
|
||||
* @brief Handle client dismounting mount
|
||||
*
|
||||
* @param inStream Raknet BitStream of incoming data
|
||||
* @param entity The Entity that is dismounting
|
||||
* @param sysAddr the system address to send game message responses to
|
||||
*/
|
||||
void HandleDismountComplete(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
/**
|
||||
* @brief Handle acknowledging that the client possessed something
|
||||
*
|
||||
* @param inStream Raknet BitStream of incoming data
|
||||
* @param entity The Entity that is possessing
|
||||
* @param sysAddr the system address to send game message responses to
|
||||
*/
|
||||
void HandleAcknowledgePossession(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
//Racing:
|
||||
void HandleModuleAssemblyQueryData(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
@ -337,8 +365,6 @@ namespace GameMessages {
|
||||
|
||||
void HandleRacingClientReady(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
void HandleAcknowledgePossession(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
void HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
void HandleVehicleNotifyServerAddPassiveBoostAction(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
|
||||
@ -372,6 +398,7 @@ namespace GameMessages {
|
||||
void SendActivityPause(LWOOBJID objectId, bool pause = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
void SendStartActivityTime(LWOOBJID objectId, float_t startTime, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
|
||||
void SendRequestActivityEnter(LWOOBJID objectId, const SystemAddress& sysAddr, bool bStart, LWOOBJID userID);
|
||||
void SendUseItemRequirementsResponse(LWOOBJID objectID, const SystemAddress& sysAddr, UseItemResponse itemResponse);
|
||||
|
||||
// SG:
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef PROPERTYSELECTQUERY_H
|
||||
#define PROPERTYSELECTQUERY_H
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
class PropertySelectQueryProperty final
|
||||
@ -24,3 +27,5 @@ public:
|
||||
float PerformanceCost = 0; // The performance cost of the property
|
||||
uint32_t PerformanceIndex = 0; // The performance index of the property? Always 0?
|
||||
};
|
||||
|
||||
#endif
|
||||
|
5
dGame/dInventory/CMakeLists.txt
Normal file
5
dGame/dInventory/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
set(DGAME_DINVENTORY_SOURCES "EquippedItem.cpp"
|
||||
"Inventory.cpp"
|
||||
"Item.cpp"
|
||||
"ItemSet.cpp"
|
||||
"ItemSetPassiveAbility.cpp" PARENT_SCOPE)
|
@ -1,7 +1,8 @@
|
||||
#include "Inventory.h"
|
||||
#include "Inventory.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Game.h"
|
||||
#include "Item.h"
|
||||
#include "eItemType.h"
|
||||
|
||||
std::vector<LOT> Inventory::m_GameMasterRestrictedItems = {
|
||||
1727, // GM Only - JetPack
|
||||
@ -274,40 +275,41 @@ eInventoryType Inventory::FindInventoryTypeForLot(const LOT lot)
|
||||
const auto itemType = static_cast<eItemType>(itemComponent.itemType);
|
||||
|
||||
switch (itemType) {
|
||||
case ITEM_TYPE_BRICK:
|
||||
case eItemType::ITEM_TYPE_BRICK:
|
||||
return BRICKS;
|
||||
|
||||
case ITEM_TYPE_BEHAVIOR:
|
||||
case eItemType::ITEM_TYPE_BEHAVIOR:
|
||||
return BEHAVIORS;
|
||||
|
||||
case ITEM_TYPE_PROPERTY:
|
||||
case eItemType::ITEM_TYPE_PROPERTY:
|
||||
return PROPERTY_DEEDS;
|
||||
|
||||
case ITEM_TYPE_MODEL:
|
||||
case ITEM_TYPE_VEHICLE:
|
||||
case ITEM_TYPE_LOOT_MODEL:
|
||||
case eItemType::ITEM_TYPE_MODEL:
|
||||
case eItemType::ITEM_TYPE_VEHICLE:
|
||||
case eItemType::ITEM_TYPE_LOOT_MODEL:
|
||||
case eItemType::ITEM_TYPE_MOUNT:
|
||||
return MODELS;
|
||||
|
||||
case ITEM_TYPE_HAT:
|
||||
case ITEM_TYPE_HAIR:
|
||||
case ITEM_TYPE_NECK:
|
||||
case ITEM_TYPE_LEFT_HAND:
|
||||
case ITEM_TYPE_RIGHT_HAND:
|
||||
case ITEM_TYPE_LEGS:
|
||||
case ITEM_TYPE_LEFT_TRINKET:
|
||||
case ITEM_TYPE_RIGHT_TRINKET:
|
||||
case ITEM_TYPE_COLLECTIBLE:
|
||||
case ITEM_TYPE_CONSUMABLE:
|
||||
case ITEM_TYPE_CHEST:
|
||||
case ITEM_TYPE_EGG:
|
||||
case ITEM_TYPE_PET_FOOD:
|
||||
case ITEM_TYPE_PET_INVENTORY_ITEM:
|
||||
case ITEM_TYPE_PACKAGE:
|
||||
case ITEM_TYPE_CURRENCY:
|
||||
case eItemType::ITEM_TYPE_HAT:
|
||||
case eItemType::ITEM_TYPE_HAIR:
|
||||
case eItemType::ITEM_TYPE_NECK:
|
||||
case eItemType::ITEM_TYPE_LEFT_HAND:
|
||||
case eItemType::ITEM_TYPE_RIGHT_HAND:
|
||||
case eItemType::ITEM_TYPE_LEGS:
|
||||
case eItemType::ITEM_TYPE_LEFT_TRINKET:
|
||||
case eItemType::ITEM_TYPE_RIGHT_TRINKET:
|
||||
case eItemType::ITEM_TYPE_COLLECTIBLE:
|
||||
case eItemType::ITEM_TYPE_CONSUMABLE:
|
||||
case eItemType::ITEM_TYPE_CHEST:
|
||||
case eItemType::ITEM_TYPE_EGG:
|
||||
case eItemType::ITEM_TYPE_PET_FOOD:
|
||||
case eItemType::ITEM_TYPE_PET_INVENTORY_ITEM:
|
||||
case eItemType::ITEM_TYPE_PACKAGE:
|
||||
case eItemType::ITEM_TYPE_CURRENCY:
|
||||
return ITEMS;
|
||||
|
||||
case ITEM_TYPE_QUEST_OBJECT:
|
||||
case ITEM_TYPE_UNKNOWN:
|
||||
case eItemType::ITEM_TYPE_QUEST_OBJECT:
|
||||
case eItemType::ITEM_TYPE_UNKNOWN:
|
||||
default:
|
||||
return HIDDEN;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef INVENTORY_H
|
||||
#define INVENTORY_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@ -182,3 +185,5 @@ private:
|
||||
*/
|
||||
static std::vector<LOT> m_GameMasterRestrictedItems;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -311,7 +311,9 @@ bool Item::UseNonEquip()
|
||||
|
||||
const auto success = !packages.empty();
|
||||
|
||||
Game::logger->Log("Item", "Used (%i) with (%d)\n", lot, success);
|
||||
auto inventoryComponent = inventory->GetComponent();
|
||||
|
||||
auto playerEntity = inventoryComponent->GetParent();
|
||||
|
||||
if (subKey != LWOOBJID_EMPTY)
|
||||
{
|
||||
@ -324,8 +326,7 @@ bool Item::UseNonEquip()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
if (success && (playerEntity->GetGMLevel() >= eGameMasterLevel::GAME_MASTER_LEVEL_JUNIOR_DEVELOPER || this->GetPreconditionExpression()->Check(playerEntity)))
|
||||
{
|
||||
auto* entityParent = inventory->GetComponent()->GetParent();
|
||||
|
||||
@ -342,7 +343,7 @@ bool Item::UseNonEquip()
|
||||
|
||||
LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION);
|
||||
}
|
||||
|
||||
Game::logger->Log("Item", "Used (%i)\n", lot);
|
||||
inventory->GetComponent()->RemoveItem(lot, 1);
|
||||
}
|
||||
|
||||
@ -386,11 +387,11 @@ void Item::DisassembleModel()
|
||||
|
||||
const auto componentId = table->GetByIDAndType(GetLot(), COMPONENT_TYPE_RENDER);
|
||||
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT render_asset FROM RenderComponent WHERE id = ?;");
|
||||
query.bind(1, (int) componentId);
|
||||
|
||||
query << "SELECT render_asset FROM RenderComponent WHERE id = " << std::to_string(componentId) << ";";
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof())
|
||||
{
|
||||
|
@ -15,17 +15,17 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent)
|
||||
|
||||
this->m_PassiveAbilities = ItemSetPassiveAbility::FindAbilities(id, m_InventoryComponent->GetParent(), this);
|
||||
|
||||
std::stringstream query;
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT skillSetWith2, skillSetWith3, skillSetWith4, skillSetWith5, skillSetWith6, itemIDs FROM ItemSets WHERE setID = ?;");
|
||||
query.bind(1, (int) id);
|
||||
|
||||
query << "SELECT skillSetWith2, skillSetWith3, skillSetWith4, skillSetWith5, skillSetWith6, itemIDs FROM ItemSets WHERE setID = " << std::to_string(id);
|
||||
|
||||
auto result = CDClientDatabase::ExecuteQuery(query.str());
|
||||
auto result = query.execQuery();
|
||||
|
||||
if (result.eof())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (auto i = 0; i < 5; ++i)
|
||||
{
|
||||
if (result.fieldIsNull(i))
|
||||
@ -33,11 +33,11 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent)
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream skillQuery;
|
||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT SkillID FROM ItemSetSkills WHERE SkillSetID = ?;");
|
||||
skillQuery.bind(1, result.getIntField(i));
|
||||
|
||||
skillQuery << "SELECT SkillID FROM ItemSetSkills WHERE SkillSetID = " << std::to_string(result.getIntField(i));
|
||||
|
||||
auto skillResult = CDClientDatabase::ExecuteQuery(skillQuery.str());
|
||||
auto skillResult = skillQuery.execQuery();
|
||||
|
||||
if (skillResult.eof())
|
||||
{
|
||||
@ -49,10 +49,10 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent)
|
||||
if (skillResult.fieldIsNull(0))
|
||||
{
|
||||
skillResult.nextRow();
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const auto skillId = skillResult.getIntField(0);
|
||||
|
||||
switch (i)
|
||||
@ -75,7 +75,7 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
skillResult.nextRow();
|
||||
}
|
||||
}
|
||||
@ -83,7 +83,7 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent)
|
||||
std::string ids = result.getStringField(5);
|
||||
|
||||
ids.erase(std::remove_if(ids.begin(), ids.end(), ::isspace), ids.end());
|
||||
|
||||
|
||||
std::istringstream stream(ids);
|
||||
std::string token;
|
||||
|
||||
@ -99,9 +99,9 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent)
|
||||
m_Items.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_Equipped = {};
|
||||
|
||||
|
||||
for (const auto item : m_Items)
|
||||
{
|
||||
if (inventoryComponent->IsEquipped(item))
|
||||
@ -141,11 +141,11 @@ void ItemSet::OnEquip(const LOT lot)
|
||||
|
||||
auto* skillComponent = m_InventoryComponent->GetParent()->GetComponent<SkillComponent>();
|
||||
auto* missionComponent = m_InventoryComponent->GetParent()->GetComponent<MissionComponent>();
|
||||
|
||||
|
||||
for (const auto skill : skillSet)
|
||||
{
|
||||
auto* skillTable = CDClientManager::Instance()->GetTable<CDSkillBehaviorTable>("SkillBehavior");
|
||||
|
||||
|
||||
const auto behaviorId = skillTable->GetSkillByID(skill).behaviorID;
|
||||
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, skill);
|
||||
@ -167,11 +167,11 @@ void ItemSet::OnUnEquip(const LOT lot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const auto& skillSet = GetSkillSet(m_Equipped.size());
|
||||
|
||||
m_Equipped.erase(index);
|
||||
|
||||
|
||||
if (skillSet.empty())
|
||||
{
|
||||
return;
|
||||
@ -199,7 +199,7 @@ uint32_t ItemSet::GetID() const
|
||||
return m_ID;
|
||||
}
|
||||
|
||||
void ItemSet::Update(float deltaTime)
|
||||
void ItemSet::Update(float deltaTime)
|
||||
{
|
||||
for (auto& passiveAbility : m_PassiveAbilities)
|
||||
{
|
||||
@ -207,7 +207,7 @@ void ItemSet::Update(float deltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
void ItemSet::TriggerPassiveAbility(PassiveAbilityTrigger trigger)
|
||||
void ItemSet::TriggerPassiveAbility(PassiveAbilityTrigger trigger)
|
||||
{
|
||||
for (auto& passiveAbility : m_PassiveAbilities)
|
||||
{
|
||||
|
3
dGame/dMission/CMakeLists.txt
Normal file
3
dGame/dMission/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
set(DGAME_DMISSION_SOURCES "Mission.cpp"
|
||||
"MissionPrerequisites.cpp"
|
||||
"MissionTask.cpp" PARENT_SCOPE)
|
@ -421,7 +421,9 @@ void Mission::YieldRewards() {
|
||||
if (param.empty() || (param[0] & 1) == 0) // Should items be removed?
|
||||
{
|
||||
for (const auto target : task->GetAllTargets()) {
|
||||
inventoryComponent->RemoveItem(target, task->GetClientInfo().targetValue);
|
||||
// This is how live did it. ONLY remove item collection items from the items and hidden inventories and none of the others.
|
||||
inventoryComponent->RemoveItem(target, task->GetClientInfo().targetValue, eInventoryType::ITEMS);
|
||||
inventoryComponent->RemoveItem(target, task->GetClientInfo().targetValue, eInventoryType::HIDDEN);
|
||||
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_ITEM_COLLECTION, target, LWOOBJID_EMPTY, "", -task->GetClientInfo().targetValue);
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef MISSION_H
|
||||
#define MISSION_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@ -259,3 +262,5 @@ private:
|
||||
*/
|
||||
std::vector<MissionTask*> m_Tasks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef MISSIONLOCKSTATE_H
|
||||
#define MISSIONLOCKSTATE_H
|
||||
|
||||
enum class MissionLockState : int
|
||||
{
|
||||
MISSION_LOCK_LOCKED,
|
||||
MISSION_LOCK_NEW,
|
||||
MISSION_LOCK_UNLOCKED,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __MISSIONSTATE__H__
|
||||
#define __MISSIONSTATE__H__
|
||||
|
||||
/**
|
||||
* Represents the possible states a mission can be in
|
||||
*/
|
||||
@ -49,3 +52,5 @@ enum class MissionState : int {
|
||||
*/
|
||||
MISSION_STATE_COMPLETE_READY_TO_COMPLETE = 12
|
||||
};
|
||||
|
||||
#endif //!__MISSIONSTATE__H__
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef MISSIONTASK_H
|
||||
#define MISSIONTASK_H
|
||||
|
||||
#include "CDMissionTasksTable.h"
|
||||
#include "MissionTaskType.h"
|
||||
#include "dCommonVars.h"
|
||||
@ -180,3 +183,5 @@ private:
|
||||
*/
|
||||
void CheckCompletion() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef MISSIONTASKTYPE_H
|
||||
#define MISSIONTASKTYPE_H
|
||||
|
||||
//! An enum for mission task types
|
||||
enum class MissionTaskType : int {
|
||||
MISSION_TASK_TYPE_UNKNOWN = -1, //!< The task type is unknown
|
||||
@ -24,3 +27,5 @@ enum class MissionTaskType : int {
|
||||
MISSION_TASK_TYPE_PLACE_MODEL = 25, //!< A task for picking up a model
|
||||
MISSION_TASK_TYPE_VISIT_PROPERTY = 30 //!< A task for visiting a property
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user